diff --git a/DEPS b/DEPS
index d3c22a8..c8e14e6 100644
--- a/DEPS
+++ b/DEPS
@@ -308,7 +308,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '6937e7b311221aaff448abce8636bb09f2874962',
+  'skia_revision': '9afb9e561f6131a36d951dd988551039d25ade88',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -316,7 +316,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'be8aa9e9fbaa9278d5a0ebd18fd43357829d8c87',
+  'angle_revision': '309520d0c1c9f38528db1cb32670f6e4630fbc59',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -335,7 +335,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:9.20221003.3.1',
+  'fuchsia_version': 'version:9.20221004.0.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -359,7 +359,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '8faf57dd17088c37fa947fd565870648bbdbad18',
+  'freetype_revision': '0417527d5b5abc3ee9426f31bd95209ca97502a5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -387,7 +387,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': '23179707af80429b41360ac4066fb79608f8aa48',
+  'devtools_frontend_revision': 'e77792ecc5a8dcfc99d5a4c949007174980b8063',
   # 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.
@@ -423,7 +423,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '4c569d14ab6573deb118cc916440d714eb9c7e8c',
+  'dawn_revision': '0c2909e4d4c8f4174acaa5db2bc3d578281f9c4e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -487,7 +487,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       '2e919977e0030ce61bd19c40cefe31b995f1e2d4',
+  'libcxx_revision':       '7261e95e5175e95172404e84c43625dc61386470',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:cc28efe62ef0c2fb32455f414a29c4a55bb7fbc4',
@@ -993,7 +993,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'tlL4NXCU9VloHEQBuJdPXXhRoEmQlCqfefFTxshooSUC',
+          'version': 'WmYFNmUFDQMSQ8gF_eefj5NuqX1dts_zlowVIHYrMIYC',
       },
     ],
     'condition': 'checkout_android',
@@ -1242,7 +1242,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'abc7a6bc6df55e64128f6f3ef10447a3141641ee',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + '6fb6e4a17a9e72a02d0b9d86635bfe52368bf022',
     'condition': 'checkout_src_internal',
   },
 
@@ -1654,7 +1654,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '6b54769fde8e593a5ad7142596935d8198b511f1',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'ad82476afb8e871871290f5e26b6477b9953db81',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1839,7 +1839,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e84b11b1d90efe59e94bb24d1f1276071f50481e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ef51274188d7cf4611bb869ca1304e2f85c3d943',
+    Var('webrtc_git') + '/src.git' + '@' + 'c11b0fec74fabff3d93fa0d340a133867533d73d',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1909,7 +1909,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5ac75be1959dbcaf6c06a91ef7ea7e36a3e39637',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b227aeb64ccc02341fde87ad1d97c131fe1a27c9',
     'condition': 'checkout_src_internal',
   },
 
@@ -1928,7 +1928,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/eche_app/app',
-        'version': '3dcivv2X5VtogWZ3_o3BLhuDQr4rdtznPv1GUjlF0DcC',
+        'version': 'yesiSRdiGQK7STQgGw0zFkf-x7WzV1e1NrQ8w995Wo4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1961,7 +1961,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'nANRyXGXTuCHYDryhEHNSWohMk_zz8IzlnIpPpEkNDMC',
+        'version': 'W2N9Xp3ERgDWhNhfO9NlgNbHfoR00vjBV94S1kYssJwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/WATCHLISTS b/WATCHLISTS
index be8d5b9..abc8185 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -3068,7 +3068,8 @@
               'cbiesinger@chromium.org',
               'icer@chromium.org',
               'npm+watch@chromium.org'],
-    'weblayer': ['cricke+watch@chromium.org'],
+    'weblayer': ['cricke+watch@chromium.org',
+                 'rayankans+watch@chromium.org'],
     'weblayer_safe_browsing' : ['carlosil+watch@chromium.org'],
     'weblayer_ssl' : ['carlosil+watch@chromium.org'],
     'webotp': ['yigu+watch@chromium.org'],
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 918391b..e1f1ed1 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -377,6 +377,8 @@
                     "This persists client hints between top-level navigations."),
             Flag.commandLine(CcFeatures.USE_DMSAA_FOR_TILES,
                     "Switches skia to use DMSAA instead of MSAA for tile raster"),
+            Flag.baseFeature(
+                    CcFeatures.AVOID_RASTER_DURING_ELASTIC_OVERSCROLL, "No effect on webview"),
 
             // Add new commandline switches and features above. The final entry should have a
             // trailing comma for cleaner diffs.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index dff82914..4ce40ab 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3688,6 +3688,7 @@
       "android/linker/modern_linker_jni.h",
       "android/linker/modern_linker_unittest.cc",
       "android/path_utils_unittest.cc",
+      "android/radio_utils_unittest.cc",
       "android/reached_addresses_bitset_unittest.cc",
       "android/scoped_java_ref_unittest.cc",
       "android/sys_utils_unittest.cc",
diff --git a/base/allocator/partition_allocator/address_pool_manager.cc b/base/allocator/partition_allocator/address_pool_manager.cc
index f3b2a144..397b2792 100644
--- a/base/allocator/partition_allocator/address_pool_manager.cc
+++ b/base/allocator/partition_allocator/address_pool_manager.cc
@@ -47,18 +47,14 @@
 
 }  // namespace
 
-pool_handle AddressPoolManager::Add(uintptr_t ptr, size_t length) {
+void AddressPoolManager::Add(pool_handle handle, uintptr_t ptr, size_t length) {
   PA_DCHECK(!(ptr & kSuperPageOffsetMask));
   PA_DCHECK(!((ptr + length) & kSuperPageOffsetMask));
+  PA_CHECK(handle > 0 && handle <= std::size(pools_));
 
-  for (pool_handle i = 0; i < std::size(pools_); ++i) {
-    if (!pools_[i].IsInitialized()) {
-      pools_[i].Initialize(ptr, length);
-      return i + 1;
-    }
-  }
-  PA_NOTREACHED();
-  return 0;
+  Pool* pool = GetPool(handle);
+  PA_CHECK(!pool->IsInitialized());
+  pool->Initialize(ptr, length);
 }
 
 void AddressPoolManager::GetPoolUsedSuperPages(
diff --git a/base/allocator/partition_allocator/address_pool_manager.h b/base/allocator/partition_allocator/address_pool_manager.h
index afaf06a..1a1dbd68 100644
--- a/base/allocator/partition_allocator/address_pool_manager.h
+++ b/base/allocator/partition_allocator/address_pool_manager.h
@@ -54,7 +54,7 @@
   AddressPoolManager& operator=(const AddressPoolManager&) = delete;
 
 #if defined(PA_HAS_64_BITS_POINTERS)
-  pool_handle Add(uintptr_t address, size_t length);
+  void Add(pool_handle handle, uintptr_t address, size_t length);
   void Remove(pool_handle handle);
 
   // Populate a |used| bitset of superpages currently in use.
diff --git a/base/allocator/partition_allocator/address_pool_manager_unittest.cc b/base/allocator/partition_allocator/address_pool_manager_unittest.cc
index 3705997fe..127a19c 100644
--- a/base/allocator/partition_allocator/address_pool_manager_unittest.cc
+++ b/base/allocator/partition_allocator/address_pool_manager_unittest.cc
@@ -59,7 +59,8 @@
                                PageAccessibilityConfiguration::kInaccessible,
                                PageTag::kPartitionAlloc);
     ASSERT_TRUE(base_address_);
-    pool_ = manager_->Add(base_address_, kPoolSize);
+    manager_->Add(kRegularPoolHandle, base_address_, kPoolSize);
+    pool_ = kRegularPoolHandle;
   }
 
   void TearDown() override {
@@ -80,9 +81,13 @@
 
 TEST_F(PartitionAllocAddressPoolManagerTest, TooLargePool) {
   uintptr_t base_addr = 0x4200000;
+  const pool_handle extra_pool = 2;
+  static_assert(kNumPools >= 2);
 
   EXPECT_DEATH_IF_SUPPORTED(
-      GetAddressPoolManager()->Add(base_addr, kPoolSize + kSuperPageSize), "");
+      GetAddressPoolManager()->Add(extra_pool, base_addr,
+                                   kPoolSize + kSuperPageSize),
+      "");
 }
 
 TEST_F(PartitionAllocAddressPoolManagerTest, ManyPages) {
diff --git a/base/allocator/partition_allocator/glossary.md b/base/allocator/partition_allocator/glossary.md
index ca18a1e7..6ed14361 100644
--- a/base/allocator/partition_allocator/glossary.md
+++ b/base/allocator/partition_allocator/glossary.md
@@ -89,21 +89,22 @@
   holds some not-too-large memory chunks, ready to be allocated. This
   speeds up in-thread allocation by reducing a lock hold to a
   thread-local storage lookup, improving cache locality.
-* **Pool**: A large (and contiguous on 64-bit)  memory region, housing
+* **Pool**: A large (and contiguous on 64-bit) virtual address region, housing
   super pages, etc. from which PartitionAlloc services allocations. The
   primary purpose of the pools is to provide a fast answer to the
   question, "Did PartitionAlloc allocate the memory for this pointer
   from this pool?" with a single bit-masking operation.
-  * The regular pool contains all non-BackupRefPtr allocations.
-  * The BRP pool contains all the BRP allocations.
-  * Pools are downgraded into a logical concept in 32-bit environments,
-    tracking a non-contiguous set of allocations using a bitmap.
+  * The regular pool is a general purpose pool that contains allocations that
+    aren't protected by BackupRefPtr.
+  * The BRP pool contains all allocations protected by BackupRefPtr.
+  * [64-bit only] The configurable pool is named generically, because its
+    primary user (the [V8 Sandbox][v8-sandbox]) can configure it at runtime,
+    providing a pre-existing mapping. Its allocations aren't protected by
+    BackupRefPtr.
 
 *** promo
-A third pool is provided in 64-bit environments. It is generically
-named the "configurable" pool, because its primary user (the
-[V8 Sandbox][v8-sandbox]) can configure it at runtime, providing a
-pre-existing mapping.
+Pools are downgraded into a logical concept in 32-bit environments,
+tracking a non-contiguous set of allocations using a bitmap.
 ***
 
 * **Payload**: The usable area of a super page in which slot spans
diff --git a/base/allocator/partition_allocator/hardening_unittest.cc b/base/allocator/partition_allocator/hardening_unittest.cc
index 760d0caf..508d275 100644
--- a/base/allocator/partition_allocator/hardening_unittest.cc
+++ b/base/allocator/partition_allocator/hardening_unittest.cc
@@ -112,7 +112,9 @@
 #endif  // !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) &&
         // defined(PA_HAS_FREELIST_SHADOW_ENTRY)
 
-// Below test also misbehaves on Android, crbug.com/1370048
+// Below test also misbehaves on Android; as above, death tests don't
+// quite work (crbug.com/1240184), and having free slot bitmaps enabled
+// force the expectations below to crash.
 #if !BUILDFLAG(IS_ANDROID)
 
 TEST(HardeningTest, SuccessfulCorruption) {
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
index e19f7cd..c0bcd04 100644
--- a/base/allocator/partition_allocator/partition_address_space.cc
+++ b/base/allocator/partition_allocator/partition_address_space.cc
@@ -183,9 +183,8 @@
   setup_.regular_pool_base_mask_ = ~(regular_pool_size - 1);
 #endif
   PA_DCHECK(!(setup_.regular_pool_base_address_ & (regular_pool_size - 1)));
-  pool_handle pool = AddressPoolManager::GetInstance().Add(
-      setup_.regular_pool_base_address_, regular_pool_size);
-  PA_CHECK(pool == kRegularPoolHandle);
+  AddressPoolManager::GetInstance().Add(
+      kRegularPoolHandle, setup_.regular_pool_base_address_, regular_pool_size);
   PA_DCHECK(!IsInRegularPool(setup_.regular_pool_base_address_ - 1));
   PA_DCHECK(IsInRegularPool(setup_.regular_pool_base_address_));
   PA_DCHECK(IsInRegularPool(setup_.regular_pool_base_address_ +
@@ -216,9 +215,8 @@
   setup_.brp_pool_base_mask_ = ~(brp_pool_size - 1);
 #endif
   PA_DCHECK(!(setup_.brp_pool_base_address_ & (brp_pool_size - 1)));
-  pool = AddressPoolManager::GetInstance().Add(setup_.brp_pool_base_address_,
-                                               brp_pool_size);
-  PA_CHECK(pool == kBRPPoolHandle);
+  AddressPoolManager::GetInstance().Add(
+      kBRPPoolHandle, setup_.brp_pool_base_address_, brp_pool_size);
   PA_DCHECK(!IsInBRPPool(setup_.brp_pool_base_address_ - 1));
   PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_));
   PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_ + brp_pool_size - 1));
@@ -270,9 +268,8 @@
   setup_.configurable_pool_base_address_ = pool_base;
   setup_.configurable_pool_base_mask_ = ~(size - 1);
 
-  pool_handle pool = AddressPoolManager::GetInstance().Add(
-      setup_.configurable_pool_base_address_, size);
-  PA_CHECK(pool == kConfigurablePoolHandle);
+  AddressPoolManager::GetInstance().Add(
+      kConfigurablePoolHandle, setup_.configurable_pool_base_address_, size);
 }
 
 void PartitionAddressSpace::UninitForTesting() {
diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h
index d32f380..5772b68 100644
--- a/base/allocator/partition_allocator/partition_address_space.h
+++ b/base/allocator/partition_allocator/partition_address_space.h
@@ -31,11 +31,8 @@
 
 namespace internal {
 
-// Reserves address space for PartitionAllocator.
-//
-// This reserves space for the regular and BRP pools. If callers would
-// like to use the configurable pool, they must manually set up the
-// address space themselves and provide the mapping to PartitionAlloc.
+// Manages PartitionAlloc address space, which is split into pools.
+// See `glossary.md`.
 class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace {
  public:
 #if defined(PA_DYNAMICALLY_SELECT_POOL_SIZE)
@@ -80,14 +77,15 @@
     return kConfigurablePoolMinSize;
   }
 
-  // Initialize pools.
+  // Initialize pools (except for the configurable one).
   //
   // This function must only be called from the main thread.
   static void Init();
   // Initialize the ConfigurablePool at the given address |pool_base|. It must
   // be aligned to the size of the pool. The size must be a power of two and
-  // must be within [ConfigurablePoolMinSize(), ConfigurablePoolMaxSize()]. This
-  // function must only be called from the main thread.
+  // must be within [ConfigurablePoolMinSize(), ConfigurablePoolMaxSize()].
+  //
+  // This function must only be called from the main thread.
   static void InitConfigurablePool(uintptr_t pool_base, size_t size);
   static void UninitForTesting();
   static void UninitConfigurablePoolForTesting();
@@ -133,6 +131,12 @@
 #endif
     return (address & brp_pool_base_mask) == setup_.brp_pool_base_address_;
   }
+
+  static PA_ALWAYS_INLINE uintptr_t OffsetInBRPPool(uintptr_t address) {
+    PA_DCHECK(IsInBRPPool(address));
+    return address - setup_.brp_pool_base_address_;
+  }
+
   // Returns false for nullptr.
   static PA_ALWAYS_INLINE bool IsInConfigurablePool(uintptr_t address) {
     return (address & setup_.configurable_pool_base_mask_) ==
@@ -143,11 +147,6 @@
     return setup_.configurable_pool_base_address_;
   }
 
-  static PA_ALWAYS_INLINE uintptr_t OffsetInBRPPool(uintptr_t address) {
-    PA_DCHECK(IsInBRPPool(address));
-    return address - setup_.brp_pool_base_address_;
-  }
-
 #if defined(PA_ENABLE_SHADOW_METADATA)
   static PA_ALWAYS_INLINE std::ptrdiff_t ShadowPoolOffset(pool_handle pool) {
     if (pool == kRegularPoolHandle) {
@@ -192,16 +191,16 @@
   // Pool sizes have to be the power of two. Each pool will be aligned at its
   // own size boundary.
   //
-  // NOTE! The BRP pool must be preceded by a reserved region, where allocations
-  // are forbidden. This is to prevent a pointer to the end of a non-BRP-pool
-  // allocation from falling into the BRP pool, thus triggering BRP mechanism
-  // and likely crashing. This "forbidden zone" can be as small as 1B, but it's
-  // simpler to just reserve an allocation granularity unit.
+  // NOTE! The BRP pool must be preceded by an inaccessible region. This is to
+  // prevent a pointer to the end of a non-BRP-pool allocation from falling into
+  // the BRP pool, thus triggering BRP mechanism and likely crashing. This
+  // "forbidden zone" can be as small as 1B, but it's simpler to just reserve an
+  // allocation granularity unit.
   //
   // The ConfigurablePool is an optional Pool that can be created inside an
-  // existing mapping by the embedder. This Pool can be used when certain PA
-  // allocations must be located inside a given virtual address region. One
-  // use case for this Pool is V8's virtual memory cage, which requires that
+  // existing mapping provided by the embedder. This Pool can be used when
+  // certain PA allocations must be located inside a given virtual address
+  // region. One use case for this Pool is V8 Sandbox, which requires that
   // ArrayBuffers be located inside of it.
   static constexpr size_t kRegularPoolSize = kPoolMaxSize;
   static constexpr size_t kBRPPoolSize = kPoolMaxSize;
diff --git a/base/allocator/partition_allocator/partition_alloc_constants.h b/base/allocator/partition_allocator/partition_alloc_constants.h
index d8bf5bf..f6eaf71 100644
--- a/base/allocator/partition_allocator/partition_alloc_constants.h
+++ b/base/allocator/partition_allocator/partition_alloc_constants.h
@@ -259,10 +259,8 @@
 constexpr size_t kSuperPageOffsetMask = kSuperPageAlignment - 1;
 constexpr size_t kSuperPageBaseMask = ~kSuperPageOffsetMask;
 
-// PartitionAlloc's address space is primarily split into two pools.
-// See `glossary.md`.
+// PartitionAlloc's address space is split into pools. See `glossary.md`.
 #if defined(PA_HAS_64_BITS_POINTERS)
-// The 3rd, Configurable Pool is only available in 64-bit mode.
 constexpr size_t kNumPools = 3;
 // Maximum pool size. With exception of Configurable Pool, it is also
 // the actual size, unless PA_DYNAMICALLY_SELECT_POOL_SIZE is set, which
diff --git a/base/android/radio_utils.cc b/base/android/radio_utils.cc
index 45fcb59..0e082eae 100644
--- a/base/android/radio_utils.cc
+++ b/base/android/radio_utils.cc
@@ -11,18 +11,35 @@
 namespace android {
 
 namespace {
+
+RadioUtils::OverrideForTesting* g_overrider_for_tests = nullptr;
+
 bool InitializeIsSupported() {
   JNIEnv* env = AttachCurrentThread();
   return Java_RadioUtils_isSupported(env);
 }
 }  // namespace
 
+RadioUtils::OverrideForTesting::OverrideForTesting() {
+  DCHECK(!g_overrider_for_tests);
+  g_overrider_for_tests = this;
+}
+
+RadioUtils::OverrideForTesting::~OverrideForTesting() {
+  DCHECK(g_overrider_for_tests);
+  g_overrider_for_tests = nullptr;
+}
+
 bool RadioUtils::IsSupported() {
   static const bool kIsSupported = InitializeIsSupported();
   return kIsSupported;
 }
 
 RadioConnectionType RadioUtils::GetConnectionType() {
+  if (g_overrider_for_tests) {
+    // If GetConnectionType is being used in tests
+    return g_overrider_for_tests->GetConnectionType();
+  }
   if (!IsSupported())
     return RadioConnectionType::kUnknown;
 
diff --git a/base/android/radio_utils.h b/base/android/radio_utils.h
index 9360db08..959647c 100644
--- a/base/android/radio_utils.h
+++ b/base/android/radio_utils.h
@@ -39,6 +39,20 @@
 
 class BASE_EXPORT RadioUtils {
  public:
+  class OverrideForTesting {
+   public:
+    OverrideForTesting();
+    ~OverrideForTesting();
+
+    void SetConnectionTypeForTesting(RadioConnectionType connection_type) {
+      connection_type_ = connection_type;
+    }
+
+    RadioConnectionType GetConnectionType() { return connection_type_; }
+
+   private:
+    RadioConnectionType connection_type_;
+  };
   static bool IsSupported();
   static RadioConnectionType GetConnectionType();
   static absl::optional<RadioSignalLevel> GetCellSignalLevel();
diff --git a/base/android/radio_utils_unittest.cc b/base/android/radio_utils_unittest.cc
new file mode 100644
index 0000000..e5dc3df
--- /dev/null
+++ b/base/android/radio_utils_unittest.cc
@@ -0,0 +1,26 @@
+// Copyright 2022 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/android/radio_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace android {
+
+TEST(RadioUtilsTest, ConnectionType) {
+  RadioUtils::OverrideForTesting radio_utils_test;
+
+  radio_utils_test.SetConnectionTypeForTesting(RadioConnectionType::kUnknown);
+  EXPECT_EQ(RadioConnectionType::kUnknown, RadioUtils::GetConnectionType());
+
+  radio_utils_test.SetConnectionTypeForTesting(RadioConnectionType::kCell);
+  EXPECT_EQ(RadioConnectionType::kCell, RadioUtils::GetConnectionType());
+
+  radio_utils_test.SetConnectionTypeForTesting(RadioConnectionType::kWifi);
+  EXPECT_EQ(RadioConnectionType::kWifi, RadioUtils::GetConnectionType());
+}
+
+}  // namespace android
+}  // namespace base
\ No newline at end of file
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 40464c1..4e581af8 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -1059,7 +1059,12 @@
 
 TestLauncher::~TestLauncher() {
   if (base::ThreadPoolInstance::Get()) {
+    // Clear the ThreadPoolInstance entirely to make it clear to final cleanup
+    // phases that they are happening in a single-threaded phase. Assertions in
+    // code like ~ScopedFeatureList are unhappy otherwise (crbug.com/1359095).
     base::ThreadPoolInstance::Get()->Shutdown();
+    base::ThreadPoolInstance::Get()->JoinForTesting();
+    base::ThreadPoolInstance::Set(nullptr);
   }
 }
 
diff --git a/base/trace_event/memory_infra_background_allowlist.cc b/base/trace_event/memory_infra_background_allowlist.cc
index 3987bcc8..3dc4747f 100644
--- a/base/trace_event/memory_infra_background_allowlist.cc
+++ b/base/trace_event/memory_infra_background_allowlist.cc
@@ -137,7 +137,7 @@
         "gpu/gl/renderbuffers/context_group_0x?",
         "gpu/gl/textures/context_group_0x?",
         "gpu/gr_shader_cache/cache_0x?",
-        "gpu/shared_images/client_0x?",
+        "gpu/shared_images",
         "gpu/transfer_cache/cache_0x?",
         "gpu/transfer_cache/cache_0x?/avg_image_size",
         "history/delta_file_service/leveldb_0x?",
diff --git a/build/config/ios/swift_source_set.gni b/build/config/ios/swift_source_set.gni
index 0f5cc07..825c2a1 100644
--- a/build/config/ios/swift_source_set.gni
+++ b/build/config/ios/swift_source_set.gni
@@ -7,7 +7,19 @@
 # with all "/" and ":" replaced with "_".
 template("swift_source_set") {
   _target_name = target_name
-  source_set(target_name) {
+  if (defined(invoker.bridge_header)) {
+    _header_name = _target_name + "_bridge_header"
+    source_set(_header_name) {
+      forward_variables_from(invoker,
+                             [
+                               "deps",
+                               "public_deps",
+                             ])
+      visibility = [ ":$_target_name" ]
+      sources = [ invoker.bridge_header ]
+    }
+  }
+  source_set(_target_name) {
     forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
     forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
     if (!defined(module_name)) {
@@ -18,8 +30,18 @@
       module_name =
           string_replace(string_replace(_target_label, "/", "_"), ":", "_")
     }
+
+    # Use an additional source_set target to allow `gn check` to check the
+    # dependencies needed for `bridge_header` are present.
+    if (defined(invoker.bridge_header)) {
+      if (!defined(public_deps)) {
+        public_deps = []
+      }
+      public_deps += [ ":$_header_name" ]
+    }
   }
 }
+
 set_defaults("swift_source_set") {
   configs = default_compiler_configs
 }
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 10fd90e..3a4e18a6 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision vars in //DEPS.
-  libcxx_revision = "2e919977e0030ce61bd19c40cefe31b995f1e2d4"
+  libcxx_revision = "7261e95e5175e95172404e84c43625dc61386470"
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ba8edf5..8fc07cde 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7160,6 +7160,9 @@
       <message name="IDS_READ_ANYTHING_COLORS_COMBOBOX_LABEL" desc="Accessibility label for the colors combobox of the Read Anything feature." translateable="false">
         Theme
       </message>
+      <message name="IDS_READ_ANYTHING_LINE_SPACING_COMBOBOX_LABEL" desc="Accessibility label for the line height combobox of the Read Anything feature." translateable="false">
+        Line height
+      </message>
       <message name="IDS_READ_ANYTHING_LETTER_SPACING_COMBOBOX_LABEL" desc="Accessibility label for the letter spacing combobox of the Read Anything feature." translateable="false">
         Letter spacing
       </message>
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 294fcf2..288b8fb4 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -77,6 +77,7 @@
     "laptop_and_smartphone.icon",
     "leading_scroll.icon",
     "letter_spacing.icon",
+    "line_spacing.icon",
     "media_controls_arrow_drop_down.icon",
     "media_controls_arrow_drop_up.icon",
     "media_toolbar_button.icon",
diff --git a/chrome/app/vector_icons/line_spacing.icon b/chrome/app/vector_icons/line_spacing.icon
new file mode 100644
index 0000000..cf02fd9
--- /dev/null
+++ b/chrome/app/vector_icons/line_spacing.icon
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 3, 0,
+LINE_TO, 0, 3,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, 8,
+H_LINE_TO, 0,
+R_LINE_TO, 3, 3,
+R_LINE_TO, 3, -3,
+H_LINE_TO, 4,
+V_LINE_TO, 3,
+R_H_LINE_TO, 2,
+LINE_TO, 3, 0,
+CLOSE,
+R_MOVE_TO, 4, 2,
+R_H_LINE_TO, 9,
+R_V_LINE_TO, 2,
+H_LINE_TO, 7,
+V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, 2, 4,
+H_LINE_TO, 7,
+R_V_LINE_TO, 2,
+R_H_LINE_TO, 9,
+V_LINE_TO, 6,
+H_LINE_TO, 9,
+CLOSE,
+R_MOVE_TO, -2, 4,
+R_H_LINE_TO, 9,
+R_V_LINE_TO, 2,
+H_LINE_TO, 7,
+R_V_LINE_TO, -2,
+CLOSE
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9b06cb8..31012e4 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -7668,6 +7668,8 @@
       "supervised_user/child_accounts/kids_management_api.h",
       "supervised_user/child_accounts/permission_request_creator_apiary.cc",
       "supervised_user/child_accounts/permission_request_creator_apiary.h",
+      "supervised_user/kids_chrome_management/kids_access_token_fetcher.cc",
+      "supervised_user/kids_chrome_management/kids_access_token_fetcher.h",
       "supervised_user/kids_chrome_management/kids_chrome_management_client.cc",
       "supervised_user/kids_chrome_management/kids_chrome_management_client.h",
       "supervised_user/kids_chrome_management/kids_chrome_management_client_factory.cc",
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics.cc b/chrome/browser/apps/app_service/metrics/website_metrics.cc
index 377811a..6b11934 100644
--- a/chrome/browser/apps/app_service/metrics/website_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/website_metrics.cc
@@ -8,6 +8,7 @@
 
 #include "ash/shell.h"
 #include "base/containers/contains.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/json/values_util.h"
 #include "base/rand_util.h"
 #include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
@@ -399,6 +400,11 @@
   // contents::WebContentsObserver::PrimaryPageChanged(), set the visible url as
   // default value for the ukm key url.
   webcontents_to_ukm_key_[web_contents] = web_contents->GetVisibleURL();
+
+  if (web_contents->GetVisibleURL().is_empty()) {
+    return;
+  }
+
   auto it = window_to_web_contents_.find(window);
   bool is_activated = wm::IsActiveWindow(window) &&
                       it != window_to_web_contents_.end() &&
@@ -590,6 +596,15 @@
                              bool is_from_last_login) {
   auto source_id = ukm::UkmRecorder::GetSourceIdForWebsiteUrl(
       base::PassKey<WebsiteMetrics>(), url);
+  if (url.is_empty() || ukm::SourceIdObj::FromInt64(source_id).GetType() !=
+                            ukm::SourceIdType::DESKTOP_WEB_APP_ID) {
+    LOG(ERROR) << "WebsiteMetrics::EmitUkm url is " << url.spec()
+               << ", source id type is "
+               << (int)ukm::SourceIdObj::FromInt64(source_id).GetType();
+    base::debug::DumpWithoutCrashing();
+    return;
+  }
+
   if (source_id != ukm::kInvalidSourceId) {
     ukm::builders::ChromeOS_WebsiteUsageTime builder(source_id);
     builder.SetDuration(usage_time)
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index dc0f4ef0..dce017d 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -1974,6 +1974,8 @@
     "os_feedback/os_feedback_screenshot_manager.h",
     "ownership/fake_owner_settings_service.cc",
     "ownership/fake_owner_settings_service.h",
+    "ownership/owner_key_loader.cc",
+    "ownership/owner_key_loader.h",
     "ownership/owner_settings_service_ash.cc",
     "ownership/owner_settings_service_ash.h",
     "ownership/owner_settings_service_ash_factory.cc",
diff --git a/chrome/browser/ash/app_mode/arc/arc_kiosk_app_service.cc b/chrome/browser/ash/app_mode/arc/arc_kiosk_app_service.cc
index 02c772e..a564671c 100644
--- a/chrome/browser/ash/app_mode/arc/arc_kiosk_app_service.cc
+++ b/chrome/browser/ash/app_mode/arc/arc_kiosk_app_service.cc
@@ -159,7 +159,7 @@
     return;
   }
 
-  for (const auto& detail : details->GetListDeprecated()) {
+  for (const auto& detail : details->GetList()) {
     const base::Value* const reason =
         detail.FindKeyOfType("nonComplianceReason", base::Value::Type::INTEGER);
     if (!reason || reason->GetInt() != kNonComplianceReasonAppNotInstalled)
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_delegate.cc b/chrome/browser/ash/app_restore/arc_ghost_window_delegate.cc
index f9ece6f..3171082 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_delegate.cc
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_delegate.cc
@@ -4,9 +4,7 @@
 
 #include "chrome/browser/ash/app_restore/arc_ghost_window_delegate.h"
 
-#include "chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.h"
 #include "chrome/browser/ash/app_restore/arc_window_utils.h"
-#include "chrome/browser/ash/arc/window_predictor/window_predictor_utils.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
@@ -19,12 +17,10 @@
 ArcGhostWindowDelegate::ArcGhostWindowDelegate(
     exo::ClientControlledShellSurface* shell_surface,
     int window_id,
-    const std::string& app_id,
     int64_t display_id,
     const gfx::Rect& bounds,
     chromeos::WindowStateType window_state)
     : window_id_(window_id),
-      app_id_(app_id),
       bounds_(gfx::Rect(bounds)),
       pending_close_(false),
       window_state_(window_state),
@@ -149,20 +145,6 @@
   UpdateWindowInfoToArc();
 }
 
-void ArcGhostWindowDelegate::OnAppStatesUpdate(const std::string& app_id,
-                                               bool ready,
-                                               bool need_fixup) {
-  if (app_id != app_id_)
-    return;
-
-  // Currently the type update is oneway. If an App need fixup, is not able to
-  // become another state before it's ready.
-  if (need_fixup) {
-    static_cast<ArcGhostWindowShellSurface*>(shell_surface_)
-        ->SetWindowType(arc::GhostWindowType::kFixup);
-  }
-}
-
 bool ArcGhostWindowDelegate::SetDisplayId(int64_t display_id) {
   absl::optional<double> scale_factor = GetDisplayScaleFactor(display_id);
   if (!scale_factor.has_value()) {
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_delegate.h b/chrome/browser/ash/app_restore/arc_ghost_window_delegate.h
index 40e9c64..1351f29 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_delegate.h
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_delegate.h
@@ -21,7 +21,6 @@
  public:
   ArcGhostWindowDelegate(exo::ClientControlledShellSurface* shell_surface,
                          int window_id,
-                         const std::string& app_id,
                          int64_t display_id,
                          const gfx::Rect& bounds,
                          chromeos::WindowStateType window_state);
@@ -51,16 +50,11 @@
 
   void OnWindowCloseRequested(int window_id) override;
 
-  void OnAppStatesUpdate(const std::string& app_id,
-                         bool ready,
-                         bool need_fixup) override;
-
  private:
   bool SetDisplayId(int64_t display_id);
   void UpdateWindowInfoToArc();
 
   int window_id_;
-  std::string app_id_;
   gfx::Rect bounds_;
   bool pending_close_;
   int64_t display_id_;
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_handler.cc b/chrome/browser/ash/app_restore/arc_ghost_window_handler.cc
index 2600dfe..a09086d 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_handler.cc
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_handler.cc
@@ -128,18 +128,6 @@
   return true;
 }
 
-bool ArcGhostWindowHandler::UpdateArcGhostWindowType(
-    int32_t session_id,
-    arc::GhostWindowType window_type) {
-  auto it = session_id_to_shell_surface_.find(session_id);
-  if (it == session_id_to_shell_surface_.end())
-    return false;
-  auto* shell_surface =
-      static_cast<ArcGhostWindowShellSurface*>(it->second.get());
-  shell_surface->SetWindowType(window_type);
-  return true;
-}
-
 void ArcGhostWindowHandler::CloseWindow(int session_id) {
   auto it = session_id_to_shell_surface_.find(session_id);
   if (it == session_id_to_shell_surface_.end())
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_handler.h b/chrome/browser/ash/app_restore/arc_ghost_window_handler.h
index d40ff77..a8cd7b0e 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_handler.h
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_handler.h
@@ -16,10 +16,6 @@
 struct AppRestoreData;
 }  // namespace app_restore
 
-namespace arc {
-enum class GhostWindowType;
-}  // namespace arc
-
 namespace ash::full_restore {
 
 // The ArcGhostWindowHandler class provides control for ARC ghost window.
@@ -81,9 +77,6 @@
                             int32_t session_id,
                             ::app_restore::AppRestoreData* restore_data);
 
-  bool UpdateArcGhostWindowType(int32_t session_id,
-                                arc::GhostWindowType window_type);
-
   void CloseWindow(int session_id);
 
   void AddObserver(Observer* observer);
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.cc b/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.cc
index ea0d1d0..b755aeb 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.cc
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.cc
@@ -121,7 +121,7 @@
 
   // TODO(sstan): Add set_surface_destroyed_callback.
   shell_surface->set_delegate(std::make_unique<ArcGhostWindowDelegate>(
-      shell_surface.get(), window_id, app_id, display_id_value, local_bounds,
+      shell_surface.get(), window_id, display_id_value, local_bounds,
       window_state.value_or(chromeos::WindowStateType::kDefault)));
   shell_surface->set_close_callback(std::move(close_callback));
 
@@ -186,7 +186,6 @@
                                                     uint32_t theme_color) {
   auto view =
       std::make_unique<ArcGhostWindowView>(type_, kDiameter, theme_color);
-  view_observer_ = view.get();
   view->LoadIcon(app_id);
   exo::ShellSurfaceBase::OverlayParams overlay_params(std::move(view));
   overlay_params.translucent = true;
@@ -211,10 +210,4 @@
     property_handler->ClearProperty(app_restore::kAppIdKey);
 }
 
-void ArcGhostWindowShellSurface::SetWindowType(
-    arc::GhostWindowType window_type) {
-  DCHECK(view_observer_);
-  view_observer_->SetType(window_type);
-}
-
 }  // namespace ash::full_restore
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.h b/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.h
index 088ce44..5256629 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.h
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_shell_surface.h
@@ -23,8 +23,6 @@
 // Explicitly identifies ARC ghost surface.
 extern const aura::WindowProperty<bool>* const kArcGhostSurface;
 
-class ArcGhostWindowView;
-
 // ArcGhostWindowShellSurface class is a shell surface which controlled its
 // root surface.
 class ArcGhostWindowShellSurface : public exo::ClientControlledShellSurface {
@@ -44,8 +42,6 @@
 
   void OverrideInitParams(views::Widget::InitParams* params) override;
 
-  void SetWindowType(arc::GhostWindowType window_type);
-
   exo::Surface* controller_surface();
 
  private:
@@ -60,7 +56,6 @@
   void SetShellAppId(ui::PropertyHandler* property_handler,
                      const absl::optional<std::string>& id);
 
-  ArcGhostWindowView* view_observer_ = nullptr;
   arc::GhostWindowType type_;
   absl::optional<std::string> app_id_;
 
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_view.cc b/chrome/browser/ash/app_restore/arc_ghost_window_view.cc
index 87ac436..dd79b12 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_view.cc
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_view.cc
@@ -92,7 +92,14 @@
   throbber->SetPreferredSize(gfx::Size(diameter, diameter));
   throbber->GetViewAccessibility().OverrideRole(ax::mojom::Role::kImage);
 
-  SetType(type);
+  if (type == arc::GhostWindowType::kFixup) {
+    auto label = std::make_unique<views::Label>(
+        l10n_util::GetStringUTF16(IDS_ARC_GHOST_WINDOW_APP_FIXUP_MESSAGE));
+    // TODO(sstan): Set font size or height, according to future UI update.
+    label->SetMultiLine(true);
+    message_label_ = label.get();
+    AddChildView(std::move(label));
+  }
   // TODO(sstan): Set window title and accessible name from saved data.
 }
 
@@ -114,28 +121,6 @@
           : std::move(icon_loaded_cb_for_testing_));
 }
 
-void ArcGhostWindowView::SetType(arc::GhostWindowType type) {
-  // Currently the only difference of App Fixup and other type of ghost window
-  // is that App Fixup ghost window has a message label.
-  if (type == arc::GhostWindowType::kFixup) {
-    if (!message_label_) {
-      auto label = std::make_unique<views::Label>(
-          l10n_util::GetStringUTF16(IDS_ARC_GHOST_WINDOW_APP_FIXUP_MESSAGE));
-      // TODO(sstan): Set font size or height, according to future UI update.
-      label->SetMultiLine(true);
-      message_label_ = label.get();
-      AddChildView(std::move(label));
-      Layout();
-    }
-  } else {
-    if (message_label_) {
-      RemoveChildView(message_label_);
-      message_label_ = nullptr;
-      Layout();
-    }
-  }
-}
-
 void ArcGhostWindowView::OnIconLoaded(apps::IconValuePtr icon_value) {
   if (!icon_value || icon_value->icon_type != apps::IconType::kStandard)
     return;
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_view.h b/chrome/browser/ash/app_restore/arc_ghost_window_view.h
index 7146aef..8e80eca 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_view.h
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_view.h
@@ -38,20 +38,17 @@
 
   void LoadIcon(const std::string& app_id);
 
-  void SetType(arc::GhostWindowType type);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(ArcGhostWindowViewTest, IconLoadTest);
   FRIEND_TEST_ALL_PREFIXES(ArcGhostWindowViewTest, FixupMessageTest);
-  FRIEND_TEST_ALL_PREFIXES(ArcGhostWindowViewTest, SetTypeTest);
 
   void InitLayout(arc::GhostWindowType type,
                   uint32_t theme_color,
                   int diameter);
   void OnIconLoaded(apps::IconValuePtr icon_value);
 
-  views::ImageView* icon_view_ = nullptr;
-  views::Label* message_label_ = nullptr;
+  views::ImageView* icon_view_;
+  views::Label* message_label_;
   base::OnceCallback<void(apps::IconValuePtr icon_value)>
       icon_loaded_cb_for_testing_;
 
diff --git a/chrome/browser/ash/app_restore/arc_ghost_window_view_unittest.cc b/chrome/browser/ash/app_restore/arc_ghost_window_view_unittest.cc
index 40bb8f2..f8ad42c 100644
--- a/chrome/browser/ash/app_restore/arc_ghost_window_view_unittest.cc
+++ b/chrome/browser/ash/app_restore/arc_ghost_window_view_unittest.cc
@@ -143,20 +143,4 @@
             l10n_util::GetStringUTF16(IDS_ARC_GHOST_WINDOW_APP_FIXUP_MESSAGE));
 }
 
-TEST_F(ArcGhostWindowViewTest, SetTypeTest) {
-  const int kDiameter = 24;
-  const uint32_t kThemeColor = SK_ColorWHITE;
-  const std::string kAppId = "test_app";
-  InstallApp(kAppId);
-
-  CreateView(arc::GhostWindowType::kFullRestore, kDiameter, kThemeColor);
-  EXPECT_EQ(view()->message_label_, nullptr);
-
-  view()->SetType(arc::GhostWindowType::kFixup);
-  EXPECT_NE(view()->message_label_, nullptr);
-
-  view()->SetType(arc::GhostWindowType::kAppLaunch);
-  EXPECT_EQ(view()->message_label_, nullptr);
-}
-
 }  // namespace ash::full_restore
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
index 4d15c67..a5c2bd3 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
@@ -552,7 +552,7 @@
 
   const base::Value& profile_list = pref_service_->GetValue(pref_name_);
 
-  for (const base::Value& cur_profile : profile_list.GetListDeprecated()) {
+  for (const base::Value& cur_profile : profile_list.GetList()) {
     const CertProfileId* id = cur_profile.FindStringKey(kCertProfileIdKey);
     if (!id || (*id != cert_profile_id)) {
       continue;
@@ -570,7 +570,7 @@
   const base::Value& profile_list = pref_service_->GetValue(pref_name_);
 
   std::vector<CertProfile> result_profiles;
-  for (const base::Value& cur_profile : profile_list.GetListDeprecated()) {
+  for (const base::Value& cur_profile : profile_list.GetList()) {
     absl::optional<CertProfile> p = CertProfile::MakeFromValue(cur_profile);
     if (!p) {
       LOG(WARNING) << "Failed to parse certificate profile";
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks.cc b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
index 4fd04aa1..62bf1d00 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks.cc
@@ -51,8 +51,7 @@
 #include "storage/browser/file_system/file_system_url.h"
 #include "url/gurl.h"
 
-namespace file_manager {
-namespace file_tasks {
+namespace file_manager::file_tasks {
 
 extensions::api::file_manager_private::TaskResult
 ConvertLaunchResultToTaskResult(const apps::LaunchResult& result,
@@ -76,7 +75,9 @@
 }
 
 using extensions::api::file_manager_private::Verb;
+
 namespace {
+
 TaskType GetTaskType(apps::AppType app_type) {
   switch (app_type) {
     case apps::AppType::kArc:
@@ -387,5 +388,4 @@
   }
 }
 
-}  // namespace file_tasks
-}  // namespace file_manager
+}  // namespace file_manager::file_tasks
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks.h b/chrome/browser/ash/file_manager/app_service_file_tasks.h
index 410a07c..ac4d2f4 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks.h
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks.h
@@ -21,8 +21,7 @@
 class FileSystemURL;
 }
 
-namespace file_manager {
-namespace file_tasks {
+namespace file_manager::file_tasks {
 
 // Returns true if a file handler is enabled. Some handlers such as
 // import-crostini-image can be disabled at runtime by enterprise policy.
@@ -51,7 +50,6 @@
     const std::vector<std::string>& mime_types,
     FileTaskFinishedCallback done);
 
-}  // namespace file_tasks
-}  // namespace file_manager
+}  // namespace file_manager::file_tasks
 
 #endif  // CHROME_BROWSER_ASH_FILE_MANAGER_APP_SERVICE_FILE_TASKS_H_
diff --git a/chrome/browser/ash/file_manager/arc_file_tasks.cc b/chrome/browser/ash/file_manager/arc_file_tasks.cc
index 78e8d0e..33de8fcc 100644
--- a/chrome/browser/ash/file_manager/arc_file_tasks.cc
+++ b/chrome/browser/ash/file_manager/arc_file_tasks.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/fileapi/arc_content_file_system_url_util.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
+#include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -114,31 +115,29 @@
 }
 
 // Below is the sequence of thread-hopping for loading ARC file tasks.
-void OnArcHandlerList(
-    Profile* profile,
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
-    FindTasksCallback callback,
-    std::vector<arc::mojom::IntentHandlerInfoPtr> handlers);
+void OnArcHandlerList(Profile* profile,
+                      std::unique_ptr<ResultingTasks> resulting_tasks,
+                      FindTasksCallback callback,
+                      std::vector<arc::mojom::IntentHandlerInfoPtr> handlers);
 
 void OnArcIconLoaded(
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
+    std::unique_ptr<ResultingTasks> resulting_tasks,
     FindTasksCallback callback,
     std::vector<arc::mojom::IntentHandlerInfoPtr> handlers,
     std::unique_ptr<arc::ArcIntentHelperBridge::ActivityToIconsMap> icons);
 
 // Called after the handlers from ARC is obtained. Proceeds to OnArcIconLoaded.
-void OnArcHandlerList(
-    Profile* profile,
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
-    FindTasksCallback callback,
-    std::vector<arc::mojom::IntentHandlerInfoPtr> handlers) {
+void OnArcHandlerList(Profile* profile,
+                      std::unique_ptr<ResultingTasks> resulting_tasks,
+                      FindTasksCallback callback,
+                      std::vector<arc::mojom::IntentHandlerInfoPtr> handlers) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   auto* intent_helper_bridge =
       arc::ArcIntentHelperBridge::GetForBrowserContext(profile);
   if (!intent_helper_bridge) {
     LOG(ERROR) << "Failed to get ArcIntentHelperBridge";
-    std::move(callback).Run(std::move(result_list));
+    std::move(callback).Run(std::move(resulting_tasks));
     return;
   }
 
@@ -150,13 +149,13 @@
 
   intent_helper_bridge->GetActivityIcons(
       activity_names,
-      base::BindOnce(&OnArcIconLoaded, std::move(result_list),
+      base::BindOnce(&OnArcIconLoaded, std::move(resulting_tasks),
                      std::move(callback), std::move(handlers_filtered)));
 }
 
 // Called after icon data for ARC apps are loaded. Proceeds to OnArcIconEncoded.
 void OnArcIconLoaded(
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
+    std::unique_ptr<ResultingTasks> resulting_tasks,
     FindTasksCallback callback,
     std::vector<arc::mojom::IntentHandlerInfoPtr> handlers,
     std::unique_ptr<arc::ArcIntentHelperBridge::ActivityToIconsMap> icons) {
@@ -179,15 +178,15 @@
     const GURL& icon_url =
         (it == icons->end() ? GURL::EmptyGURL()
                             : it->second.icon16_dataurl->data);
-    result_list->push_back(FullTaskDescriptor(
+    resulting_tasks->tasks.emplace_back(
         TaskDescriptor(
             ActivityNameToAppId(handler->package_name, handler->activity_name),
             TASK_TYPE_ARC_APP, ArcActionToFileTaskActionId(action)),
         name, handler_verb, icon_url, false /* is_default */,
         action != arc::kIntentActionView /* is_generic */,
-        false /* is_file_extension_match */));
+        false /* is_file_extension_match */);
   }
-  std::move(callback).Run(std::move(result_list));
+  std::move(callback).Run(std::move(resulting_tasks));
 }
 
 // |ignore_paths_to_share| contains the paths to be shared to
@@ -196,7 +195,7 @@
 void FindArcTasksAfterContentUrlsResolved(
     Profile* profile,
     const std::vector<extensions::EntryInfo>& entries,
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
+    std::unique_ptr<ResultingTasks> resulting_tasks,
     FindTasksCallback callback,
     const std::vector<GURL>& content_urls,
     const std::vector<base::FilePath>& ignore_paths_to_share) {
@@ -217,7 +216,7 @@
   }
   if (!arc_intent_helper) {
     LOG(ERROR) << "Failed to get arc_intent_helper";
-    std::move(callback).Run(std::move(result_list));
+    std::move(callback).Run(std::move(resulting_tasks));
     return;
   }
 
@@ -227,12 +226,12 @@
     const GURL& content_url = content_urls[i];
 
     if (entry.is_directory) {  // ARC apps don't support directories.
-      std::move(callback).Run(std::move(result_list));
+      std::move(callback).Run(std::move(resulting_tasks));
       return;
     }
 
     if (!content_url.is_valid()) {
-      std::move(callback).Run(std::move(result_list));
+      std::move(callback).Run(std::move(resulting_tasks));
       return;
     }
 
@@ -246,7 +245,7 @@
   arc_intent_helper->RequestUrlListHandlerList(
       std::move(urls),
       base::BindOnce(&OnArcHandlerList, base::Unretained(profile),
-                     std::move(result_list), std::move(callback)));
+                     std::move(resulting_tasks), std::move(callback)));
 }
 
 void ExecuteArcTaskAfterContentUrlsResolved(
@@ -258,11 +257,11 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(content_urls.size(), mime_types.size());
 
-  for (size_t i = 0; i < content_urls.size(); ++i) {
-    if (!content_urls[i].is_valid()) {
+  for (const GURL& content_url : content_urls) {
+    if (!content_url.is_valid()) {
       std::move(done).Run(
           extensions::api::file_manager_private::TASK_RESULT_FAILED,
-          "Invalid url: " + content_urls[i].possibly_invalid_spec());
+          "Invalid url: " + content_url.possibly_invalid_spec());
       return;
     }
   }
@@ -312,7 +311,7 @@
 void FindArcTasks(Profile* profile,
                   const std::vector<extensions::EntryInfo>& entries,
                   const std::vector<GURL>& file_urls,
-                  std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
+                  std::unique_ptr<ResultingTasks> resulting_tasks,
                   FindTasksCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(entries.size(), file_urls.size());
@@ -331,8 +330,8 @@
   file_manager::util::ConvertToContentUrls(
       ProfileManager::GetPrimaryUserProfile(), file_system_urls,
       base::BindOnce(&FindArcTasksAfterContentUrlsResolved,
-                     base::Unretained(profile), entries, std::move(result_list),
-                     std::move(callback)));
+                     base::Unretained(profile), entries,
+                     std::move(resulting_tasks), std::move(callback)));
 }
 
 void ExecuteArcTask(Profile* profile,
diff --git a/chrome/browser/ash/file_manager/arc_file_tasks.h b/chrome/browser/ash/file_manager/arc_file_tasks.h
index 0507bc86..f0aedfd 100644
--- a/chrome/browser/ash/file_manager/arc_file_tasks.h
+++ b/chrome/browser/ash/file_manager/arc_file_tasks.h
@@ -21,15 +21,14 @@
 class FileSystemURL;
 }
 
-namespace file_manager {
-namespace file_tasks {
+namespace file_manager::file_tasks {
 
-// Finds the ARC tasks that can handle |entries|, appends them to |result_list|,
-// and calls back to |callback|.
+// Finds the ARC tasks that can handle |entries|, appends them to
+// |resulting_tasks|, and calls back to |callback|.
 void FindArcTasks(Profile* profile,
                   const std::vector<extensions::EntryInfo>& entries,
                   const std::vector<GURL>& file_urls,
-                  std::unique_ptr<std::vector<FullTaskDescriptor>> result_list,
+                  std::unique_ptr<ResultingTasks> resulting_tasks,
                   FindTasksCallback callback);
 
 // Executes the specified task by ARC.
@@ -39,7 +38,6 @@
                     const std::vector<std::string>& mime_types,
                     FileTaskFinishedCallback done);
 
-}  // namespace file_tasks
-}  // namespace file_manager
+}  // namespace file_manager::file_tasks
 
 #endif  // CHROME_BROWSER_ASH_FILE_MANAGER_ARC_FILE_TASKS_H_
diff --git a/chrome/browser/ash/file_manager/file_manager_test_util.cc b/chrome/browser/ash/file_manager/file_manager_test_util.cc
index 15dd050..4578d7966 100644
--- a/chrome/browser/ash/file_manager/file_manager_test_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_test_util.cc
@@ -11,6 +11,7 @@
 #include "base/test/bind.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_ash.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
+#include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/file_manager/volume_manager_observer.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -192,8 +193,8 @@
   std::vector<file_tasks::FullTaskDescriptor> result;
   bool invoked_synchronously = false;
   auto callback = base::BindLambdaForTesting(
-      [&](std::unique_ptr<std::vector<file_tasks::FullTaskDescriptor>> tasks) {
-        result = *tasks;
+      [&](std::unique_ptr<file_tasks::ResultingTasks> resulting_tasks) {
+        result = std::move(resulting_tasks->tasks);
         invoked_synchronously = true;
       });
 
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index 890b32f3..1515da0 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -19,6 +19,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "base/notreached.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
@@ -66,6 +67,7 @@
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/cpp/app_update.h"
 #include "components/services/app_service/public/cpp/file_handler.h"
 #include "components/services/app_service/public/cpp/file_handler_info.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -323,16 +325,15 @@
   }
 }
 
-void PostProcessFoundTasks(
-    Profile* profile,
-    const std::vector<extensions::EntryInfo>& entries,
-    FindTasksCallback callback,
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list) {
-  AdjustTasksForMediaApp(entries, result_list.get());
+void PostProcessFoundTasks(Profile* profile,
+                           const std::vector<extensions::EntryInfo>& entries,
+                           FindTasksCallback callback,
+                           std::unique_ptr<ResultingTasks> resulting_tasks) {
+  AdjustTasksForMediaApp(entries, &resulting_tasks->tasks);
 
   // Google documents can only be handled by internal handlers.
   if (ContainsGoogleDocument(entries))
-    KeepOnlyFileManagerInternalTasks(result_list.get());
+    KeepOnlyFileManagerInternalTasks(&resulting_tasks->tasks);
 
   std::set<std::string> disabled_actions;
 
@@ -346,14 +347,15 @@
     disabled_actions.emplace(kActionIdWebDriveOfficePowerPoint);
   } else {
     // Hide the office PWA File Handler.
-    RemoveActionsForApp(extension_misc::kOfficePwaAppId, result_list.get());
+    RemoveActionsForApp(extension_misc::kOfficePwaAppId,
+                        &resulting_tasks->tasks);
 
     // Hack around the fact that App Service will only return one task for each
     // app. We want both tasks to be available, so add the office task if the
     // WebDrive task is available.
     // TODO(petermarshall): Find a better way to enable both tasks.
-    auto it =
-        base::ranges::find_if(*result_list, [](const FullTaskDescriptor& task) {
+    auto it = base::ranges::find_if(
+        resulting_tasks->tasks, [](const FullTaskDescriptor& task) {
           if (!IsFilesAppId(task.task_descriptor.app_id)) {
             return false;
           }
@@ -363,19 +365,19 @@
                  action_id == kActionIdWebDriveOfficeExcel ||
                  action_id == kActionIdWebDriveOfficePowerPoint;
         });
-    if (it != result_list->end()) {
+    if (it != resulting_tasks->tasks.end()) {
       FullTaskDescriptor office_task(*it);
       office_task.task_descriptor.action_id =
           base::StrCat({kChromeUIFileManagerURL, "?", kActionIdOpenInOffice});
-      result_list->push_back(office_task);
+      resulting_tasks->tasks.push_back(office_task);
     }
   }
 
   if (!disabled_actions.empty())
-    RemoveFileManagerInternalActions(disabled_actions, result_list.get());
+    RemoveFileManagerInternalActions(disabled_actions, &resulting_tasks->tasks);
 
-  ChooseAndSetDefaultTask(profile, entries, result_list.get());
-  std::move(callback).Run(std::move(result_list));
+  ChooseAndSetDefaultTask(profile, entries, resulting_tasks.get());
+  std::move(callback).Run(std::move(resulting_tasks));
 }
 
 // Returns true if |extension_id| and |action_id| indicate that the file
@@ -560,6 +562,9 @@
 
 }  // namespace
 
+ResultingTasks::ResultingTasks() = default;
+ResultingTasks::~ResultingTasks() = default;
+
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(prefs::kDefaultHandlersForFileExtensions);
 }
@@ -952,23 +957,23 @@
   return result;
 }
 
-void FindExtensionAndAppTasks(
-    Profile* profile,
-    const std::vector<extensions::EntryInfo>& entries,
-    const std::vector<GURL>& file_urls,
-    FindTasksCallback callback,
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result_list) {
-  std::vector<FullTaskDescriptor>* result_list_ptr = result_list.get();
+void FindExtensionAndAppTasks(Profile* profile,
+                              const std::vector<extensions::EntryInfo>& entries,
+                              const std::vector<GURL>& file_urls,
+                              FindTasksCallback callback,
+                              std::unique_ptr<ResultingTasks> resulting_tasks) {
+  auto* tasks = &resulting_tasks->tasks;
 
   // 2. Web tasks file_handlers (View/Open With), Chrome app file_handlers, and
   // extension file_browser_handlers.
-  FindAppServiceTasks(profile, entries, file_urls, result_list_ptr);
+  FindAppServiceTasks(profile, entries, file_urls, tasks);
 
   // 3. Find and append Guest OS tasks.
-  FindGuestOsTasks(profile, entries, file_urls, result_list_ptr,
-                   // Done. Apply post-filtering and callback.
-                   base::BindOnce(PostProcessFoundTasks, profile, entries,
-                                  std::move(callback), std::move(result_list)));
+  FindGuestOsTasks(
+      profile, entries, file_urls, tasks,
+      // Done. Apply post-filtering and callback.
+      base::BindOnce(PostProcessFoundTasks, profile, entries,
+                     std::move(callback), std::move(resulting_tasks)));
 }
 
 void FindAllTypesOfTasks(Profile* profile,
@@ -976,18 +981,17 @@
                          const std::vector<GURL>& file_urls,
                          FindTasksCallback callback) {
   DCHECK(profile);
-  std::unique_ptr<std::vector<FullTaskDescriptor>> result_list(
-      new std::vector<FullTaskDescriptor>);
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
 
   if (ash::features::ShouldArcAndGuestOsFileTasksUseAppService()) {
     // Skip FindArcTasks and FindGuestOsTasks since these tasks are now found in
     // App Service.
-    FindAppServiceTasks(profile, entries, file_urls, result_list.get());
+    FindAppServiceTasks(profile, entries, file_urls, &resulting_tasks->tasks);
     PostProcessFoundTasks(profile, entries, std::move(callback),
-                          std::move(result_list));
+                          std::move(resulting_tasks));
   } else {
     // 1. Find and append ARC handler tasks.
-    FindArcTasks(profile, entries, file_urls, std::move(result_list),
+    FindArcTasks(profile, entries, file_urls, std::move(resulting_tasks),
                  base::BindOnce(&FindExtensionAndAppTasks, profile, entries,
                                 file_urls, std::move(callback)));
   }
@@ -995,9 +999,9 @@
 
 void ChooseAndSetDefaultTask(Profile* profile,
                              const std::vector<extensions::EntryInfo>& entries,
-                             std::vector<FullTaskDescriptor>* tasks) {
+                             ResultingTasks* resulting_tasks) {
   // Collect the default tasks from the preferences into a set.
-  std::set<TaskDescriptor> default_tasks;
+  base::flat_set<TaskDescriptor> default_tasks;
   for (const extensions::EntryInfo& entry : entries) {
     const base::FilePath& file_path = entry.path;
     const std::string& mime_type = entry.mime_type;
@@ -1041,9 +1045,11 @@
     }
   }
 
+  auto& tasks = resulting_tasks->tasks;
+
   // Go through all the tasks from the beginning and see if there is any
   // default task. If found, pick and set it as default and return.
-  for (FullTaskDescriptor& task : *tasks) {
+  for (FullTaskDescriptor& task : tasks) {
     DCHECK(!task.is_default);
     if (base::Contains(default_tasks, task.task_descriptor)) {
       task.is_default = true;
@@ -1053,7 +1059,7 @@
 
   // No default task. If the "Open in Docs/Sheets/Slides through Drive" workflow
   // is available for Office files, set as default.
-  for (FullTaskDescriptor& task : *tasks) {
+  for (FullTaskDescriptor& task : tasks) {
     if (IsWebDriveOfficeTask(task.task_descriptor)) {
       task.is_default = true;
       return;
@@ -1063,7 +1069,7 @@
   // Check for an explicit file extension match (without MIME match) in the
   // extension manifest and pick that over the fallback handlers below (see
   // crbug.com/803930)
-  for (FullTaskDescriptor& task : *tasks) {
+  for (FullTaskDescriptor& task : tasks) {
     if (task.is_file_extension_match && !task.is_generic_file_handler &&
         !IsFallbackFileHandler(task)) {
       task.is_default = true;
@@ -1073,7 +1079,7 @@
 
   // Prefer a fallback app over viewing in the browser (crbug.com/1111399).
   // Unless it's HTML which should open in the browser (crbug.com/1121396).
-  for (FullTaskDescriptor& task : *tasks) {
+  for (FullTaskDescriptor& task : tasks) {
     if (IsFallbackFileHandler(task) &&
         ParseFilesAppActionId(task.task_descriptor.action_id) !=
             "view-in-browser") {
@@ -1090,7 +1096,7 @@
 
   // No default tasks found. If there is any fallback file browser handler,
   // make it as default task, so it's selected by default.
-  for (FullTaskDescriptor& task : *tasks) {
+  for (FullTaskDescriptor& task : tasks) {
     DCHECK(!task.is_default);
     if (IsFallbackFileHandler(task)) {
       task.is_default = true;
diff --git a/chrome/browser/ash/file_manager/file_tasks.h b/chrome/browser/ash/file_manager/file_tasks.h
index c9b4fc88..dbe8202 100644
--- a/chrome/browser/ash/file_manager/file_tasks.h
+++ b/chrome/browser/ash/file_manager/file_tasks.h
@@ -101,7 +101,6 @@
 #include "base/callback_forward.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
 #include "components/prefs/pref_registry_simple.h"
-#include "extensions/browser/api/file_handlers/app_file_handler_util.h"
 #include "url/gurl.h"
 
 class PrefService;
@@ -250,6 +249,29 @@
   bool is_file_extension_match;
 };
 
+// Describes how admin policy affects the default task in a ResultingTasks.
+enum class PolicyDefaultHandlerStatus {
+  // Indicates that the default task was selected according to the policy
+  // settings.
+  kDefaultHandlerAssignedByPolicy,
+
+  // Indicates that no default task was set due to some assignment conflicts.
+  // Possible reasons are:
+  //  * The user is trying to open multiple files which have different policy
+  //  default handlers;
+  //  * The admin-specified handler was not found in the list of tasks.
+  kIncorrectAssignment
+};
+
+// Represents a set of tasks capable of handling file entries.
+struct ResultingTasks {
+  ResultingTasks();
+  ~ResultingTasks();
+
+  std::vector<FullTaskDescriptor> tasks;
+  absl::optional<PolicyDefaultHandlerStatus> policy_default_handler_status;
+};
+
 // Registers profile prefs related to file_manager.
 void RegisterProfilePrefs(PrefRegistrySimple*);
 
@@ -318,7 +340,7 @@
 
 // Callback function type for FindAllTypesOfTasks.
 typedef base::OnceCallback<void(
-    std::unique_ptr<std::vector<FullTaskDescriptor>> result)>
+    std::unique_ptr<ResultingTasks> resulting_tasks)>
     FindTasksCallback;
 
 // Finds all types (file handlers, file browser handlers) of
@@ -333,12 +355,13 @@
                          const std::vector<GURL>& file_urls,
                          FindTasksCallback callback);
 
-// Chooses the default task in |tasks| and sets it as default, if the default
-// task is found (i.e. the default task may not exist in |tasks|). No tasks
-// should be set as default before calling this function.
+// Chooses the default task in |resulting_tasks| and sets it as default, if the
+// default task is found (i.e. the default task may not exist in
+// |resulting_tasks|). No tasks should be set as default before calling this
+// function.
 void ChooseAndSetDefaultTask(Profile* profile,
                              const std::vector<extensions::EntryInfo>& entries,
-                             std::vector<FullTaskDescriptor>* tasks);
+                             ResultingTasks* resulting_tasks);
 
 // Returns whether |path| is an HTML file according to its extension.
 bool IsHtmlFile(const base::FilePath& path);
diff --git a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
index 6e5af78..4911562 100644
--- a/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_browsertest.cc
@@ -67,27 +67,29 @@
 // asynchronously).
 void VerifyTasks(int* remaining,
                  Expectation expectation,
-                 std::unique_ptr<std::vector<FullTaskDescriptor>> result) {
-  ASSERT_TRUE(result) << expectation.file_extensions;
+                 std::unique_ptr<ResultingTasks> resulting_tasks) {
+  ASSERT_TRUE(resulting_tasks) << expectation.file_extensions;
   --*remaining;
 
-  auto default_task =
-      base::ranges::find_if(*result, &FullTaskDescriptor::is_default);
+  auto default_task = base::ranges::find_if(resulting_tasks->tasks,
+                                            &FullTaskDescriptor::is_default);
 
   // Early exit for the uncommon situation where no default should be set.
   if (!expectation.app_id) {
-    EXPECT_TRUE(default_task == result->end()) << expectation.file_extensions;
+    EXPECT_TRUE(default_task == resulting_tasks->tasks.end())
+        << expectation.file_extensions;
     return;
   }
 
-  ASSERT_TRUE(default_task != result->end()) << expectation.file_extensions;
+  ASSERT_TRUE(default_task != resulting_tasks->tasks.end())
+      << expectation.file_extensions;
 
   EXPECT_EQ(expectation.app_id, default_task->task_descriptor.app_id)
       << " for extension: " << expectation.file_extensions;
 
   // Verify no other task is set as default.
-  EXPECT_EQ(1, std::count_if(result->begin(), result->end(),
-                             [](const auto& task) { return task.is_default; }))
+  EXPECT_EQ(1, base::ranges::count_if(resulting_tasks->tasks,
+                                      &FullTaskDescriptor::is_default))
       << expectation.file_extensions;
 }
 
@@ -95,8 +97,8 @@
 void VerifyAsyncTask(int* remaining,
                      Expectation expectation,
                      base::OnceClosure quit_closure,
-                     std::unique_ptr<std::vector<FullTaskDescriptor>> result) {
-  VerifyTasks(remaining, expectation, std::move(result));
+                     std::unique_ptr<ResultingTasks> resulting_tasks) {
+  VerifyTasks(remaining, expectation, std::move(resulting_tasks));
   std::move(quit_closure).Run();
 }
 
@@ -141,7 +143,7 @@
         } else {
           EXPECT_FALSE(mime_type.empty()) << "No mime type for " << path;
         }
-        entries.push_back({path, mime_type, false});
+        entries.emplace_back(path, mime_type, false);
         GURL url = GURL(base::JoinString(
             {"filesystem:https://site.com/isolated/foo.", extension}, ""));
         ASSERT_TRUE(url.is_valid());
diff --git a/chrome/browser/ash/file_manager/file_tasks_unittest.cc b/chrome/browser/ash/file_manager/file_tasks_unittest.cc
index 9ce0f01..08a371c 100644
--- a/chrome/browser/ash/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/ash/file_manager/file_tasks_unittest.cc
@@ -66,8 +66,7 @@
 
 using extensions::api::file_manager_private::Verb;
 
-namespace file_manager {
-namespace file_tasks {
+namespace file_manager::file_tasks {
 
 TEST(FileManagerFileTasksTest, FullTaskDescriptor_WithIconAndDefault) {
   FullTaskDescriptor full_descriptor(
@@ -233,7 +232,10 @@
                                "action-id");
   TaskDescriptor nice_app_task("nice-app-id", TASK_TYPE_FILE_HANDLER,
                                "action-id");
-  std::vector<FullTaskDescriptor> tasks;
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
   tasks.emplace_back(
       text_app_task, "Text.app", Verb::VERB_OPEN_WITH,
       GURL("http://example.com/text_app.png"), false /* is_default */,
@@ -248,7 +250,7 @@
 
   // None of them should be chosen as default, as nothing is set in the
   // preferences.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_FALSE(tasks[0].is_default);
   EXPECT_FALSE(tasks[1].is_default);
 
@@ -259,7 +261,7 @@
   UpdateDefaultTaskPreferences(mime_types, empty);
 
   // Text.app should be chosen as default.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_TRUE(tasks[0].is_default);
   EXPECT_FALSE(tasks[1].is_default);
 
@@ -268,7 +270,7 @@
 
   // Clear the preferences and make sure none of them are default.
   UpdateDefaultTaskPreferences(empty, empty);
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_FALSE(tasks[0].is_default);
   EXPECT_FALSE(tasks[1].is_default);
 
@@ -278,7 +280,7 @@
   UpdateDefaultTaskPreferences(empty, suffixes);
 
   // Now Nice.app should be chosen as default.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_FALSE(tasks[0].is_default);
   EXPECT_TRUE(tasks[1].is_default);
 }
@@ -291,7 +293,10 @@
   // "foo.txt".
   TaskDescriptor files_app_task(
       kFileManagerAppId, TASK_TYPE_FILE_BROWSER_HANDLER, "view-in-browser");
-  std::vector<FullTaskDescriptor> tasks;
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
   tasks.emplace_back(
       files_app_task, "View in browser", Verb::VERB_OPEN_WITH,
       GURL("http://example.com/some_icon.png"), false /* is_default */,
@@ -302,7 +307,7 @@
 
   // The internal file browser handler should be chosen as default, as it's a
   // fallback file browser handler.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_TRUE(tasks[0].is_default);
 }
 
@@ -316,7 +321,10 @@
   // Define the text editor app for "foo.txt".
   TaskDescriptor text_app_task(kTextEditorAppId, TASK_TYPE_FILE_HANDLER,
                                "Text");
-  std::vector<FullTaskDescriptor> tasks;
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
   tasks.emplace_back(
       files_app_task, "View in browser", Verb::VERB_OPEN_WITH,
       GURL("http://example.com/some_icon.png"), false /* is_default */,
@@ -332,7 +340,7 @@
 
   // The text editor app should be chosen as default, as it's a fallback file
   // browser handler.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_TRUE(tasks[1].is_default);
 }
 
@@ -346,7 +354,10 @@
   // Define the text editor app for "foo.html".
   TaskDescriptor text_app_task(kTextEditorAppId, TASK_TYPE_FILE_HANDLER,
                                "Text");
-  std::vector<FullTaskDescriptor> tasks;
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
   tasks.emplace_back(
       files_app_task, "View in browser", Verb::VERB_OPEN_WITH,
       GURL("http://example.com/some_icon.png"), false /* is_default */,
@@ -362,7 +373,7 @@
 
   // The internal file browser handler should be chosen as default,
   // as it's a fallback file browser handler.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_TRUE(tasks[0].is_default);
 }
 
@@ -374,7 +385,10 @@
   TaskDescriptor files_app_task(
       extension_misc::kQuickOfficeComponentExtensionId, TASK_TYPE_FILE_HANDLER,
       "Office Editing for Docs, Sheets & Slides");
-  std::vector<FullTaskDescriptor> tasks;
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
   tasks.emplace_back(
       files_app_task, "Office Editing for Docs, Sheets & Slides",
       Verb::VERB_OPEN_WITH,
@@ -387,7 +401,7 @@
 
   // The Office Editing app should be chosen as default, as it's a fallback
   // file browser handler.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   EXPECT_TRUE(tasks[0].is_default);
 }
 
@@ -449,7 +463,10 @@
   // Create the file task descriptors to match against.
   TaskDescriptor app_service_file_task(app_id, task_type, activity);
   TaskDescriptor other_task("other", TASK_TYPE_FILE_BROWSER_HANDLER, "view");
-  std::vector<FullTaskDescriptor> tasks;
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
   tasks.emplace_back(
       app_service_file_task, "View Images", Verb::VERB_NONE,
       GURL("http://example.com/some_icon.png"), false /* is_default */,
@@ -463,7 +480,7 @@
                        false);
 
   // Check if the correct task matched against the default preference.
-  ChooseAndSetDefaultTask(profile(), entries, &tasks);
+  ChooseAndSetDefaultTask(profile(), entries, resulting_tasks.get());
   ASSERT_TRUE(tasks[0].is_default);
   ASSERT_FALSE(tasks[1].is_default);
 }
@@ -584,17 +601,16 @@
     void Call(Profile* profile,
               const std::vector<extensions::EntryInfo>& entries,
               const std::vector<GURL>& file_urls,
-              std::vector<FullTaskDescriptor>* result) {
+              ResultingTasks* resulting_tasks) {
       FindAllTypesOfTasks(
           profile, entries, file_urls,
           base::BindOnce(&FindAllTypesOfTasksSynchronousWrapper::OnReply,
-                         base::Unretained(this), result));
+                         base::Unretained(this), resulting_tasks));
       run_loop_.Run();
     }
 
    private:
-    void OnReply(std::vector<FullTaskDescriptor>* out,
-                 std::unique_ptr<std::vector<FullTaskDescriptor>> result) {
+    void OnReply(ResultingTasks* out, std::unique_ptr<ResultingTasks> result) {
       *out = *result;
       run_loop_.Quit();
     }
@@ -696,17 +712,19 @@
       {crostini_folder_.Append("foo.txt"), "text/plain", false}};
   std::vector<GURL> file_urls{PathToURL("dir/foo.txt")};
 
-  std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(text_app_id_, tasks[0].task_descriptor.app_id);
 
   // Multiple text files
   entries.emplace_back(crostini_folder_.Append("bar.txt"), "text/plain", false);
   file_urls.emplace_back(PathToURL("dir/bar.txt"));
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(text_app_id_, tasks[0].task_descriptor.app_id);
 }
@@ -715,15 +733,18 @@
   std::vector<extensions::EntryInfo> entries{
       {crostini_folder_.Append("dir"), "", true}};
   std::vector<GURL> file_urls{PathToURL("dir/dir")};
-  std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   EXPECT_EQ(0U, tasks.size());
 
   entries.emplace_back(crostini_folder_.Append("foo.txt"), "text/plain", false);
   file_urls.emplace_back(PathToURL("dir/foo.txt"));
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   EXPECT_EQ(0U, tasks.size());
 }
 
@@ -734,9 +755,11 @@
   std::vector<GURL> file_urls{PathToURL("dir/foo.gif"),
                               PathToURL("dir/bar.gif")};
 
-  std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   // The returned values happen to be ordered alphabetically by app_id, so we
   // rely on this to keep the test simple.
   EXPECT_LT(gif_app_id_, image_app_id_);
@@ -752,16 +775,18 @@
   std::vector<GURL> file_urls{PathToURL("dir/foo.gif"),
                               PathToURL("dir/bar.png")};
 
-  std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(image_app_id_, tasks[0].task_descriptor.app_id);
 
   entries.emplace_back(crostini_folder_.Append("qux.mp4"), "video/mp4", false);
   file_urls.emplace_back(PathToURL("dir/qux.mp4"));
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   EXPECT_EQ(0U, tasks.size());
 }
 
@@ -772,12 +797,13 @@
   std::vector<GURL> file_urls{PathToURL("dir/bar1.foo"),
                               PathToURL("dir/bar2.foo")};
 
-  std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(test_profile_.get(), entries,
-                                               file_urls, &tasks);
+  auto resulting_tasks = std::make_unique<ResultingTasks>();
+  std::vector<FullTaskDescriptor>& tasks = resulting_tasks->tasks;
+
+  FindAllTypesOfTasksSynchronousWrapper().Call(
+      test_profile_.get(), entries, file_urls, resulting_tasks.get());
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(alt_mime_app_id_, tasks[0].task_descriptor.app_id);
 }
 
-}  // namespace file_tasks
-}  // namespace file_manager.
+}  // namespace file_manager::file_tasks
diff --git a/chrome/browser/ash/file_manager/open_util.cc b/chrome/browser/ash/file_manager/open_util.cc
index b841e53..7430517 100644
--- a/chrome/browser/ash/file_manager/open_util.cc
+++ b/chrome/browser/ash/file_manager/open_util.cc
@@ -33,8 +33,7 @@
 using content::BrowserThread;
 using storage::FileSystemURL;
 
-namespace file_manager {
-namespace util {
+namespace file_manager::util {
 namespace {
 
 bool shell_operations_allowed = true;
@@ -82,11 +81,11 @@
     Profile* profile,
     const GURL& url,
     platform_util::OpenOperationCallback callback,
-    std::unique_ptr<std::vector<file_tasks::FullTaskDescriptor>> tasks) {
+    std::unique_ptr<file_tasks::ResultingTasks> resulting_tasks) {
   // Select a default handler. If a default handler is not available, select
   // the first non-generic file handler.
   const file_tasks::FullTaskDescriptor* chosen_task = nullptr;
-  for (const auto& task : *tasks) {
+  for (const auto& task : resulting_tasks->tasks) {
     if (!task.is_generic_file_handler) {
       if (task.is_default) {
         chosen_task = &task;
@@ -233,5 +232,4 @@
   shell_operations_allowed = false;
 }
 
-}  // namespace util
-}  // namespace file_manager
+}  // namespace file_manager::util
diff --git a/chrome/browser/ash/file_manager/open_util.h b/chrome/browser/ash/file_manager/open_util.h
index a00de535..5af914c 100644
--- a/chrome/browser/ash/file_manager/open_util.h
+++ b/chrome/browser/ash/file_manager/open_util.h
@@ -17,8 +17,7 @@
 class FilePath;
 }
 
-namespace file_manager {
-namespace util {
+namespace file_manager::util {
 
 // If |item_type| is OPEN_FILE: Opens an item using a file handler, a file
 // browser handler, or the browser (open in a tab). The default handler has
@@ -48,7 +47,6 @@
 // extensions including a file browser.
 void DisableShellOperationsForTesting();
 
-}  // namespace util
-}  // namespace file_manager
+}  // namespace file_manager::util
 
 #endif  // CHROME_BROWSER_ASH_FILE_MANAGER_OPEN_UTIL_H_
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 824dd41..8bcef8cd 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -1303,8 +1303,8 @@
             .Get(policy::key::kSessionLocales);
     if (entry && entry->level == policy::POLICY_LEVEL_RECOMMENDED &&
         entry->value(base::Value::Type::LIST)) {
-      base::Value::ConstListView list =
-          entry->value(base::Value::Type::LIST)->GetListDeprecated();
+      const base::Value::List& list =
+          entry->value(base::Value::Type::LIST)->GetList();
       if (!list.empty() && list[0].is_string()) {
         locale = list[0].GetString();
         new_user_context.SetPublicSessionLocale(locale);
diff --git a/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc b/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc
index 3272f65..ea7ac57c 100644
--- a/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc
+++ b/chrome/browser/ash/login/saml/password_sync_token_fetcher.cc
@@ -343,8 +343,7 @@
         consumer_->OnApiCallFailed(ErrorType::kGetNoList);
         return;
       }
-      base::Value::ConstListView list_of_tokens =
-          token_list_entry->GetListDeprecated();
+      const base::Value::List& list_of_tokens = token_list_entry->GetList();
       if (list_of_tokens.size() > 0) {
         const auto* sync_token_value =
             list_of_tokens[0].FindKeyOfType(kToken, base::Value::Type::STRING);
diff --git a/chrome/browser/ash/login/screens/active_directory_login_screen.cc b/chrome/browser/ash/login/screens/active_directory_login_screen.cc
index b3420900..1cc1e26 100644
--- a/chrome/browser/ash/login/screens/active_directory_login_screen.cc
+++ b/chrome/browser/ash/login/screens/active_directory_login_screen.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/ui/signin_ui.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/webui/chromeos/login/active_directory_login_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
@@ -138,7 +139,8 @@
       DCHECK(account_info.has_account_id() &&
              !account_info.account_id().empty() &&
              LoginDisplayHost::default_host());
-      const AccountId account_id(user_manager::known_user::GetAccountId(
+      user_manager::KnownUser known_user(g_browser_process->local_state());
+      const AccountId account_id(known_user.GetAccountId(
           username, account_info.account_id(), AccountType::ACTIVE_DIRECTORY));
       LoginDisplayHost::default_host()->SetDisplayAndGivenName(
           account_info.display_name(), account_info.given_name());
diff --git a/chrome/browser/ash/login/screens/active_directory_password_change_screen.cc b/chrome/browser/ash/login/screens/active_directory_password_change_screen.cc
index 2aeae76..247b55e 100644
--- a/chrome/browser/ash/login/screens/active_directory_password_change_screen.cc
+++ b/chrome/browser/ash/login/screens/active_directory_password_change_screen.cc
@@ -10,6 +10,7 @@
 #include "base/check_op.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chrome/grit/generated_resources.h"
@@ -114,7 +115,8 @@
     case authpolicy::ERROR_NONE: {
       DCHECK(account_info.has_account_id() &&
              !account_info.account_id().empty());
-      const AccountId account_id = user_manager::known_user::GetAccountId(
+      user_manager::KnownUser known_user(g_browser_process->local_state());
+      const AccountId account_id = known_user.GetAccountId(
           username, account_info.account_id(), AccountType::ACTIVE_DIRECTORY);
       DCHECK(LoginDisplayHost::default_host());
       LoginDisplayHost::default_host()->SetDisplayAndGivenName(
diff --git a/chrome/browser/ash/login/screens/chrome_user_selection_screen.cc b/chrome/browser/ash/login/screens/chrome_user_selection_screen.cc
index 938e857..7c255fa 100644
--- a/chrome/browser/ash/login/screens/chrome_user_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/chrome_user_selection_screen.cc
@@ -82,7 +82,8 @@
 
 void ChromeUserSelectionScreen::CheckForPublicSessionDisplayNameChange(
     policy::DeviceLocalAccountPolicyBroker* broker) {
-  const AccountId& account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId account_id = known_user.GetAccountId(
       broker->user_id(), std::string() /* id */, AccountType::UNKNOWN);
   DCHECK(account_id.is_valid());
   const std::string& display_name = broker->GetDisplayName();
@@ -113,7 +114,8 @@
 
 void ChromeUserSelectionScreen::CheckForPublicSessionLocalePolicyChange(
     policy::DeviceLocalAccountPolicyBroker* broker) {
-  const AccountId& account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId account_id = known_user.GetAccountId(
       broker->user_id(), std::string() /* id */, AccountType::UNKNOWN);
   DCHECK(account_id.is_valid());
   const policy::PolicyMap::Entry* entry =
diff --git a/chrome/browser/ash/login/screens/network_screen.cc b/chrome/browser/ash/login/screens/network_screen.cc
index dc66588..19a16ac4 100644
--- a/chrome/browser/ash/login/screens/network_screen.cc
+++ b/chrome/browser/ash/login/screens/network_screen.cc
@@ -32,17 +32,11 @@
 // static
 std::string NetworkScreen::GetResultString(Result result) {
   switch (result) {
-    case Result::CONNECTED_REGULAR:
-    case Result::CONNECTED_DEMO:
-    case Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT:
+    case Result::CONNECTED:
       return "Connected";
-    case Result::BACK_REGULAR:
-    case Result::BACK_DEMO:
-    case Result::BACK_OS_INSTALL:
+    case Result::BACK:
       return "Back";
     case Result::NOT_APPLICABLE:
-    case Result::NOT_APPLICABLE_CONSOLIDATED_CONSENT:
-    case Result::NOT_APPLICABLE_CONNECTED_DEMO:
       return BaseScreen::kNotApplicable;
   }
 }
@@ -130,14 +124,7 @@
 }
 
 void NetworkScreen::NotifyOnConnection() {
-  if (DemoSetupController::IsOobeDemoSetupFlowInProgress()) {
-    exit_callback_.Run(Result::CONNECTED_DEMO);
-  } else {
-    if (chromeos::features::IsOobeConsolidatedConsentEnabled())
-      exit_callback_.Run(Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
-    else
-      exit_callback_.Run(Result::CONNECTED_REGULAR);
-  }
+  exit_callback_.Run(Result::CONNECTED);
 }
 
 void NetworkScreen::OnConnectionTimeout() {
@@ -202,12 +189,7 @@
   if (view_)
     view_->ClearErrors();
 
-  if (DemoSetupController::IsOobeDemoSetupFlowInProgress())
-    exit_callback_.Run(Result::BACK_DEMO);
-  else if (switches::IsOsInstallAllowed())
-    exit_callback_.Run(Result::BACK_OS_INSTALL);
-  else
-    exit_callback_.Run(Result::BACK_REGULAR);
+  exit_callback_.Run(Result::BACK);
 }
 
 void NetworkScreen::OnContinueButtonClicked() {
@@ -239,22 +221,10 @@
 
   if (is_hidden()) {
     // Screen not shown yet: skipping it.
-    if (DemoSetupController::IsOobeDemoSetupFlowInProgress()) {
-      exit_callback_.Run(Result::NOT_APPLICABLE_CONNECTED_DEMO);
-    } else {
-      if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
-        exit_callback_.Run(Result::NOT_APPLICABLE_CONSOLIDATED_CONSENT);
-      } else {
-        exit_callback_.Run(Result::NOT_APPLICABLE);
-      }
-    }
+    exit_callback_.Run(Result::NOT_APPLICABLE);
   } else {
     // Screen already shown: automatically continuing.
-    if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
-      exit_callback_.Run(Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
-    } else {
-      exit_callback_.Run(Result::CONNECTED_REGULAR);
-    }
+    exit_callback_.Run(Result::CONNECTED);
   }
 
   return true;
diff --git a/chrome/browser/ash/login/screens/network_screen.h b/chrome/browser/ash/login/screens/network_screen.h
index fd07cd8..c937784 100644
--- a/chrome/browser/ash/login/screens/network_screen.h
+++ b/chrome/browser/ash/login/screens/network_screen.h
@@ -29,15 +29,9 @@
   using TView = NetworkScreenView;
 
   enum class Result {
-    CONNECTED_REGULAR,
-    CONNECTED_DEMO,
-    CONNECTED_REGULAR_CONSOLIDATED_CONSENT,
-    BACK_REGULAR,
-    BACK_DEMO,
-    BACK_OS_INSTALL,
+    CONNECTED,
+    BACK,
     NOT_APPLICABLE,
-    NOT_APPLICABLE_CONSOLIDATED_CONSENT,
-    NOT_APPLICABLE_CONNECTED_DEMO,
   };
 
   static std::string GetResultString(Result result);
diff --git a/chrome/browser/ash/login/screens/network_screen_browsertest.cc b/chrome/browser/ash/login/screens/network_screen_browsertest.cc
index 08e9f8f7..7d40f36 100644
--- a/chrome/browser/ash/login/screens/network_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/network_screen_browsertest.cc
@@ -81,14 +81,7 @@
     network_screen->OnContinueButtonClicked();
     base::RunLoop().RunUntilIdle();
 
-    ASSERT_TRUE(last_screen_result_.has_value());
-    if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
-      EXPECT_EQ(NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT,
-                last_screen_result_.value());
-    } else {
-      EXPECT_EQ(NetworkScreen::Result::CONNECTED_REGULAR,
-                last_screen_result_.value());
-    }
+    CheckResult(NetworkScreen::Result::CONNECTED);
   }
 
   void SetDefaultNetworkStateHelperExpectations() {
@@ -208,10 +201,7 @@
   network_screen()->UpdateStatus();
 
   WaitForScreenExit();
-  if (chromeos::features::IsOobeConsolidatedConsentEnabled())
-    CheckResult(NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
-  else
-    CheckResult(NetworkScreen::Result::CONNECTED_REGULAR);
+  CheckResult(NetworkScreen::Result::CONNECTED);
 }
 
 // The network screen should NOT be skipped if the connection times out, even if
@@ -248,10 +238,7 @@
   ShowNetworkScreen();
   WaitForScreenExit();
 
-  if (chromeos::features::IsOobeConsolidatedConsentEnabled())
-    CheckResult(NetworkScreen::Result::NOT_APPLICABLE_CONSOLIDATED_CONSENT);
-  else
-    CheckResult(NetworkScreen::Result::NOT_APPLICABLE);
+  CheckResult(NetworkScreen::Result::NOT_APPLICABLE);
 
   histogram_tester_.ExpectTotalCount(
       "OOBE.StepCompletionTimeByExitReason.Network-selection.Connected", 0);
@@ -288,10 +275,7 @@
   network_screen()->UpdateStatus();
   WaitForScreenExit();
 
-  if (chromeos::features::IsOobeConsolidatedConsentEnabled())
-    CheckResult(NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
-  else
-    CheckResult(NetworkScreen::Result::CONNECTED_REGULAR);
+  CheckResult(NetworkScreen::Result::CONNECTED);
 
   // Showing screen again to test skip doesn't work now.
   ShowNetworkScreen();
diff --git a/chrome/browser/ash/login/screens/network_screen_unittest.cc b/chrome/browser/ash/login/screens/network_screen_unittest.cc
index 9b4e0c2..b20478c 100644
--- a/chrome/browser/ash/login/screens/network_screen_unittest.cc
+++ b/chrome/browser/ash/login/screens/network_screen_unittest.cc
@@ -88,14 +88,7 @@
   EXPECT_EQ(1, network_screen_->continue_attempts_);
 
   ASSERT_TRUE(last_screen_result_.has_value());
-
-  if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
-    EXPECT_EQ(NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT,
-              last_screen_result_.value());
-  } else {
-    EXPECT_EQ(NetworkScreen::Result::CONNECTED_REGULAR,
-              last_screen_result_.value());
-  }
+  EXPECT_EQ(NetworkScreen::Result::CONNECTED, last_screen_result_.value());
 }
 
 TEST_F(NetworkScreenUnitTest, ContinuesOnlyOnce) {
@@ -114,13 +107,7 @@
   ASSERT_EQ(1, network_screen_->continue_attempts_);
 
   ASSERT_TRUE(last_screen_result_.has_value());
-  if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
-    EXPECT_EQ(NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT,
-              last_screen_result_.value());
-  } else {
-    EXPECT_EQ(NetworkScreen::Result::CONNECTED_REGULAR,
-              last_screen_result_.value());
-  }
+  EXPECT_EQ(NetworkScreen::Result::CONNECTED, last_screen_result_.value());
 
   // Stop waiting for another network, net1.
   network_screen_->StopWaitingForConnection(u"net1");
diff --git a/chrome/browser/ash/login/screens/offline_login_screen.cc b/chrome/browser/ash/login/screens/offline_login_screen.cc
index 957cbb0..219d394 100644
--- a/chrome/browser/ash/login/screens/offline_login_screen.cc
+++ b/chrome/browser/ash/login/screens/offline_login_screen.cc
@@ -132,7 +132,8 @@
 void OfflineLoginScreen::HandleCompleteAuth(const std::string& email,
                                             const std::string& password) {
   const std::string sanitized_email = gaia::SanitizeEmail(email);
-  const AccountId account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId account_id = known_user.GetAccountId(
       sanitized_email, std::string() /* id */, AccountType::UNKNOWN);
   const user_manager::User* user =
       user_manager::UserManager::Get()->FindUser(account_id);
diff --git a/chrome/browser/ash/login/screens/user_selection_screen.cc b/chrome/browser/ash/login/screens/user_selection_screen.cc
index ba30117..22542f4d 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen.cc
@@ -144,7 +144,8 @@
 AccountId GetOwnerAccountId() {
   std::string owner_email;
   CrosSettings::Get()->GetString(kDeviceOwner, &owner_email);
-  const AccountId owner = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId owner = known_user.GetAccountId(
       owner_email, std::string() /* id */, AccountType::UNKNOWN);
   return owner;
 }
diff --git a/chrome/browser/ash/login/test/session_flags_manager.cc b/chrome/browser/ash/login/test/session_flags_manager.cc
index 89a86c6..adb3b92 100644
--- a/chrome/browser/ash/login/test/session_flags_manager.cc
+++ b/chrome/browser/ash/login/test/session_flags_manager.cc
@@ -136,7 +136,7 @@
   base::Value* user_flags = value->FindListKey(kUserFlagsKey);
   if (user_flags) {
     user_flags_ = std::vector<Switch>();
-    for (const base::Value& flag : user_flags->GetListDeprecated()) {
+    for (const base::Value& flag : user_flags->GetList()) {
       DCHECK(flag.is_dict());
       user_flags_->emplace_back(
           std::make_pair(*flag.FindStringKey(kFlagNameKey),
@@ -147,7 +147,7 @@
   base::Value* restart_job = value->FindListKey(kRestartJobKey);
   if (restart_job) {
     restart_job_ = std::vector<Switch>();
-    for (const base::Value& job_switch : restart_job->GetListDeprecated()) {
+    for (const base::Value& job_switch : restart_job->GetList()) {
       DCHECK(job_switch.is_dict());
       restart_job_->emplace_back(
           std::make_pair(*job_switch.FindStringKey(kFlagNameKey),
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 58c1a106..c9443f42 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -1346,7 +1346,7 @@
   if (!chromeos::features::IsOobeConsolidatedConsentEnabled())
     StartupUtils::MarkEulaAccepted();
 
-  PerformPostEulaActions();
+  PerformPostNetworkScreenActions();
   OnDeviceDisabledChecked(false /* device_disabled */);
 }
 
@@ -1420,43 +1420,68 @@
 void WizardController::OnQuickStartScreenExit(QuickStartScreen::Result result) {
 }
 
+// The network screen is part of 3 different flows:
+// * Regular Flow: Welcome Screen -> Network Screen -> Update Screen.
+// * Demo Flow: Welcome Screen -> Network Screen -> Demo Preferences Screen.
+// * OS Install Flow: OS Trial Screen -> Network Screen -> Update Screen.
 void WizardController::OnNetworkScreenExit(NetworkScreen::Result result) {
   OnScreenExit(NetworkScreenView::kScreenId,
                NetworkScreen::GetResultString(result));
 
+  // Demo mode flow.
+  if (DemoSetupController::IsOobeDemoSetupFlowInProgress()) {
+    switch (result) {
+      case NetworkScreen::Result::CONNECTED:
+      case NetworkScreen::Result::NOT_APPLICABLE:
+        demo_setup_controller_->set_demo_config(
+            DemoSession::DemoModeConfig::kOnline);
+        ShowDemoModePreferencesScreen();
+        break;
+      case NetworkScreen::Result::BACK:
+        demo_setup_controller_.reset();
+        ShowWelcomeScreen();
+        break;
+    }
+    return;
+  }
+
+  // OS Install flow.
+  bool is_consolidated_consent_enabled =
+      chromeos::features::IsOobeConsolidatedConsentEnabled();
+  if (switches::IsOsInstallAllowed()) {
+    switch (result) {
+      case NetworkScreen::Result::CONNECTED:
+      case NetworkScreen::Result::NOT_APPLICABLE:
+        if (is_consolidated_consent_enabled) {
+          MaybeTakeTPMOwnership();
+          PerformPostNetworkScreenActions();
+          InitiateOOBEUpdate();
+        } else {
+          ShowEulaScreen();
+        }
+        break;
+      case NetworkScreen::Result::BACK:
+        ShowOsTrialScreen();
+        break;
+    }
+    return;
+  }
+
+  // Regular flow.
   switch (result) {
-    case NetworkScreen::Result::CONNECTED_REGULAR:
+    case NetworkScreen::Result::CONNECTED:
     case NetworkScreen::Result::NOT_APPLICABLE:
-      DCHECK(!demo_setup_controller_);
-      ShowEulaScreen();
+      if (is_consolidated_consent_enabled) {
+        MaybeTakeTPMOwnership();
+        PerformPostNetworkScreenActions();
+        InitiateOOBEUpdate();
+      } else {
+        ShowEulaScreen();
+      }
       break;
-    case NetworkScreen::Result::CONNECTED_DEMO:
-    case NetworkScreen::Result::NOT_APPLICABLE_CONNECTED_DEMO:
-      DCHECK(demo_setup_controller_);
-      demo_setup_controller_->set_demo_config(
-          DemoSession::DemoModeConfig::kOnline);
-      ShowDemoModePreferencesScreen();
-      break;
-    case NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT:
-    case NetworkScreen::Result::NOT_APPLICABLE_CONSOLIDATED_CONSENT:
-      DCHECK(!demo_setup_controller_);
-      MaybeTakeTPMOwnership();
-      PerformPostEulaActions();
-      InitiateOOBEUpdate();
-      break;
-    case NetworkScreen::Result::BACK_DEMO:
-      DCHECK(demo_setup_controller_);
-      demo_setup_controller_.reset();
+    case NetworkScreen::Result::BACK:
       ShowWelcomeScreen();
       break;
-    case NetworkScreen::Result::BACK_REGULAR:
-      DCHECK(!demo_setup_controller_);
-      ShowWelcomeScreen();
-      break;
-    case NetworkScreen::Result::BACK_OS_INSTALL:
-      DCHECK(!demo_setup_controller_);
-      ShowOsTrialScreen();
-      break;
   }
 }
 
@@ -1502,7 +1527,7 @@
     AdvanceToScreen(HWDataCollectionView::kScreenId);
     return;
   }
-  PerformPostEulaActions();
+  PerformPostNetworkScreenActions();
 
   if (arc::IsArcTermsOfServiceOobeNegotiationNeeded()) {
     ShowArcTermsOfServiceScreen();
@@ -1676,7 +1701,7 @@
       demo_setup_controller_->set_demo_config(
           DemoSession::DemoModeConfig::kOnline);
       MaybeTakeTPMOwnership();
-      PerformPostEulaActions();
+      PerformPostNetworkScreenActions();
       InitiateOOBEUpdate();
       break;
     case DemoPreferencesScreen::Result::CANCELED:
@@ -2023,13 +2048,12 @@
                      weak_factory_.GetWeakPtr()));
 }
 
-void WizardController::PerformPostEulaActions() {
+void WizardController::PerformPostNetworkScreenActions() {
   StartNetworkTimezoneResolve();
   DelayNetworkCall(base::Milliseconds(kDefaultNetworkRetryDelayMS),
                    ServicesCustomizationDocument::GetInstance()
                        ->EnsureCustomizationAppliedClosure());
 
-  // Now that EULA has been accepted (for official builds), enable portal check.
   // ChromiumOS builds would go though this code path too.
   GetAutoEnrollmentController()->Start();
   network_portal_detector::GetInstance()->Enable();
diff --git a/chrome/browser/ash/login/wizard_controller.h b/chrome/browser/ash/login/wizard_controller.h
index 5ba6a871..737ab0e 100644
--- a/chrome/browser/ash/login/wizard_controller.h
+++ b/chrome/browser/ash/login/wizard_controller.h
@@ -397,9 +397,11 @@
   // Retrieve filtered OOBE configuration and apply relevant values.
   void UpdateOobeConfiguration();
 
-  // Actions that should be done right after EULA is accepted,
-  // before update check.
-  void PerformPostEulaActions();
+  // Actions that should be done right after Network Screen if
+  // OobeConsolidatedConsent is enabled, and should be done right after EULA is
+  // accepted if OobeConsolidatedConsent is disabled. These actions should be
+  // done before the update check.
+  void PerformPostNetworkScreenActions();
 
   // Actions that should be done right after update stage is finished.
   void PerformOOBECompletedActions();
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index d5b69b45..15531c5 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -738,12 +738,10 @@
     EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
     if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
       // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-      mock_network_screen_->ExitScreen(
-          NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+      mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
     } else {
       EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-      mock_network_screen_->ExitScreen(
-          NetworkScreen::Result::CONNECTED_REGULAR);
+      mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
       CheckCurrentScreen(EulaView::kScreenId);
       // Login shelf should still be visible.
@@ -855,11 +853,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -899,11 +896,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -948,11 +944,10 @@
         .Times(1);
     EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
     EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -995,7 +990,7 @@
   EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
   EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
   CheckCurrentScreen(EulaView::kScreenId);
   EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
@@ -1104,11 +1099,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
 
@@ -1227,11 +1221,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1267,15 +1260,18 @@
                                             std::move(device_state));
 
   EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
-  EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
+  if (!chromeos::features::IsOobeConsolidatedConsentEnabled())
+    EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
   EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
   EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(1);
   EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
 
   mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::NEXT);
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
-  mock_eula_screen_->ExitScreen(
-      EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
+  if (!chromeos::features::IsOobeConsolidatedConsentEnabled()) {
+    mock_eula_screen_->ExitScreen(
+        EulaScreen::Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING);
+  }
   base::RunLoop().RunUntilIdle();
   mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);
   mock_auto_enrollment_check_screen_->ExitScreen();
@@ -1306,11 +1302,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1409,11 +1404,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1502,11 +1496,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1653,12 +1646,10 @@
 
     if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
       // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-      mock_network_screen_->ExitScreen(
-          NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+      mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
     } else {
       EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-      mock_network_screen_->ExitScreen(
-          NetworkScreen::Result::CONNECTED_REGULAR);
+      mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
       CheckCurrentScreen(EulaView::kScreenId);
       EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1768,11 +1759,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1860,11 +1850,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1905,11 +1894,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -1964,11 +1952,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -2024,11 +2011,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -2308,11 +2294,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -2358,11 +2343,10 @@
 
   if (chromeos::features::IsOobeConsolidatedConsentEnabled()) {
     // EULA screen is skipped when OobeConsolidatedConsent is enabled.
-    mock_network_screen_->ExitScreen(
-        NetworkScreen::Result::CONNECTED_REGULAR_CONSOLIDATED_CONSENT);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
   } else {
     EXPECT_CALL(*mock_eula_screen_, ShowImpl()).Times(1);
-    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_REGULAR);
+    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
     CheckCurrentScreen(EulaView::kScreenId);
     EXPECT_CALL(*mock_eula_screen_, HideImpl()).Times(1);
@@ -2540,7 +2524,7 @@
   EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl()).Times(1);
 
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_DEMO);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
   CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2634,7 +2618,7 @@
   EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl()).Times(1);
 
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_DEMO);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
   CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2736,7 +2720,7 @@
   EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);
 
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::BACK_DEMO);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::BACK);
 
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
 }
@@ -2752,7 +2736,7 @@
   EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);
 
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::BACK_DEMO);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::BACK);
 
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -2867,7 +2851,7 @@
   EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl()).Times(1);
 
-  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED_DEMO);
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
 
   CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
   EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
@@ -3213,7 +3197,7 @@
   ASSERT_TRUE(network_list);
   ASSERT_TRUE(network_list->is_list());
 
-  const base::Value& network = network_list->GetListDeprecated()[0];
+  const base::Value& network = network_list->GetList()[0];
   ASSERT_TRUE(network.is_dict());
 
   const std::string* guid = network.FindStringKey("GUID");
diff --git a/chrome/browser/ash/ownership/owner_key_loader.cc b/chrome/browser/ash/ownership/owner_key_loader.cc
new file mode 100644
index 0000000..471b90d
--- /dev/null
+++ b/chrome/browser/ash/ownership/owner_key_loader.cc
@@ -0,0 +1,308 @@
+// Copyright 2022 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/ash/ownership/owner_key_loader.h"
+
+#include "base/check_is_test.h"
+#include "base/task/thread_pool.h"
+#include "chrome/browser/ash/settings/device_settings_service.h"
+#include "chrome/browser/net/nss_service.h"
+#include "chrome/browser/net/nss_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/ownership/owner_key_util.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/cert/nss_cert_database.h"
+
+namespace ash {
+
+namespace {
+
+// Max number of attempts to generate a new owner key.
+constexpr int kMaxGenerateAttempts = 5;
+
+void LoadPublicKeyOnlyOnWorkerThread(
+    scoped_refptr<ownership::OwnerKeyUtil> owner_key_util,
+    base::OnceCallback<void(scoped_refptr<ownership::PublicKey>)>
+        ui_thread_callback) {
+  scoped_refptr<ownership::PublicKey> public_key =
+      owner_key_util->ImportPublicKey();
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(std::move(ui_thread_callback), public_key));
+}
+
+void LoadPrivateKeyOnWorkerThread(
+    scoped_refptr<ownership::OwnerKeyUtil> owner_key_util,
+    scoped_refptr<ownership::PublicKey> public_key,
+    base::OnceCallback<void(scoped_refptr<ownership::PrivateKey>)>
+        ui_thread_callback,
+    net::NSSCertDatabase* database) {
+  // TODO(davidben): FindPrivateKeyInSlot internally checks for a null slot if
+  // needbe. The null check should be in the caller rather than internally in
+  // the OwnerKeyUtil implementation. The tests currently get a null
+  // private_slot and expect the mock OwnerKeyUtil to still be called.
+  scoped_refptr<ownership::PrivateKey> private_key =
+      base::MakeRefCounted<ownership::PrivateKey>(
+          owner_key_util->FindPrivateKeyInSlot(
+              public_key->data(), database->GetPrivateSlot().get()));
+  if (!private_key->key()) {
+    private_key = base::MakeRefCounted<ownership::PrivateKey>(
+        owner_key_util->FindPrivateKeyInSlot(public_key->data(),
+                                             database->GetPublicSlot().get()));
+  }
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(std::move(ui_thread_callback), private_key));
+}
+
+void GenerateNewOwnerKeyOnWorkerThread(
+    scoped_refptr<ownership::OwnerKeyUtil> owner_key_util,
+    base::OnceCallback<void(scoped_refptr<ownership::PublicKey>,
+                            scoped_refptr<ownership::PrivateKey>)>
+        ui_thread_callback,
+    net::NSSCertDatabase* nss_db) {
+  crypto::ScopedSECKEYPrivateKey sec_priv_key =
+      owner_key_util->GenerateKeyPair(nss_db->GetPublicSlot().get());
+  if (!sec_priv_key) {
+    LOG(ERROR) << "Failed to generete owner key";
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(ui_thread_callback), nullptr, nullptr));
+    return;
+  }
+
+  crypto::ScopedSECKEYPublicKey sec_pub_key(
+      SECKEY_ConvertToPublicKey(sec_priv_key.get()));
+  crypto::ScopedSECItem sec_pub_key_der(
+      SECKEY_EncodeDERSubjectPublicKeyInfo(sec_pub_key.get()));
+  if (!sec_pub_key_der.get()) {
+    LOG(ERROR) << "Failed to extract public key";
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(ui_thread_callback), nullptr, nullptr));
+    return;
+  }
+
+  scoped_refptr<ownership::PublicKey> public_key =
+      base::MakeRefCounted<ownership::PublicKey>(
+          /*is_persisted=*/false,
+          std::vector<uint8_t>(sec_pub_key_der->data,
+                               sec_pub_key_der->data + sec_pub_key_der->len));
+  scoped_refptr<ownership::PrivateKey> private_key =
+      base::MakeRefCounted<ownership::PrivateKey>(std::move(sec_priv_key));
+
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(std::move(ui_thread_callback),
+                                std::move(public_key), std::move(private_key)));
+}
+
+void PostOnWorkerThreadWithCertDb(
+    base::OnceCallback<void(net::NSSCertDatabase*)> worker_task,
+    net::NSSCertDatabase* nss_db) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // TODO(eseckler): It seems loading the key is important for the UsersPrivate
+  // extension API to work correctly during startup, which is why we cannot
+  // currently use the BEST_EFFORT TaskPriority here.
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(std::move(worker_task), nss_db));
+}
+
+void GetCertDbAndPostOnWorkerThreadOnIO(
+    NssCertDatabaseGetter nss_getter,
+    base::OnceCallback<void(net::NSSCertDatabase*)> worker_task) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // Running |nss_getter| may either return a non-null pointer
+  // synchronously or invoke the given callback asynchronously with a non-null
+  // pointer. |callback_split| is used here to handle both cases.
+  auto callback_split = base::SplitOnceCallback(
+      base::BindOnce(&PostOnWorkerThreadWithCertDb, std::move(worker_task)));
+
+  net::NSSCertDatabase* database =
+      std::move(nss_getter).Run(std::move(callback_split.first));
+  if (database)
+    std::move(callback_split.second).Run(database);
+}
+
+void GetCertDbAndPostOnWorkerThread(
+    Profile* profile,
+    base::OnceCallback<void(net::NSSCertDatabase*)> worker_task) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&GetCertDbAndPostOnWorkerThreadOnIO,
+                                NssServiceFactory::GetForContext(profile)
+                                    ->CreateNSSCertDatabaseGetterForIOThread(),
+                                std::move(worker_task)));
+}
+
+}  // namespace
+
+OwnerKeyLoader::OwnerKeyLoader(
+    Profile* profile,
+    DeviceSettingsService* device_settings_service,
+    scoped_refptr<ownership::OwnerKeyUtil> owner_key_util,
+    KeypairCallback callback)
+    : profile_(profile),
+      device_settings_service_(device_settings_service),
+      owner_key_util_(std::move(owner_key_util)),
+      callback_(std::move(callback)) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(profile_);
+  DCHECK(owner_key_util_);
+  DCHECK(callback_);
+  if (!device_settings_service_) {
+    CHECK_IS_TEST();
+  }
+}
+OwnerKeyLoader::~OwnerKeyLoader() = default;
+
+void OwnerKeyLoader::Run() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(callback_) << "Run() can only be called once.";
+
+  if (!device_settings_service_) {
+    CHECK_IS_TEST();
+    std::move(callback_).Run(/*public_key=*/nullptr, /*private_key=*/nullptr);
+    // `this` might be deleted here.
+    return;
+  }
+
+  // device_settings_service_ indicates that the current user should become the
+  // owner, generate a new owner key pair for them.
+  if (device_settings_service_->GetWillEstablishConsumerOwnership()) {
+    LOG(WARNING) << "Establishing consumer ownership.";
+    GetCertDbAndPostOnWorkerThread(
+        profile_,
+        base::BindOnce(&GenerateNewOwnerKeyOnWorkerThread, owner_key_util_,
+                       base::BindOnce(&OwnerKeyLoader::OnNewKeyGenerated,
+                                      weak_factory_.GetWeakPtr())));
+    return;
+  }
+
+  // Otherwise it might be the owner or not, start with loading the public key.
+  base::ThreadPool::PostTask(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&LoadPublicKeyOnlyOnWorkerThread, owner_key_util_,
+                     base::BindOnce(&OwnerKeyLoader::OnPublicKeyLoaded,
+                                    weak_factory_.GetWeakPtr())));
+}
+
+void OwnerKeyLoader::OnPublicKeyLoaded(
+    scoped_refptr<ownership::PublicKey> public_key) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (!public_key || public_key->is_empty()) {
+    // This should not happen. For the very first user that doesn't have the
+    // public key yet, device_settings_service_ should indicate that. For other
+    // users, they should either have the public key or session_manager should
+    // recover it from the policies or session_manager should initiate powerwash
+    // if both policies and the public key were lost.
+    LOG(ERROR) << "Failed to load public key.";
+    std::move(callback_).Run(
+        /*public_key=*/nullptr, /*private_key=*/nullptr);
+    // `this` might be deleted here.
+    return;
+  }
+  public_key_ = public_key;
+
+  // Now check whether the current user has access to the private key associated
+  // with the public key.
+  GetCertDbAndPostOnWorkerThread(
+      profile_, base::BindOnce(
+                    &LoadPrivateKeyOnWorkerThread, owner_key_util_, public_key_,
+                    base::BindOnce(&OwnerKeyLoader::OnPrivateKeyLoaded,
+                                   weak_factory_.GetWeakPtr())));
+}
+
+void OwnerKeyLoader::OnPrivateKeyLoaded(
+    scoped_refptr<ownership::PrivateKey> private_key) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (private_key && private_key->key()) {
+    // Success: both keys were loaded, the current user is the owner.
+    std::move(callback_).Run(public_key_, private_key);
+    // `this` might be deleted here.
+    return;
+  }
+
+  // Private key failed to load. Maybe the current user is not the owner. Or
+  // the private key was lost. Check the policies to make the decision.
+  if (device_settings_service_->policy_data()) {
+    MaybeRegenerateLostKey(device_settings_service_->policy_data());
+    return;
+  }
+  // If policy data is not available yet, try waiting for it. The assumption is
+  // that it can be loaded before this class finishes its work. The public key
+  // is usually required to load the policies, but device_settings_service_ also
+  // independently loads it for itself.
+  device_settings_service_->GetPolicyDataAsync(base::BindOnce(
+      &OwnerKeyLoader::OnPolicyDataReady, weak_factory_.GetWeakPtr()));
+}
+
+void OwnerKeyLoader::OnNewKeyGenerated(
+    scoped_refptr<ownership::PublicKey> public_key,
+    scoped_refptr<ownership::PrivateKey> private_key) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (private_key && private_key->key()) {
+    LOG(WARNING) << "New owner key pair was generated.";
+    std::move(callback_).Run(public_key, private_key);
+    // `this` might be deleted here.
+    return;
+  }
+
+  if (++generate_attempt_counter_ <= kMaxGenerateAttempts) {
+    // Key generation is not expected to fail, but it is too important to simply
+    // give up. Retry up to `kMaxGenerateAttempts` times if needed.
+    GetCertDbAndPostOnWorkerThread(
+        profile_,
+        base::BindOnce(&GenerateNewOwnerKeyOnWorkerThread, owner_key_util_,
+                       base::BindOnce(&OwnerKeyLoader::OnNewKeyGenerated,
+                                      weak_factory_.GetWeakPtr())));
+    return;
+  }
+
+  LOG(ERROR) << "Failed to generate new owner key.";
+  // Return at least the public key, if it was loaded. If Chrome is taking
+  // ownership for the first time, it should be null. If recovering from a lost
+  // private key, it should be not null.
+  std::move(callback_).Run(public_key_, nullptr);
+  // `this` might be deleted here.
+}
+
+// This method is needed just to convert non-const pointer to a const one.
+// OnceCallback struggles to do it itself.
+void OwnerKeyLoader::OnPolicyDataReady(
+    enterprise_management::PolicyData* policy_data) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  MaybeRegenerateLostKey(policy_data);
+}
+
+void OwnerKeyLoader::MaybeRegenerateLostKey(
+    const enterprise_management::PolicyData* policy_data) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // If the policy says that the current user is the owner, generate a new key
+  // pair for them. Also, in theory ChromeOS is allowed to lose the policies and
+  // recover, so be prepared for them to still be empty.
+  if (policy_data && policy_data->has_username() &&
+      (policy_data->username() == profile_->GetProfileUserName())) {
+    LOG(WARNING) << "The owner key was lost. Generating a new one.";
+    GetCertDbAndPostOnWorkerThread(
+        profile_,
+        base::BindOnce(&GenerateNewOwnerKeyOnWorkerThread, owner_key_util_,
+                       base::BindOnce(&OwnerKeyLoader::OnNewKeyGenerated,
+                                      weak_factory_.GetWeakPtr())));
+    return;
+  }
+
+  // The user doesn't seem to be the owner, return just the public key.
+  std::move(callback_).Run(public_key_, /*private_key=*/nullptr);
+  // `this` might be deleted here.
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/ownership/owner_key_loader.h b/chrome/browser/ash/ownership/owner_key_loader.h
new file mode 100644
index 0000000..969ddab
--- /dev/null
+++ b/chrome/browser/ash/ownership/owner_key_loader.h
@@ -0,0 +1,71 @@
+// Copyright 2022 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_ASH_OWNERSHIP_OWNER_KEY_LOADER_H_
+#define CHROME_BROWSER_ASH_OWNERSHIP_OWNER_KEY_LOADER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/ownership/owner_key_util.h"
+
+class Profile;
+
+namespace enterprise_management {
+class PolicyData;
+}
+
+namespace ash {
+
+class DeviceSettingsService;
+
+// A helper single-use class to load the owner key.
+// Determines whether the current user is the owner or not.
+// For the non-owner just loads the public owner key (which can be used to
+// verify signature on the device policies).
+// For the owner loads both public and private key or generates new ones if the
+// previous ones were lost.
+// For the first user that should become the owner generates a new key pair.
+// All public methods might depend on the profile and therefore should be run on
+// the UI thread.
+class OwnerKeyLoader {
+ public:
+  using KeypairCallback = base::OnceCallback<void(
+      scoped_refptr<ownership::PublicKey> public_key,
+      scoped_refptr<ownership::PrivateKey> private_key)>;
+
+  OwnerKeyLoader(Profile* profile,
+                 DeviceSettingsService* device_settings_service,
+                 scoped_refptr<ownership::OwnerKeyUtil> owner_key_util,
+                 KeypairCallback callback);
+  OwnerKeyLoader(const OwnerKeyLoader&) = delete;
+  auto operator=(const OwnerKeyLoader&) = delete;
+  ~OwnerKeyLoader();
+
+  // Starts the loading of the key(s). Can be called only once per instance of
+  // the class.
+  void Run();
+
+ private:
+  void OnPublicKeyLoaded(scoped_refptr<ownership::PublicKey> public_key);
+  void OnPrivateKeyLoaded(scoped_refptr<ownership::PrivateKey> private_key);
+  void OnNewKeyGenerated(scoped_refptr<ownership::PublicKey> public_key,
+                         scoped_refptr<ownership::PrivateKey> private_key);
+  void OnPolicyDataReady(enterprise_management::PolicyData* policy_data);
+  void MaybeRegenerateLostKey(
+      const enterprise_management::PolicyData* policy_data);
+
+  Profile* const profile_;
+  DeviceSettingsService* const device_settings_service_;
+  scoped_refptr<ownership::OwnerKeyUtil> owner_key_util_;
+  scoped_refptr<ownership::PublicKey> public_key_;
+  KeypairCallback callback_;
+  int generate_attempt_counter_ = 0;
+
+  base::WeakPtrFactory<OwnerKeyLoader> weak_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_OWNERSHIP_OWNER_KEY_LOADER_H_
diff --git a/chrome/browser/ash/ownership/owner_key_loader_unittest.cc b/chrome/browser/ash/ownership/owner_key_loader_unittest.cc
new file mode 100644
index 0000000..b1ca912
--- /dev/null
+++ b/chrome/browser/ash/ownership/owner_key_loader_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2022 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/ash/ownership/owner_key_loader.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/test/test_future.h"
+#include "chrome/browser/ash/policy/core/device_policy_builder.h"
+#include "chrome/browser/ash/settings/device_settings_service.h"
+#include "chrome/browser/net/fake_nss_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/ownership/mock_owner_key_util.h"
+#include "content/public/test/browser_task_environment.h"
+#include "crypto/rsa_private_key.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+std::vector<uint8_t> ExtractBytes(
+    const std::unique_ptr<crypto::RSAPrivateKey>& key) {
+  std::vector<uint8_t> bytes;
+  key->ExportPublicKey(&bytes);
+  return bytes;
+}
+
+class OwnerKeyLoaderTest : public testing::Test {
+ public:
+  // testing::Test:
+  void SetUp() override {
+    owner_key_util_ = base::MakeRefCounted<ownership::MockOwnerKeyUtil>();
+
+    device_settings_service_.SetSessionManager(&session_manager_client_,
+                                               owner_key_util_);
+
+    profile_ = TestingProfile::Builder().Build();
+
+    FakeNssService::InitializeForBrowserContext(profile_.get(),
+                                                /*enable_system_slot=*/false);
+
+    key_loader_ = std::make_unique<OwnerKeyLoader>(
+        profile_.get(), &device_settings_service_, owner_key_util_,
+        result_observer_.GetCallback());
+  }
+
+ protected:
+  std::unique_ptr<crypto::RSAPrivateKey> ConfigureExistingPolicies(
+      const std::string& owner_username) {
+    // The actual content of the policies doesn't matter, OwnerKeyLoader only
+    // looks at the username that created them (i.e. at the user that was
+    // recognised as the owner when they were created).
+    policy::DevicePolicyBuilder policy_builder;
+    policy_builder.policy_data().set_username(owner_username);
+    policy_builder.Build();
+    session_manager_client_.set_device_policy(policy_builder.GetBlob());
+    return policy_builder.GetSigningKey();
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+
+  scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_;
+  FakeSessionManagerClient session_manager_client_;
+  std::unique_ptr<TestingProfile> profile_;
+  ash::DeviceSettingsService device_settings_service_;
+  std::unique_ptr<OwnerKeyLoader> key_loader_;
+  base::test::TestFuture<scoped_refptr<ownership::PublicKey>,
+                         scoped_refptr<ownership::PrivateKey>>
+      result_observer_;
+};
+
+// Test that the first user generates a new owner key.
+TEST_F(OwnerKeyLoaderTest, FirstUserGeneratesOwnerKey) {
+  // In real code DeviceSettingsService must call this for the first user.
+  device_settings_service_.MarkWillEstablishConsumerOwnership();
+  // Do not prepare any keys, so key_loader_ has to generate a new one.
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_TRUE(result_observer_.Get<1>()->key());
+}
+
+// Test that the second user doesn't try to generate a new owner key.
+TEST_F(OwnerKeyLoaderTest, SecondUserDoesNotTakeOwnership) {
+  // In real code the first user would have created some device policies and
+  // saved the public owner key on disk. Emulate that.
+  auto signing_key = ConfigureExistingPolicies("owner@example.com");
+  owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key);
+  device_settings_service_.LoadImmediately();  // Reload policies.
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_EQ(result_observer_.Get<0>()->data(), ExtractBytes(signing_key));
+  EXPECT_FALSE(result_observer_.Get<1>());
+}
+
+// Test that an owner user gets recognized as the owner when it's mentioned in
+// the existing device policies and owns the key.
+TEST_F(OwnerKeyLoaderTest, OwnerUserLoadsExistingKey) {
+  // Configure existing device policies and the owner key.
+  auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName());
+  owner_key_util_->ImportPrivateKeyAndSetPublicKey(signing_key->Copy());
+  device_settings_service_.LoadImmediately();  // Reload policies.
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_EQ(result_observer_.Get<0>()->data(), ExtractBytes(signing_key));
+  EXPECT_TRUE(result_observer_.Get<1>()->key());
+}
+
+// Test that even without existing device policies the owner key gets loaded
+// (that will help Chrome to recognize the current user as the owner).
+TEST_F(OwnerKeyLoaderTest, OwnerUserLoadsExistingKeyWithoutPolicies) {
+  policy::DevicePolicyBuilder policy_builder;
+  auto signing_key = policy_builder.GetSigningKey();
+
+  owner_key_util_->ImportPrivateKeyAndSetPublicKey(signing_key->Copy());
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_EQ(result_observer_.Get<0>()->data(), ExtractBytes(signing_key));
+  EXPECT_TRUE(result_observer_.Get<1>()->key());
+}
+
+// Test that the second user is not falsely recognized as the owner even if
+// policies fail to load and does not have the owner key.
+TEST_F(OwnerKeyLoaderTest, SecondaryUserWithoutPolicies) {
+  policy::DevicePolicyBuilder policy_builder;
+  auto signing_key = policy_builder.GetSigningKey();
+
+  owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key);
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_EQ(result_observer_.Get<0>()->data(), ExtractBytes(signing_key));
+  EXPECT_FALSE(result_observer_.Get<1>());
+}
+
+// Test that an owner user still gets recognized as the owner when it's
+// mentioned in the existing device policies, but the owner key was lost.
+// The key must be re-generated in such a case.
+TEST_F(OwnerKeyLoaderTest, OwnerUserRegeneratesMissingKey) {
+  auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName());
+  // Configure that the public key is on disk, but the private key doesn't
+  // exist.
+  owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key);
+  device_settings_service_.LoadImmediately();  // Reload policies.
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_NE(result_observer_.Get<0>()->data(), ExtractBytes(signing_key));
+  EXPECT_TRUE(result_observer_.Get<1>()->key());
+}
+
+// Test that OwnerKeyLoader makes several attempts to generate the owner key
+// pair.
+TEST_F(OwnerKeyLoaderTest, KeyGenerationRetriedSuccessfully) {
+  // Make key_loader_ generate the key for the first user.
+  device_settings_service_.MarkWillEstablishConsumerOwnership();
+  owner_key_util_->SimulateGenerateKeyFailure(/*fail_times=*/5);
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>()->is_empty());
+  EXPECT_TRUE(result_observer_.Get<1>()->key());
+}
+
+// Test that OwnerKeyLoader gives up to generate the owner key pair after a
+// certain amount of attempts.
+TEST_F(OwnerKeyLoaderTest, KeyGenerationRetriedUnsuccessfully) {
+  // Make key_loader_ generate the key for the first user.
+  device_settings_service_.MarkWillEstablishConsumerOwnership();
+  owner_key_util_->SimulateGenerateKeyFailure(/*fail_times=*/10);
+
+  key_loader_->Run();
+
+  EXPECT_FALSE(result_observer_.Get<0>());
+  EXPECT_FALSE(result_observer_.Get<1>());
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/ownership/owner_settings_service_ash.cc b/chrome/browser/ash/ownership/owner_settings_service_ash.cc
index d9080dd..419a192 100644
--- a/chrome/browser/ash/ownership/owner_settings_service_ash.cc
+++ b/chrome/browser/ash/ownership/owner_settings_service_ash.cc
@@ -82,17 +82,14 @@
     crypto::ScopedPK11Slot public_slot,
     crypto::ScopedPK11Slot private_slot,
     ReloadKeyCallback callback) {
-  std::vector<uint8_t> public_key_data;
-  scoped_refptr<PublicKey> public_key;
-  if (!owner_key_util->ImportPublicKey(&public_key_data)) {
+  scoped_refptr<PublicKey> public_key = owner_key_util->ImportPublicKey();
+  if (!public_key) {
     scoped_refptr<PrivateKey> private_key;
     content::GetUIThreadTaskRunner({})->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(callback), public_key, private_key));
     return;
   }
-  public_key = new PublicKey();
-  public_key->data().swap(public_key_data);
 
   // If private slot is already available, this will check it. If not, we'll get
   // called again later when the TPM Token is ready, and the slot will be
@@ -156,11 +153,11 @@
 
 bool DoesPrivateKeyExistAsyncHelper(
     const scoped_refptr<OwnerKeyUtil>& owner_key_util) {
-  std::vector<uint8_t> public_key;
-  if (!owner_key_util->ImportPublicKey(&public_key))
+  scoped_refptr<PublicKey> public_key = owner_key_util->ImportPublicKey();
+  if (!public_key)
     return false;
   crypto::ScopedSECKEYPrivateKey key =
-      crypto::FindNSSKeyFromPublicKeyInfo(public_key);
+      crypto::FindNSSKeyFromPublicKeyInfo(public_key->data());
   return key && SECKEY_GetPrivateKeyType(key.get()) == rsaKey;
 }
 
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_store_ash.cc b/chrome/browser/ash/policy/core/device_cloud_policy_store_ash.cc
index d7a6b27..ba030a8 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_store_ash.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_store_ash.cc
@@ -79,13 +79,13 @@
       device_settings_service_->GetPublicKey());
   if (!install_attributes_->IsCloudManaged() ||
       !device_settings_service_->policy_data() || !public_key.get() ||
-      !public_key->is_loaded()) {
+      public_key->is_empty()) {
     LOG(ERROR) << "Policy store failed, is_cloud_managed: "
                << install_attributes_->IsCloudManaged() << ", policy_data: "
                << (device_settings_service_->policy_data() != nullptr)
                << ", public_key: " << (public_key.get() != nullptr)
                << ", public_key_is_loaded: "
-               << (public_key.get() ? public_key->is_loaded() : false);
+               << (public_key.get() ? !public_key->is_empty() : false);
     status_ = STATUS_BAD_STATE;
     NotifyStoreError();
     return;
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_store.cc b/chrome/browser/ash/policy/core/device_local_account_policy_store.cc
index 9ea313a..313ac4ff 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_store.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_store.cc
@@ -221,9 +221,9 @@
   // updated only after the successful installation of the policy.
   scoped_refptr<ownership::PublicKey> key =
       device_settings_service_->GetPublicKey();
-  if (!key.get() || !key->is_loaded() || !device_policy_data) {
+  if (!key.get() || key->is_empty() || !device_policy_data) {
     LOG(ERROR) << "Failed policy validation, key: " << (key.get() != nullptr)
-               << ", is_loaded: " << (key.get() ? key->is_loaded() : false)
+               << ", is_loaded: " << (key.get() ? !key->is_empty() : false)
                << ", device_policy_data: " << (device_policy_data != nullptr);
     std::move(callback).Run(/*signature_validation_public_key=*/std::string(),
                             /*validator=*/nullptr);
diff --git a/chrome/browser/ash/policy/external_data/handlers/cloud_external_data_policy_handler.cc b/chrome/browser/ash/policy/external_data/handlers/cloud_external_data_policy_handler.cc
index 1b46b2a..2810e1f9 100644
--- a/chrome/browser/ash/policy/external_data/handlers/cloud_external_data_policy_handler.cc
+++ b/chrome/browser/ash/policy/external_data/handlers/cloud_external_data_policy_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/policy/external_data/handlers/cloud_external_data_policy_handler.h"
 
+#include "chrome/browser/browser_process.h"
 #include "components/user_manager/known_user.h"
 
 namespace policy {
@@ -13,8 +14,9 @@
 // static
 AccountId CloudExternalDataPolicyHandler::GetAccountId(
     const std::string& user_id) {
-  return user_manager::known_user::GetAccountId(user_id, std::string() /* id */,
-                                                AccountType::UNKNOWN);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  return known_user.GetAccountId(user_id, std::string() /* id */,
+                                 AccountType::UNKNOWN);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/handlers/system_proxy_handler.cc b/chrome/browser/ash/policy/handlers/system_proxy_handler.cc
index 3dec089e..95b3d2a 100644
--- a/chrome/browser/ash/policy/handlers/system_proxy_handler.cc
+++ b/chrome/browser/ash/policy/handlers/system_proxy_handler.cc
@@ -61,7 +61,7 @@
 
   std::vector<std::string> system_services_auth_schemes;
   if (auth_schemes) {
-    for (const auto& auth_scheme : auth_schemes->GetListDeprecated())
+    for (const auto& auth_scheme : auth_schemes->GetList())
       system_services_auth_schemes.push_back(auth_scheme.GetString());
   }
 
diff --git a/chrome/browser/ash/policy/networking/euicc_status_uploader.cc b/chrome/browser/ash/policy/networking/euicc_status_uploader.cc
index 18cdcf7..6a4ba9ed3 100644
--- a/chrome/browser/ash/policy/networking/euicc_status_uploader.cc
+++ b/chrome/browser/ash/policy/networking/euicc_status_uploader.cc
@@ -128,7 +128,7 @@
   auto* mutable_esim_profiles = upload_request->mutable_esim_profiles();
   for (const auto& esim_profile :
        status.FindListPath(kLastUploadedEuiccStatusESimProfilesKey)
-           ->GetListDeprecated()) {
+           ->GetList()) {
     enterprise_management::ESimProfileInfo esim_profile_info;
     esim_profile_info.set_iccid(*esim_profile.FindStringKey(
         kLastUploadedEuiccStatusESimProfilesIccidKey));
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_logger.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_logger.cc
index e5b4334..fbbd7e95 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_logger.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_logger.cc
@@ -146,7 +146,7 @@
       GetPackagesFromPref(arc::prefs::kArcPushInstallAppsPending);
 
   std::set<std::string> noncompliant_apps_in_report;
-  for (const auto& detail : details->GetListDeprecated()) {
+  for (const auto& detail : details->GetList()) {
     const base::Value* const reason =
         detail.FindKeyOfType("nonComplianceReason", base::Value::Type::INTEGER);
     if (!reason || reason->GetInt() != kNonComplianceReasonAppNotInstalled) {
diff --git a/chrome/browser/ash/settings/about_flags.cc b/chrome/browser/ash/settings/about_flags.cc
index 7f5cfb3..c6fc4cc 100644
--- a/chrome/browser/ash/settings/about_flags.cc
+++ b/chrome/browser/ash/settings/about_flags.cc
@@ -47,7 +47,7 @@
     return flags;
   }
 
-  for (const auto& flag : flags_list.value().GetListDeprecated()) {
+  for (const auto& flag : flags_list.value().GetList()) {
     if (!flag.is_string()) {
       LOG(WARNING) << "Invalid entry in encoded feature flags";
       continue;
diff --git a/chrome/browser/ash/settings/device_settings_service.cc b/chrome/browser/ash/settings/device_settings_service.cc
index 65c84a00..2e82985 100644
--- a/chrome/browser/ash/settings/device_settings_service.cc
+++ b/chrome/browser/ash/settings/device_settings_service.cc
@@ -133,6 +133,17 @@
   }
 }
 
+void DeviceSettingsService::GetPolicyDataAsync(PolicyDataCallback callback) {
+  if (policy_data_) {
+    std::move(callback).Run(policy_data_.get());
+    return;
+  }
+
+  pending_policy_data_callbacks_.push_back(std::move(callback));
+  if (pending_operations_.empty())
+    EnqueueLoad(false);
+}
+
 scoped_refptr<PublicKey> DeviceSettingsService::GetPublicKey() {
   return public_key_;
 }
@@ -172,9 +183,9 @@
 }
 
 DeviceSettingsService::OwnershipStatus
-    DeviceSettingsService::GetOwnershipStatus() {
+DeviceSettingsService::GetOwnershipStatus() {
   if (public_key_.get())
-    return public_key_->is_loaded() ? OWNERSHIP_TAKEN : OWNERSHIP_NONE;
+    return public_key_->is_empty() ? OWNERSHIP_NONE : OWNERSHIP_TAKEN;
   if (device_mode_ == policy::DEVICE_MODE_ENTERPRISE_AD)
     return OWNERSHIP_TAKEN;
   return OWNERSHIP_UNKNOWN;
@@ -308,8 +319,8 @@
 void DeviceSettingsService::StartNextOperation() {
   if (!pending_operations_.empty() && session_manager_client_ &&
       owner_key_util_.get()) {
-    pending_operations_.front()->Start(
-        session_manager_client_, owner_key_util_, public_key_);
+    pending_operations_.front()->Start(session_manager_client_, owner_key_util_,
+                                       public_key_);
   }
 }
 
@@ -360,6 +371,10 @@
   NotifyDeviceSettingsUpdated();
   RunPendingOwnershipStatusCallbacks();
 
+  if ((status == STORE_SUCCESS) || (status == STORE_NO_POLICY)) {
+    RunPendingPolicyDataCallbacks();
+  }
+
   // The completion callback happens after the notification so clients can
   // filter self-triggered updates.
   if (!callback.is_null())
@@ -384,6 +399,14 @@
   }
 }
 
+void DeviceSettingsService::RunPendingPolicyDataCallbacks() {
+  std::vector<PolicyDataCallback> callbacks;
+  callbacks.swap(pending_policy_data_callbacks_);
+  for (auto& callback : callbacks) {
+    std::move(callback).Run(policy_data_.get());
+  }
+}
+
 ScopedTestDeviceSettingsService::ScopedTestDeviceSettingsService() {
   DeviceSettingsService::Initialize();
 }
diff --git a/chrome/browser/ash/settings/device_settings_service.h b/chrome/browser/ash/settings/device_settings_service.h
index 53ab4c2..223e0c1 100644
--- a/chrome/browser/ash/settings/device_settings_service.h
+++ b/chrome/browser/ash/settings/device_settings_service.h
@@ -24,7 +24,7 @@
 namespace ownership {
 class OwnerKeyUtil;
 class PublicKey;
-}
+}  // namespace ownership
 
 namespace policy {
 namespace off_hours {
@@ -60,7 +60,9 @@
     OWNERSHIP_TAKEN
   };
 
-  typedef base::OnceCallback<void(OwnershipStatus)> OwnershipStatusCallback;
+  using OwnershipStatusCallback = base::OnceCallback<void(OwnershipStatus)>;
+  using PolicyDataCallback =
+      base::OnceCallback<void(enterprise_management::PolicyData*)>;
 
   // Status codes for Load() and Store().
   // These values are logged to UMA. Entries should not be renumbered and
@@ -127,6 +129,13 @@
     return policy_data_.get();
   }
 
+  // Asynchronously return policy data. If Chrome doesn't have any policy data
+  // yet, it will enqueue a new policy load. The callback will be called on
+  // success or if policy loaded successfully, but it it was empty (i.e.
+  // STORE_NO_POLICY). If failed to load, the callback is expected to be called
+  // some time later on the next successful load (requested by something else).
+  void GetPolicyDataAsync(PolicyDataCallback callback);
+
   const enterprise_management::PolicyFetchResponse* policy_fetch_response()
       const {
     return policy_fetch_response_.get();
@@ -134,8 +143,8 @@
 
   // Returns the currently active device settings. Returns nullptr if the device
   // settings have not been retrieved from session_manager yet.
-  const enterprise_management::ChromeDeviceSettingsProto*
-      device_settings() const {
+  const enterprise_management::ChromeDeviceSettingsProto* device_settings()
+      const {
     return device_settings_.get();
   }
 
@@ -209,6 +218,12 @@
   // of owner is fully loaded.
   void MarkWillEstablishConsumerOwnership();
 
+  // Returns whether the current user should take ownership of the device
+  // (effectively whether the user is the first consumer user on the device).
+  bool GetWillEstablishConsumerOwnership() const {
+    return will_establish_consumer_ownership_;
+  }
+
   // Adds an observer.
   void AddObserver(Observer* observer);
   // Removes an observer.
@@ -259,12 +274,16 @@
   // Processes pending callbacks from GetOwnershipStatusAsync().
   void RunPendingOwnershipStatusCallbacks();
 
+  // Processes pending callbacks from GetPolicyDataAsync().
+  void RunPendingPolicyDataCallbacks();
+
   SessionManagerClient* session_manager_client_ = nullptr;
   scoped_refptr<ownership::OwnerKeyUtil> owner_key_util_;
 
   Status store_status_ = STORE_SUCCESS;
 
   std::vector<OwnershipStatusCallback> pending_ownership_status_callbacks_;
+  std::vector<PolicyDataCallback> pending_policy_data_callbacks_;
 
   std::string username_;
   scoped_refptr<ownership::PublicKey> public_key_;
diff --git a/chrome/browser/ash/settings/device_settings_service_unittest.cc b/chrome/browser/ash/settings/device_settings_service_unittest.cc
index 4a6fd73..5b155080 100644
--- a/chrome/browser/ash/settings/device_settings_service_unittest.cc
+++ b/chrome/browser/ash/settings/device_settings_service_unittest.cc
@@ -225,7 +225,7 @@
   FlushDeviceSettings();
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  EXPECT_FALSE(device_settings_service_->GetPublicKey()->is_loaded());
+  EXPECT_TRUE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_NONE,
             device_settings_service_->GetOwnershipStatus());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_NONE, ownership_status_);
@@ -237,7 +237,7 @@
   FlushDeviceSettings();
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -253,7 +253,7 @@
   FlushDeviceSettings();
   EXPECT_TRUE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -290,7 +290,7 @@
 
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -302,7 +302,7 @@
 
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
@@ -330,7 +330,7 @@
 
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -343,7 +343,7 @@
 
   EXPECT_TRUE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
 }
@@ -368,7 +368,7 @@
 
   EXPECT_FALSE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -382,7 +382,7 @@
 
   EXPECT_TRUE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -410,7 +410,7 @@
 
   EXPECT_TRUE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -427,7 +427,7 @@
 
   EXPECT_TRUE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
@@ -512,7 +512,7 @@
   // Verify owner key is loaded and ownership status is updated.
   EXPECT_TRUE(device_settings_service_->HasPrivateOwnerKey());
   ASSERT_TRUE(device_settings_service_->GetPublicKey().get());
-  ASSERT_TRUE(device_settings_service_->GetPublicKey()->is_loaded());
+  ASSERT_FALSE(device_settings_service_->GetPublicKey()->is_empty());
   EXPECT_EQ(device_policy_->GetPublicSigningKeyAsString(),
             device_settings_service_->GetPublicKey()->as_string());
   EXPECT_EQ(DeviceSettingsService::OWNERSHIP_TAKEN,
diff --git a/chrome/browser/ash/settings/session_manager_operation.cc b/chrome/browser/ash/settings/session_manager_operation.cc
index 076b01c..5f4364e 100644
--- a/chrome/browser/ash/settings/session_manager_operation.cc
+++ b/chrome/browser/ash/settings/session_manager_operation.cc
@@ -89,7 +89,7 @@
 }
 
 void SessionManagerOperation::EnsurePublicKey(base::OnceClosure callback) {
-  if (force_key_load_ || !public_key_ || !public_key_->is_loaded()) {
+  if (force_key_load_ || !public_key_ || public_key_->is_empty()) {
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE,
         {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
@@ -107,14 +107,18 @@
 scoped_refptr<PublicKey> SessionManagerOperation::LoadPublicKey(
     scoped_refptr<OwnerKeyUtil> util,
     scoped_refptr<PublicKey> current_key) {
-  scoped_refptr<PublicKey> public_key(new PublicKey());
-
   // Keep already-existing public key.
-  if (current_key && current_key->is_loaded()) {
-    public_key->data() = current_key->data();
+  if (current_key && !current_key->is_empty()) {
+    return current_key->clone();
   }
-  if (!public_key->is_loaded() && util->IsPublicKeyPresent()) {
-    if (!util->ImportPublicKey(&public_key->data()))
+
+  scoped_refptr<PublicKey> public_key =
+      base::MakeRefCounted<ownership::PublicKey>(
+          /*is_persisted=*/false, /*data=*/std::vector<uint8_t>());
+
+  if (util->IsPublicKeyPresent()) {
+    public_key = util->ImportPublicKey();
+    if (!public_key || public_key->is_empty())
       LOG(ERROR) << "Failed to load public owner key.";
   }
 
@@ -126,7 +130,7 @@
   force_key_load_ = false;
   public_key_ = new_key;
 
-  if (!public_key_ || !public_key_->is_loaded()) {
+  if (!public_key_ || public_key_->is_empty()) {
     ReportResult(DeviceSettingsService::STORE_KEY_UNAVAILABLE);
     return;
   }
diff --git a/chrome/browser/ash/settings/session_manager_operation_unittest.cc b/chrome/browser/ash/settings/session_manager_operation_unittest.cc
index ef6bf2e..ad0d18f9 100644
--- a/chrome/browser/ash/settings/session_manager_operation_unittest.cc
+++ b/chrome/browser/ash/settings/session_manager_operation_unittest.cc
@@ -106,7 +106,7 @@
 
   void CheckPublicKeyLoaded(SessionManagerOperation* op) {
     ASSERT_TRUE(op->public_key().get());
-    ASSERT_TRUE(op->public_key()->is_loaded());
+    ASSERT_FALSE(op->public_key()->is_empty());
     std::vector<uint8_t> public_key;
     ASSERT_TRUE(policy_.GetSigningKey()->ExportPublicKey(&public_key));
     EXPECT_EQ(public_key, op->public_key()->data());
@@ -144,7 +144,7 @@
   EXPECT_FALSE(op.policy_data().get());
   EXPECT_FALSE(op.device_settings().get());
   ASSERT_TRUE(op.public_key().get());
-  EXPECT_FALSE(op.public_key()->is_loaded());
+  EXPECT_TRUE(op.public_key()->is_empty());
 }
 
 TEST_F(SessionManagerOperationTest, LoadOwnerKey) {
@@ -233,7 +233,7 @@
         // Verify the public_key() is properly set, but the callback is
         // not yet called.
         EXPECT_TRUE(op->public_key().get());
-        EXPECT_TRUE(op->public_key()->is_loaded());
+        EXPECT_FALSE(op->public_key()->is_empty());
         Mock::VerifyAndClearExpectations(test);
 
         // Now install a different key and policy.
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc
index 8e2b375f..3db2e6de 100644
--- a/chrome/browser/autofill/autofill_browsertest.cc
+++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -912,8 +912,8 @@
   };
 
   AutofillTestFormSubmission() {
-    std::vector<base::Feature> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> enabled;
+    std::vector<base::test::FeatureRef> disabled;
     if (std::get<0>(GetParam())) {
       enabled.push_back(features::kAutofillAllowDuplicateFormSubmissions);
     } else {
diff --git a/chrome/browser/browsing_data/access_context_audit_service_unittest.cc b/chrome/browser/browsing_data/access_context_audit_service_unittest.cc
index cba58b5..5fb3e6c 100644
--- a/chrome/browser/browsing_data/access_context_audit_service_unittest.cc
+++ b/chrome/browser/browsing_data/access_context_audit_service_unittest.cc
@@ -172,10 +172,10 @@
   scoped_refptr<base::UpdateableSequencedTaskRunner> task_runner_;
   std::vector<AccessContextAuditDatabase::AccessRecord> records_;
 
-  virtual std::vector<base::Feature> enabled_features() {
+  virtual std::vector<base::test::FeatureRef> enabled_features() {
     return {features::kClientStorageAccessContextAuditing};
   }
-  virtual std::vector<base::Feature> disabled_features() {
+  virtual std::vector<base::test::FeatureRef> disabled_features() {
     return {browsing_data::features::kEnableRemovingAllThirdPartyCookies};
   }
 };
@@ -878,11 +878,13 @@
     EXPECT_TRUE(records[0].top_frame_origin.opaque());
   }
 
-  std::vector<base::Feature> enabled_features() override {
+  std::vector<base::test::FeatureRef> enabled_features() override {
     return {features::kClientStorageAccessContextAuditing,
             browsing_data::features::kEnableRemovingAllThirdPartyCookies};
   }
-  std::vector<base::Feature> disabled_features() override { return {}; }
+  std::vector<base::test::FeatureRef> disabled_features() override {
+    return {};
+  }
 };
 
 // Test that when we enable user controls to clear third-party data, we do not
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 399975e..c5ca255 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -117,7 +117,7 @@
     : public BrowsingDataRemoverBrowserTestBase {
  public:
   BrowsingDataRemoverBrowserTest() {
-    std::vector<base::Feature> enabled_features = {};
+    std::vector<base::test::FeatureRef> enabled_features = {};
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
     enabled_features.push_back(media::kExternalClearKeyForTesting);
 #endif
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
index aa80907..4676d05 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
@@ -139,7 +139,7 @@
     default;
 
 void BrowsingDataRemoverBrowserTestBase::InitFeatureList(
-    std::vector<base::Feature> enabled_features) {
+    std::vector<base::test::FeatureRef> enabled_features) {
   feature_list_.InitWithFeatures(enabled_features, {});
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h
index 42798619..52a16ac2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h
@@ -18,7 +18,7 @@
   BrowsingDataRemoverBrowserTestBase();
   ~BrowsingDataRemoverBrowserTestBase() override;
 
-  void InitFeatureList(std::vector<base::Feature> enabled_features);
+  void InitFeatureList(std::vector<base::test::FeatureRef> enabled_features);
 
   void SetUpOnMainThread() override;
   // If `web_contents` is not specified, `GetActiveWebContents` will be used.
diff --git a/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc b/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc
index 83e985bf..fc0f0f38 100644
--- a/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc
+++ b/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc
@@ -64,7 +64,7 @@
     : public BrowsingDataRemoverBrowserTestBase {
  public:
   IncognitoBrowsingDataBrowserTest() {
-    std::vector<base::Feature> enabled_features = {};
+    std::vector<base::test::FeatureRef> enabled_features = {};
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
     enabled_features.push_back(media::kExternalClearKeyForTesting);
 #endif
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 099c082..879988f 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1634,6 +1634,7 @@
     "../ash/notifications/request_system_proxy_credentials_view_unittest.cc",
     "../ash/notifications/update_required_notification_unittest.cc",
     "../ash/os_feedback/os_feedback_screenshot_manager_unittest.cc",
+    "../ash/ownership/owner_key_loader_unittest.cc",
     "../ash/ownership/owner_settings_service_ash_unittest.cc",
     "../ash/pcie_peripheral/ash_usb_detector_unittest.cc",
     "../ash/phonehub/browser_tabs_metadata_fetcher_impl_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
index 44f04f2..7ddf52f02 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
@@ -17,6 +17,7 @@
 #include "base/feature_list.h"
 #include "chrome/browser/ash/drive/file_system_util.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
+#include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/filesystem_api_util.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
@@ -45,14 +46,14 @@
 
 // Make a set of unique filename suffixes out of the list of file URLs.
 std::set<std::string> GetUniqueSuffixes(
-    const std::vector<std::string>& url_list,
+    const std::vector<std::string>& file_urls,
     const storage::FileSystemContext* context) {
   std::set<std::string> suffixes;
-  for (size_t i = 0; i < url_list.size(); ++i) {
+  for (const auto& file_url : file_urls) {
     const FileSystemURL url =
-        context->CrackURLInFirstPartyContext(GURL(url_list[i]));
+        context->CrackURLInFirstPartyContext(GURL{file_url});
     if (!url.is_valid() || url.path().empty())
-      return std::set<std::string>();
+      return {};
     // We'll skip empty suffixes.
     if (!url.path().Extension().empty())
       suffixes.insert(url.path().Extension());
@@ -64,8 +65,7 @@
 std::set<std::string> GetUniqueMimeTypes(
     const std::vector<std::string>& mime_type_list) {
   std::set<std::string> mime_types;
-  for (size_t i = 0; i < mime_type_list.size(); ++i) {
-    const std::string mime_type = mime_type_list[i];
+  for (const auto& mime_type : mime_type_list) {
     // We'll skip empty MIME types and existing MIME types.
     if (!mime_type.empty())
       mime_types.insert(mime_type);
@@ -105,9 +105,9 @@
           profile, render_frame_host());
 
   std::vector<FileSystemURL> urls;
-  for (size_t i = 0; i < params->urls.size(); i++) {
+  for (const auto& url_param : params->urls) {
     const FileSystemURL url =
-        file_system_context->CrackURLInFirstPartyContext(GURL(params->urls[i]));
+        file_system_context->CrackURLInFirstPartyContext(GURL{url_param});
     if (!chromeos::FileSystemBackend::CanHandleURL(url)) {
       return RespondNow(Error(kInvalidFileUrl));
     }
@@ -159,8 +159,8 @@
 
   // Collect all the URLs, convert them to GURLs, and crack all the urls into
   // file paths.
-  for (size_t i = 0; i < params->urls.size(); ++i) {
-    const GURL url(params->urls[i]);
+  for (const auto& url_param : params->urls) {
+    const GURL url{url_param};
     storage::FileSystemURL file_system_url(
         file_system_context->CrackURLInFirstPartyContext(url));
     if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
@@ -211,12 +211,11 @@
 }
 
 void FileManagerPrivateInternalGetFileTasksFunction::OnFileTasksListed(
-    std::unique_ptr<std::vector<file_manager::file_tasks::FullTaskDescriptor>>
-        tasks) {
+    std::unique_ptr<file_manager::file_tasks::ResultingTasks> resulting_tasks) {
   // Convert the tasks into JSON compatible objects.
   using api::file_manager_private::FileTask;
   std::vector<FileTask> results;
-  for (const file_manager::file_tasks::FullTaskDescriptor& task : *tasks) {
+  for (const auto& task : resulting_tasks->tasks) {
     FileTask converted;
     converted.descriptor.app_id = task.task_descriptor.app_id;
     converted.descriptor.task_type =
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
index 779bb4d..78e3d823 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
@@ -70,8 +70,8 @@
       std::unique_ptr<std::set<base::FilePath>> path_directory_set);
 
   void OnFileTasksListed(
-      std::unique_ptr<std::vector<file_manager::file_tasks::FullTaskDescriptor>>
-          tasks);
+      std::unique_ptr<file_manager::file_tasks::ResultingTasks>
+          resulting_tasks);
 
   std::unique_ptr<app_file_handler_util::IsDirectoryCollector>
       is_directory_collector_;
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc b/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
index b697aac06..35d1dd4 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
@@ -110,6 +110,14 @@
         );
         chrome.test.succeed();
       },
+      async function getTpmInfo() {
+        await chrome.test.assertPromiseRejects(
+            chrome.os.telemetry.getTpmInfo(),
+            'Error: Unauthorized access to chrome.os.telemetry.getTpmInfo. ' +
+            '%s'
+        );
+        chrome.test.succeed();
+      },
       async function getVpdInfo() {
         await chrome.test.assertPromiseRejects(
             chrome.os.telemetry.getVpdInfo(),
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc
index bbacad91..8d52a0f 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.cc
@@ -339,6 +339,32 @@
       api::os_telemetry::GetStatefulPartitionInfo::Results::Create(result)));
 }
 
+// OsTelemetryGetTpmInfoFunction -----------------------------------------------
+
+OsTelemetryGetTpmInfoFunction::OsTelemetryGetTpmInfoFunction() = default;
+OsTelemetryGetTpmInfoFunction::~OsTelemetryGetTpmInfoFunction() = default;
+
+void OsTelemetryGetTpmInfoFunction::RunIfAllowed() {
+  auto cb = base::BindOnce(&OsTelemetryGetTpmInfoFunction::OnResult, this);
+
+  GetRemoteService()->ProbeTelemetryInfo(
+      {crosapi::mojom::ProbeCategoryEnum::kTpm}, std::move(cb));
+}
+
+void OsTelemetryGetTpmInfoFunction::OnResult(
+    crosapi::mojom::ProbeTelemetryInfoPtr ptr) {
+  if (!ptr || !ptr->tpm_result || !ptr->tpm_result->is_tpm_info()) {
+    Respond(Error("API internal error"));
+    return;
+  }
+  auto& tpm_info = ptr->tpm_result->get_tpm_info();
+
+  api::os_telemetry::TpmInfo result =
+      converters::ConvertPtr<api::os_telemetry::TpmInfo>(std::move(tpm_info));
+
+  Respond(ArgumentList(api::os_telemetry::GetTpmInfo::Results::Create(result)));
+}
+
 // OsTelemetryGetVpdInfoFunction -----------------------------------------------
 
 OsTelemetryGetVpdInfoFunction::OsTelemetryGetVpdInfoFunction() = default;
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.h b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.h
index ffe5731..a8f4e154 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.h
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api.h
@@ -214,6 +214,24 @@
   void OnResult(crosapi::mojom::ProbeTelemetryInfoPtr ptr);
 };
 
+class OsTelemetryGetTpmInfoFunction : public TelemetryApiFunctionBase {
+ public:
+  DECLARE_EXTENSION_FUNCTION("os.telemetry.getTpmInfo", OS_TELEMETRY_GETTPMINFO)
+
+  OsTelemetryGetTpmInfoFunction();
+  OsTelemetryGetTpmInfoFunction(const OsTelemetryGetTpmInfoFunction&) = delete;
+  OsTelemetryGetTpmInfoFunction& operator=(
+      const OsTelemetryGetTpmInfoFunction&) = delete;
+
+ private:
+  ~OsTelemetryGetTpmInfoFunction() override;
+
+  // BaseTelemetryExtensionApiGuardFunction:
+  void RunIfAllowed() override;
+
+  void OnResult(crosapi::mojom::ProbeTelemetryInfoPtr ptr);
+};
+
 }  // namespace chromeos
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_TELEMETRY_API_TELEMETRY_API_H_
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_browsertest.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_browsertest.cc
index 7641a77..1f59250 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_browsertest.cc
@@ -160,6 +160,14 @@
         );
         chrome.test.succeed();
       },
+      async function getTpmInfo() {
+        await chrome.test.assertPromiseRejects(
+            chrome.os.telemetry.getTpmInfo(),
+            'Error: API chrome.os.telemetry.getTpmInfo failed. ' +
+            'Not supported by ash browser'
+        );
+        chrome.test.succeed();
+      },
       async function getVpdInfo() {
         await chrome.test.assertPromiseRejects(
             chrome.os.telemetry.getVpdInfo(),
@@ -1078,6 +1086,127 @@
   )");
 }
 
+IN_PROC_BROWSER_TEST_F(TelemetryExtensionTelemetryApiBrowserTest,
+                       GetTpmInfo_Error) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // If Probe interface is not available on this version of ash-chrome, this
+  // test suite will no-op.
+  if (!IsServiceAvailable()) {
+    return;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  // Configure FakeProbeService.
+  {
+    auto fake_service_impl = std::make_unique<FakeProbeService>();
+    fake_service_impl->SetExpectedLastRequestedCategories(
+        {crosapi::mojom::ProbeCategoryEnum::kTpm});
+
+    SetServiceForTesting(std::move(fake_service_impl));
+  }
+
+  CreateExtensionAndRunServiceWorker(R"(
+    chrome.test.runTests([
+      async function getTpmInfo() {
+        await chrome.test.assertPromiseRejects(
+            chrome.os.telemetry.getTpmInfo(),
+            'Error: API internal error'
+        );
+        chrome.test.succeed();
+      }
+    ]);
+  )");
+}
+
+IN_PROC_BROWSER_TEST_F(TelemetryExtensionTelemetryApiBrowserTest,
+                       GetTpmInfo_Success) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // If Probe interface is not available on this version of ash-chrome, this
+  // test suite will no-op.
+  if (!IsServiceAvailable()) {
+    return;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  // Configure FakeProbeService.
+  {
+    auto telemetry_info = crosapi::mojom::ProbeTelemetryInfo::New();
+    {
+      auto tpm_version = crosapi::mojom::ProbeTpmVersion::New();
+      tpm_version->gsc_version = crosapi::mojom::ProbeTpmGSCVersion::kCr50;
+      tpm_version->family = crosapi::mojom::UInt32Value::New(120);
+      tpm_version->spec_level = crosapi::mojom::UInt64Value::New(1000);
+      tpm_version->manufacturer = crosapi::mojom::UInt32Value::New(42);
+      tpm_version->tpm_model = crosapi::mojom::UInt32Value::New(333);
+      tpm_version->firmware_version = crosapi::mojom::UInt64Value::New(10000);
+      tpm_version->vendor_specific = "VendorSpecific";
+
+      auto tpm_status = crosapi::mojom::ProbeTpmStatus::New();
+      tpm_status->enabled = crosapi::mojom::BoolValue::New(true);
+      tpm_status->owned = crosapi::mojom::BoolValue::New(false);
+      tpm_status->owner_password_is_present =
+          crosapi::mojom::BoolValue::New(false);
+
+      auto dictonary_attack = crosapi::mojom::ProbeTpmDictionaryAttack::New();
+      dictonary_attack->counter = crosapi::mojom::UInt32Value::New(5);
+      dictonary_attack->threshold = crosapi::mojom::UInt32Value::New(1000);
+      dictonary_attack->lockout_in_effect =
+          crosapi::mojom::BoolValue::New(false);
+      dictonary_attack->lockout_seconds_remaining =
+          crosapi::mojom::UInt32Value::New(0);
+
+      auto tpm_info = crosapi::mojom::ProbeTpmInfo::New();
+      tpm_info->version = std::move(tpm_version);
+      tpm_info->status = std::move(tpm_status);
+      tpm_info->dictionary_attack = std::move(dictonary_attack);
+
+      telemetry_info->tpm_result =
+          crosapi::mojom::ProbeTpmResult::NewTpmInfo(std::move(tpm_info));
+    }
+
+    auto fake_service_impl = std::make_unique<FakeProbeService>();
+    fake_service_impl->SetProbeTelemetryInfoResponse(std::move(telemetry_info));
+    fake_service_impl->SetExpectedLastRequestedCategories(
+        {crosapi::mojom::ProbeCategoryEnum::kTpm});
+
+    SetServiceForTesting(std::move(fake_service_impl));
+  }
+
+  CreateExtensionAndRunServiceWorker(R"(
+    chrome.test.runTests([
+      async function getTpmInfo() {
+        const result = await chrome.os.telemetry.getTpmInfo();
+        chrome.test.assertEq(
+          // The dictionary members are ordered lexicographically by the Unicode
+          // codepoints that comprise their identifiers.
+          {
+            version: {
+              gscVersion: "cr50",
+              family: 120,
+              specLevel: 1000,
+              manufacturer: 42,
+              tpmModel: 333,
+              firmwareVersion: 10000,
+              vendorSpecific: "VendorSpecific",
+            },
+            status: {
+              enabled: true,
+              owned: false,
+              ownerPasswordIsPresent: false,
+            },
+            dictionaryAttack: {
+              counter: 5,
+              threshold: 1000,
+              lockoutInEffect: false,
+              lockoutSecondsRemaining: 0,
+            },
+          }, result);
+        chrome.test.succeed();
+      }
+    ]);
+  )");
+}
+
 class TelemetryExtensionTelemetryApiWithoutSerialNumberBrowserTest
     : public TelemetryExtensionTelemetryApiBrowserTest {
  public:
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc
index b85137fb..3f935fb 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.cc
@@ -163,6 +163,88 @@
   return result;
 }
 
+telemetry_api::TpmVersion UncheckedConvertPtr(
+    telemetry_service::ProbeTpmVersionPtr input) {
+  telemetry_api::TpmVersion result;
+
+  result.gsc_version = Convert(input->gsc_version);
+  if (input->family) {
+    result.family = input->family->value;
+  }
+  if (input->spec_level) {
+    result.spec_level = input->spec_level->value;
+  }
+  if (input->manufacturer) {
+    result.manufacturer = input->manufacturer->value;
+  }
+  if (input->tpm_model) {
+    result.tpm_model = input->tpm_model->value;
+  }
+  if (input->firmware_version) {
+    result.firmware_version = input->firmware_version->value;
+  }
+  result.vendor_specific = input->vendor_specific;
+
+  return result;
+}
+
+telemetry_api::TpmStatus UncheckedConvertPtr(
+    telemetry_service::ProbeTpmStatusPtr input) {
+  telemetry_api::TpmStatus result;
+
+  if (input->enabled) {
+    result.enabled = input->enabled->value;
+  }
+  if (input->owned) {
+    result.owned = input->owned->value;
+  }
+  if (input->owner_password_is_present) {
+    result.owner_password_is_present = input->owner_password_is_present->value;
+  }
+
+  return result;
+}
+
+telemetry_api::TpmDictionaryAttack UncheckedConvertPtr(
+    telemetry_service::ProbeTpmDictionaryAttackPtr input) {
+  telemetry_api::TpmDictionaryAttack result;
+
+  if (input->counter) {
+    result.counter = input->counter->value;
+  }
+  if (input->threshold) {
+    result.threshold = input->threshold->value;
+  }
+  if (input->lockout_in_effect) {
+    result.lockout_in_effect = input->lockout_in_effect->value;
+  }
+  if (input->lockout_seconds_remaining) {
+    result.lockout_seconds_remaining = input->lockout_seconds_remaining->value;
+  }
+
+  return result;
+}
+
+telemetry_api::TpmInfo UncheckedConvertPtr(
+    telemetry_service::ProbeTpmInfoPtr input) {
+  telemetry_api::TpmInfo result;
+
+  if (input->version) {
+    result.version =
+        ConvertPtr<telemetry_api::TpmVersion>(std::move(input->version));
+  }
+  if (input->status) {
+    result.status =
+        ConvertPtr<telemetry_api::TpmStatus>(std::move(input->status));
+  }
+  if (input->dictionary_attack) {
+    result.dictionary_attack = ConvertPtr<telemetry_api::TpmDictionaryAttack>(
+        std::move(input->dictionary_attack));
+  }
+
+  return result;
+}
+
 }  // namespace unchecked
 
 telemetry_api::CpuArchitectureEnum Convert(
@@ -230,5 +312,18 @@
   NOTREACHED();
 }
 
+chromeos::api::os_telemetry::TpmGSCVersion Convert(
+    crosapi::mojom::ProbeTpmGSCVersion input) {
+  switch (input) {
+    case telemetry_service::ProbeTpmGSCVersion::kNotGSC:
+      return telemetry_api::TpmGSCVersion::TPM_GSC_VERSION_NOT_GSC;
+    case telemetry_service::ProbeTpmGSCVersion::kCr50:
+      return telemetry_api::TpmGSCVersion::TPM_GSC_VERSION_CR50;
+    case telemetry_service::ProbeTpmGSCVersion::kTi50:
+      return telemetry_api::TpmGSCVersion::TPM_GSC_VERSION_TI50;
+  }
+  NOTREACHED();
+}
+
 }  // namespace converters
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.h b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.h
index c188b76f..89f0bf4 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.h
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters.h
@@ -41,6 +41,9 @@
 chromeos::api::os_telemetry::BatteryInfo UncheckedConvertPtr(
     crosapi::mojom::ProbeBatteryInfoPtr input);
 
+chromeos::api::os_telemetry::NetworkInfo UncheckedConvertPtr(
+    chromeos::network_health::mojom::NetworkPtr input);
+
 chromeos::api::os_telemetry::NonRemovableBlockDeviceInfo UncheckedConvertPtr(
     crosapi::mojom::ProbeNonRemovableBlockDeviceInfoPtr);
 
@@ -50,8 +53,17 @@
 chromeos::api::os_telemetry::StatefulPartitionInfo UncheckedConvertPtr(
     crosapi::mojom::ProbeStatefulPartitionInfoPtr input);
 
-chromeos::api::os_telemetry::NetworkInfo UncheckedConvertPtr(
-    chromeos::network_health::mojom::NetworkPtr input);
+chromeos::api::os_telemetry::TpmVersion UncheckedConvertPtr(
+    crosapi::mojom::ProbeTpmVersionPtr input);
+
+chromeos::api::os_telemetry::TpmStatus UncheckedConvertPtr(
+    crosapi::mojom::ProbeTpmStatusPtr input);
+
+chromeos::api::os_telemetry::TpmDictionaryAttack UncheckedConvertPtr(
+    crosapi::mojom::ProbeTpmDictionaryAttackPtr input);
+
+chromeos::api::os_telemetry::TpmInfo UncheckedConvertPtr(
+    crosapi::mojom::ProbeTpmInfoPtr input);
 
 }  // namespace unchecked
 
@@ -64,6 +76,9 @@
 chromeos::api::os_telemetry::NetworkType Convert(
     chromeos::network_config::mojom::NetworkType input);
 
+chromeos::api::os_telemetry::TpmGSCVersion Convert(
+    crosapi::mojom::ProbeTpmGSCVersion input);
+
 template <class OutputT, class InputT>
 std::vector<OutputT> ConvertPtrVector(std::vector<InputT> input) {
   std::vector<OutputT> output;
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
index 4fed4107..1cf030d9 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/telemetry_api_converters_unittest.cc
@@ -406,5 +406,179 @@
   EXPECT_EQ(static_cast<double_t>(*result.signal_strength), kSignalStrength);
 }
 
+TEST(TelemetryApiConverters, TpmVersion) {
+  constexpr uint32_t kFamily = 0x322e3000;
+  constexpr uint64_t kSpecLevel = 1000;
+  constexpr uint32_t kManufacturer = 42;
+  constexpr uint32_t kTpmModel = 101;
+  constexpr uint64_t kFirmwareVersion = 1001;
+  constexpr char kVendorSpecific[] = "info";
+
+  auto input = telemetry_service::ProbeTpmVersion::New();
+  input->gsc_version = telemetry_service::ProbeTpmGSCVersion::kCr50;
+  input->family = telemetry_service::UInt32Value::New(kFamily);
+  input->spec_level = telemetry_service::UInt64Value::New(kSpecLevel);
+  input->manufacturer = telemetry_service::UInt32Value::New(kManufacturer);
+  input->tpm_model = telemetry_service::UInt32Value::New(kTpmModel);
+  input->firmware_version =
+      telemetry_service::UInt64Value::New(kFirmwareVersion);
+  input->vendor_specific = kVendorSpecific;
+
+  auto result = ConvertPtr<telemetry_api::TpmVersion>(std::move(input));
+  EXPECT_EQ(telemetry_api::TpmGSCVersion::TPM_GSC_VERSION_CR50,
+            result.gsc_version);
+  ASSERT_TRUE(result.family);
+  EXPECT_EQ(kFamily, static_cast<uint32_t>(*result.family));
+  ASSERT_TRUE(result.spec_level);
+  EXPECT_EQ(kSpecLevel, static_cast<uint64_t>(*result.spec_level));
+  ASSERT_TRUE(result.manufacturer);
+  EXPECT_EQ(kManufacturer, static_cast<uint32_t>(*result.manufacturer));
+  ASSERT_TRUE(result.tpm_model);
+  EXPECT_EQ(kTpmModel, static_cast<uint32_t>(*result.tpm_model));
+  ASSERT_TRUE(result.firmware_version);
+  EXPECT_EQ(kFirmwareVersion, static_cast<uint64_t>(*result.firmware_version));
+  ASSERT_TRUE(result.vendor_specific);
+  EXPECT_EQ(kVendorSpecific, *result.vendor_specific);
+}
+
+TEST(TelemetryApiConverters, TpmStatus) {
+  constexpr bool kEnabled = true;
+  constexpr bool kOwned = false;
+  constexpr bool kOwnerPasswortIsPresent = false;
+
+  auto input = telemetry_service::ProbeTpmStatus::New();
+  input->enabled = telemetry_service::BoolValue::New(kEnabled);
+  input->owned = telemetry_service::BoolValue::New(kOwned);
+  input->owner_password_is_present =
+      telemetry_service::BoolValue::New(kOwnerPasswortIsPresent);
+
+  auto result = ConvertPtr<telemetry_api::TpmStatus>(std::move(input));
+  ASSERT_TRUE(result.enabled);
+  EXPECT_EQ(kEnabled, *result.enabled);
+  ASSERT_TRUE(result.owned);
+  EXPECT_EQ(kOwned, *result.owned);
+  ASSERT_TRUE(result.owner_password_is_present);
+  EXPECT_EQ(kOwnerPasswortIsPresent, *result.owner_password_is_present);
+}
+
+TEST(TelemetryApiConverters, TpmDictionaryAttack) {
+  constexpr uint32_t kCounter = 42;
+  constexpr uint32_t kThreshold = 100;
+  constexpr bool kLockOutInEffect = true;
+  constexpr uint32_t kLockoutSecondsRemaining = 5;
+
+  auto input = telemetry_service::ProbeTpmDictionaryAttack::New();
+  input->counter = telemetry_service::UInt32Value::New(kCounter);
+  input->threshold = telemetry_service::UInt32Value::New(kThreshold);
+  input->lockout_in_effect =
+      telemetry_service::BoolValue::New(kLockOutInEffect);
+  input->lockout_seconds_remaining =
+      telemetry_service::UInt32Value::New(kLockoutSecondsRemaining);
+
+  auto result =
+      ConvertPtr<telemetry_api::TpmDictionaryAttack>(std::move(input));
+  ASSERT_TRUE(result.counter);
+  EXPECT_EQ(kCounter, static_cast<uint32_t>(*result.counter));
+  ASSERT_TRUE(result.threshold);
+  EXPECT_EQ(kThreshold, static_cast<uint32_t>(*result.threshold));
+  ASSERT_TRUE(result.lockout_in_effect);
+  EXPECT_EQ(kLockOutInEffect, *result.lockout_in_effect);
+  ASSERT_TRUE(result.lockout_seconds_remaining);
+  EXPECT_EQ(kLockoutSecondsRemaining,
+            static_cast<uint32_t>(*result.lockout_seconds_remaining));
+}
+
+TEST(TelemetryApiConverters, TpmInfo) {
+  // TPM Version fields.
+  constexpr uint32_t kFamily = 0x322e3000;
+  constexpr uint64_t kSpecLevel = 1000;
+  constexpr uint32_t kManufacturer = 42;
+  constexpr uint32_t kTpmModel = 101;
+  constexpr uint64_t kFirmwareVersion = 1001;
+  constexpr char kVendorSpecific[] = "info";
+
+  // TPM Status fields.
+  constexpr bool kEnabled = true;
+  constexpr bool kOwned = false;
+  constexpr bool kOwnerPasswortIsPresent = false;
+
+  // TPM dictionary attack fields.
+  constexpr uint32_t kCounter = 42;
+  constexpr uint32_t kThreshold = 100;
+  constexpr bool kLockOutInEffect = true;
+  constexpr uint32_t kLockoutSecondsRemaining = 5;
+
+  auto tpm_version = telemetry_service::ProbeTpmVersion::New();
+  tpm_version->gsc_version = telemetry_service::ProbeTpmGSCVersion::kCr50;
+  tpm_version->family = telemetry_service::UInt32Value::New(kFamily);
+  tpm_version->spec_level = telemetry_service::UInt64Value::New(kSpecLevel);
+  tpm_version->manufacturer =
+      telemetry_service::UInt32Value::New(kManufacturer);
+  tpm_version->tpm_model = telemetry_service::UInt32Value::New(kTpmModel);
+  tpm_version->firmware_version =
+      telemetry_service::UInt64Value::New(kFirmwareVersion);
+  tpm_version->vendor_specific = kVendorSpecific;
+
+  auto tpm_status = telemetry_service::ProbeTpmStatus::New();
+  tpm_status->enabled = telemetry_service::BoolValue::New(kEnabled);
+  tpm_status->owned = telemetry_service::BoolValue::New(kOwned);
+  tpm_status->owner_password_is_present =
+      telemetry_service::BoolValue::New(kOwnerPasswortIsPresent);
+
+  auto dictionary_attack = telemetry_service::ProbeTpmDictionaryAttack::New();
+  dictionary_attack->counter = telemetry_service::UInt32Value::New(kCounter);
+  dictionary_attack->threshold =
+      telemetry_service::UInt32Value::New(kThreshold);
+  dictionary_attack->lockout_in_effect =
+      telemetry_service::BoolValue::New(kLockOutInEffect);
+  dictionary_attack->lockout_seconds_remaining =
+      telemetry_service::UInt32Value::New(kLockoutSecondsRemaining);
+
+  auto input = telemetry_service::ProbeTpmInfo::New();
+  input->version = std::move(tpm_version);
+  input->status = std::move(tpm_status);
+  input->dictionary_attack = std::move(dictionary_attack);
+
+  auto result = ConvertPtr<telemetry_api::TpmInfo>(std::move(input));
+
+  auto version_result = std::move(result.version);
+  EXPECT_EQ(telemetry_api::TpmGSCVersion::TPM_GSC_VERSION_CR50,
+            version_result.gsc_version);
+  ASSERT_TRUE(version_result.family);
+  EXPECT_EQ(kFamily, static_cast<uint32_t>(*version_result.family));
+  ASSERT_TRUE(version_result.spec_level);
+  EXPECT_EQ(kSpecLevel, static_cast<uint64_t>(*version_result.spec_level));
+  ASSERT_TRUE(version_result.manufacturer);
+  EXPECT_EQ(kManufacturer, static_cast<uint32_t>(*version_result.manufacturer));
+  ASSERT_TRUE(version_result.tpm_model);
+  EXPECT_EQ(kTpmModel, static_cast<uint32_t>(*version_result.tpm_model));
+  ASSERT_TRUE(version_result.firmware_version);
+  EXPECT_EQ(kFirmwareVersion,
+            static_cast<uint64_t>(*version_result.firmware_version));
+  ASSERT_TRUE(version_result.vendor_specific);
+  EXPECT_EQ(kVendorSpecific, *version_result.vendor_specific);
+
+  auto status_result = std::move(result.status);
+  ASSERT_TRUE(status_result.enabled);
+  EXPECT_EQ(kEnabled, *status_result.enabled);
+  ASSERT_TRUE(status_result.owned);
+  EXPECT_EQ(kOwned, *status_result.owned);
+  ASSERT_TRUE(status_result.owner_password_is_present);
+  EXPECT_EQ(kOwnerPasswortIsPresent, *status_result.owner_password_is_present);
+
+  auto dictionary_attack_result = std::move(result.dictionary_attack);
+  ASSERT_TRUE(dictionary_attack_result.counter);
+  EXPECT_EQ(kCounter, static_cast<uint32_t>(*dictionary_attack_result.counter));
+  ASSERT_TRUE(dictionary_attack_result.threshold);
+  EXPECT_EQ(kThreshold,
+            static_cast<uint32_t>(*dictionary_attack_result.threshold));
+  ASSERT_TRUE(dictionary_attack_result.lockout_in_effect);
+  EXPECT_EQ(kLockOutInEffect, *dictionary_attack_result.lockout_in_effect);
+  ASSERT_TRUE(dictionary_attack_result.lockout_seconds_remaining);
+  EXPECT_EQ(kLockoutSecondsRemaining,
+            static_cast<uint32_t>(
+                *dictionary_attack_result.lockout_seconds_remaining));
+}
+
 }  // namespace converters
 }  // namespace chromeos
diff --git a/chrome/browser/download/download_file_picker.cc b/chrome/browser/download/download_file_picker.cc
index 2a1eed6..d8af81c 100644
--- a/chrome/browser/download/download_file_picker.cc
+++ b/chrome/browser/download/download_file_picker.cc
@@ -24,8 +24,6 @@
 #include "chrome/browser/ash/policy/dlp/dlp_files_controller.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
-#include "chrome/browser/chromeos/policy/dlp/dlp_warn_dialog.h"
-#include "chrome/browser/profiles/profile.h"
 #endif
 
 using download::DownloadItem;
@@ -96,10 +94,17 @@
   }
 #endif
 
+  const GURL* caller =
+#if BUILDFLAG(IS_CHROMEOS)
+      &download_item_->GetURL();
+#else
+      nullptr;
+#endif
+
   select_file_dialog_->SelectFile(
       ui::SelectFileDialog::SELECT_SAVEAS_FILE, std::u16string(),
       suggested_path_, &file_type_info, 0, base::FilePath::StringType(),
-      owning_window, nullptr);
+      owning_window, /*params=*/nullptr, caller);
 }
 
 DownloadFilePicker::~DownloadFilePicker() {
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service/BUILD.gn
index 3ab21b2..83fc2eee 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service/BUILD.gn
@@ -46,15 +46,12 @@
 
     sources = [ "rotate_util.cc" ]
 
-    public_deps = [
-      "//base",
-      "//chrome/browser/enterprise/connectors/device_trust/key_management/installer:elevated_rotation",
-      "//components/version_info",
-    ]
+    public_deps = [ "//components/version_info:channel" ]
 
     deps = [
+      "//base",
       "//chrome/browser/enterprise/connectors/device_trust/key_management/core:constants",
-      "//chrome/common:channel_info",
+      "//chrome/browser/enterprise/connectors/device_trust/key_management/installer:elevated_rotation",
       "//third_party/abseil-cpp:absl",
       "//url",
     ]
diff --git a/chrome/browser/media/webrtc/region_capture_browsertest.cc b/chrome/browser/media/webrtc/region_capture_browsertest.cc
index ab609c9..a9afaa1 100644
--- a/chrome/browser/media/webrtc/region_capture_browsertest.cc
+++ b/chrome/browser/media/webrtc/region_capture_browsertest.cc
@@ -450,16 +450,8 @@
   EXPECT_TRUE(tab.CropTo(crop_target, Frame::kTopLevelDocument));
 }
 
-// TODO(crbug.com/1359258): Flaky on Mac.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_CropToAllowedIfTopLevelCropsToElementInEmbedded \
-  DISABLED_CropToAllowedIfTopLevelCropsToElementInEmbedded
-#else
-#define MAYBE_CropToAllowedIfTopLevelCropsToElementInEmbedded \
-  CropToAllowedIfTopLevelCropsToElementInEmbedded
-#endif
 IN_PROC_BROWSER_TEST_F(RegionCaptureBrowserTest,
-                       MAYBE_CropToAllowedIfTopLevelCropsToElementInEmbedded) {
+                       CropToAllowedIfTopLevelCropsToElementInEmbedded) {
   SetUpTest(Frame::kTopLevelDocument, /*self_capture=*/true);
   TabInfo& tab = tabs_[kMainTab];
 
@@ -729,16 +721,8 @@
 }
 
 // Original track becomes unblocked for cropping after clone is GCed 1/3.
-// Flaky on Mac crbug.com/1353349
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_CanCropOriginalTrackAfterCloneIsGarbageCollected \
-  DISABLED_CanCropOriginalTrackAfterCloneIsGarbageCollected
-#else
-#define MAYBE_CanCropOriginalTrackAfterCloneIsGarbageCollected \
-  CanCropOriginalTrackAfterCloneIsGarbageCollected
-#endif
 IN_PROC_BROWSER_TEST_F(RegionCaptureClonesBrowserTest,
-                       MAYBE_CanCropOriginalTrackAfterCloneIsGarbageCollected) {
+                       CanCropOriginalTrackAfterCloneIsGarbageCollected) {
   ManualSetUp();
 
   ASSERT_TRUE(CloneTrack());
@@ -750,17 +734,8 @@
 }
 
 // Original track becomes unblocked for cropping after clone is GCed 2/3.
-// Flaky on Mac crbug.com/1353349
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_CanRecropOriginalTrackAfterCloneIsGarbageCollected \
-  DISABLED_CanRecropOriginalTrackAfterCloneIsGarbageCollected
-#else
-#define MAYBE_CanRecropOriginalTrackAfterCloneIsGarbageCollected \
-  CanRecropOriginalTrackAfterCloneIsGarbageCollected
-#endif
-IN_PROC_BROWSER_TEST_F(
-    RegionCaptureClonesBrowserTest,
-    MAYBE_CanRecropOriginalTrackAfterCloneIsGarbageCollected) {
+IN_PROC_BROWSER_TEST_F(RegionCaptureClonesBrowserTest,
+                       CanRecropOriginalTrackAfterCloneIsGarbageCollected) {
   ManualSetUp();
 
   ASSERT_TRUE(CropTo(kCropTarget0, Frame::kTopLevelDocument, Track::kOriginal));
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
index 2ced5558..4e04416d 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
@@ -203,7 +203,7 @@
   FakePasswordManagerLifecycleHelper* lifecycle_helper() {
     return lifecycle_helper_;
   }
-  syncer::SyncService* sync_service() { return &sync_service_; }
+  syncer::TestSyncService* sync_service() { return &sync_service_; }
   PasswordSyncControllerDelegateAndroid* sync_controller_delegate() {
     return sync_controller_delegate_;
   }
@@ -227,10 +227,6 @@
         /*sync_everything=*/false, /*types=*/{});
   }
 
-  void SetSyncAuthError(GoogleServiceAuthError error) {
-    sync_service_.SetAuthError(error);
-  }
-
  private:
   std::unique_ptr<PasswordStoreAndroidBackendBridge> CreateMockBridge() {
     auto unique_bridge =
@@ -1069,10 +1065,7 @@
                         base::RepeatingClosure(), base::DoNothing());
   backend().OnSyncServiceInitialized(sync_service());
 
-  GoogleServiceAuthError transient_error(
-      GoogleServiceAuthError::CONNECTION_FAILED);
-  ASSERT_TRUE(transient_error.IsTransientError());
-  SetSyncAuthError(transient_error);
+  sync_service()->SetTransientAuthError();
 
   base::MockCallback<LoginsOrErrorReply> mock_reply;
   EXPECT_CALL(*bridge(), GetAllLogins).WillOnce(Return(kJobId));
@@ -1103,10 +1096,7 @@
                         base::RepeatingClosure(), base::DoNothing());
   backend().OnSyncServiceInitialized(sync_service());
 
-  GoogleServiceAuthError persistent_error(
-      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
-  ASSERT_TRUE(persistent_error.IsPersistentError());
-  SetSyncAuthError(persistent_error);
+  sync_service()->SetPersistentAuthErrorOtherThanWebSignout();
 
   base::MockCallback<LoginsOrErrorReply> mock_reply;
   EXPECT_CALL(*bridge(), GetAllLogins).WillOnce(Return(kJobId));
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 75b522f..369bd34c 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1013,12 +1013,17 @@
           safe_browsing::kExtensionTelemetryPotentialPasswordTheft)) {
     return;
   }
-  // TODO(zackhan@): Create the struct directly without calling the construct
-  // method. And remove ConstructPasswordReuseInfo method to simplify the code.
-  safe_browsing::PasswordReuseInfo password_reuse_info =
-      pps->ConstructPasswordReuseInfo(
-          reused_password_hash, username, password_type,
-          GetMatchingDomains(matching_reused_credentials));
+  // Construct password reuse info.
+  safe_browsing::PasswordReuseInfo password_reuse_info;
+  password_reuse_info.matches_signin_password =
+      password_type == PasswordType::PRIMARY_ACCOUNT_PASSWORD;
+  password_reuse_info.matching_domains =
+      GetMatchingDomains(matching_reused_credentials);
+  password_reuse_info.reused_password_account_type =
+      pps->GetPasswordProtectionReusedPasswordAccountType(password_type,
+                                                          username);
+  password_reuse_info.count = 1;
+  password_reuse_info.reused_password_hash = reused_password_hash;
 
   // Extract the host part of an extension domain, which will be the extension
   // ID.
diff --git a/chrome/browser/password_manager/password_scripts_fetcher_factory.cc b/chrome/browser/password_manager/password_scripts_fetcher_factory.cc
index b01a3f35..66f0e41 100644
--- a/chrome/browser/password_manager/password_scripts_fetcher_factory.cc
+++ b/chrome/browser/password_manager/password_scripts_fetcher_factory.cc
@@ -5,23 +5,30 @@
 #include "chrome/browser/password_manager/password_scripts_fetcher_factory.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/no_destructor.h"
 #include "chrome/browser/autofill_assistant/common_dependencies_chrome.h"
+#include "chrome/browser/password_manager/account_password_store_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/autofill_assistant/browser/public/autofill_assistant.h"
 #include "components/autofill_assistant/browser/public/autofill_assistant_factory.h"
 #include "components/password_manager/core/browser/capabilities_service_impl.h"
 #include "components/password_manager/core/browser/password_scripts_fetcher_impl.h"
 #include "components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h"
+#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 
 PasswordScriptsFetcherFactory::PasswordScriptsFetcherFactory()
-    : ProfileKeyedServiceFactory("PasswordScriptsFetcher") {}
+    : ProfileKeyedServiceFactory("PasswordScriptsFetcher") {
+  DependsOn(PasswordStoreFactory::GetInstance());
+  DependsOn(AccountPasswordStoreFactory::GetInstance());
+}
 
 PasswordScriptsFetcherFactory::~PasswordScriptsFetcherFactory() = default;
 
@@ -53,10 +60,15 @@
         std::make_unique<CapabilitiesServiceImpl>(
             std::move(autofill_assistant));
 
+    Profile* profile = Profile::FromBrowserContext(browser_context);
+
     return new password_manager::SavedPasswordsCapabilitiesFetcher(
-        std::move(service), PasswordStoreFactory::GetForProfile(
-                                Profile::FromBrowserContext(browser_context),
-                                ServiceAccessType::EXPLICIT_ACCESS));
+        std::move(service),
+        std::make_unique<password_manager::SavedPasswordsPresenter>(
+            PasswordStoreFactory::GetForProfile(
+                profile, ServiceAccessType::EXPLICIT_ACCESS),
+            AccountPasswordStoreFactory::GetForProfile(
+                profile, ServiceAccessType::EXPLICIT_ACCESS)));
   }
 
   return new password_manager::PasswordScriptsFetcherImpl(
diff --git a/chrome/browser/resources/side_panel/read_anything/app.html b/chrome/browser/resources/side_panel/read_anything/app.html
index 4401cb4..abd66db 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.html
+++ b/chrome/browser/resources/side_panel/read_anything/app.html
@@ -5,6 +5,7 @@
     font-family: var(--font-family);
     font-size: var(--font-size);
     letter-spacing: var(--letter-spacing);
+    line-height: var(--line-height);
     padding: 20px;
   }
 </style>
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index 14fab5b0..28124e1a 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -144,11 +144,12 @@
         SkColor = {value: chrome.readAnything.backgroundColor};
 
     this.updateStyles({
-      '--foreground-color': skColorToRgba(foregroundColor),
       '--background-color': skColorToRgba(backgroundColor),
       '--font-family': this.validatedFontName(),
       '--font-size': chrome.readAnything.fontSize + 'em',
+      '--foreground-color': skColorToRgba(foregroundColor),
       '--letter-spacing': chrome.readAnything.letterSpacing + 'em',
+      '--line-height': chrome.readAnything.lineSpacing,
     });
   }
 }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index 6bae73a..7a8d604a 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -18,6 +18,7 @@
     let fontSize: number;
     let foregroundColor: number;
     let backgroundColor: number;
+    let lineSpacing: number;
     let letterSpacing: number;
 
     // Returns a list of AXNodeIDs corresponding to the unignored children of
@@ -67,7 +68,8 @@
     // Set the theme. Used by tests only.
     function setThemeForTesting(
         fontName: string, fontSize: number, foregroundColor: number,
-        backgroundColor: number, letterSpacing: number): void;
+        backgroundColor: number, lineSpacing: number,
+        letterSpacing: number): void;
 
     ////////////////////////////////////////////////////////////////
     // Implemented in read_anything/app.ts and called by native c++.
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
index 0f66d07..cb542e9 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
@@ -60,11 +60,6 @@
       persisted_at_write_interval);
 }
 
-void RecordSignalType(ExtensionSignalType signal_type) {
-  base::UmaHistogramEnumeration(
-      "SafeBrowsing.ExtensionTelemetry.Signals.SignalType", signal_type);
-}
-
 static_assert(extensions::Manifest::NUM_LOAD_TYPES == 10,
               "ExtensionTelemetryReportRequest::ExtensionInfo::Type "
               "needs to match extensions::Manifest::Type.");
@@ -178,6 +173,12 @@
   SetEnabled(IsEnhancedProtectionEnabled(*pref_service_));
 }
 
+void ExtensionTelemetryService::RecordSignalType(
+    ExtensionSignalType signal_type) {
+  base::UmaHistogramEnumeration(
+      "SafeBrowsing.ExtensionTelemetry.Signals.SignalType", signal_type);
+}
+
 void ExtensionTelemetryService::OnPrefChanged() {
   SetEnabled(IsEnhancedProtectionEnabled(*pref_service_));
 }
@@ -312,7 +313,7 @@
   ExtensionSignalType signal_type = signal->GetType();
   RecordSignalType(signal_type);
 
-  DCHECK(base::Contains(signal_processors_, signal_type));
+  DCHECK(base::Contains(signal_subscribers_, signal_type));
 
   if (extension_store_.find(signal->extension_id()) == extension_store_.end()) {
     // This is the first signal triggered by this extension since the last
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
index c7a5eee..544e937 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
@@ -69,6 +69,11 @@
 
   ~ExtensionTelemetryService() override;
 
+  // Records the signal type when a signal is:
+  // - created externally and passed to extension service using AddSignal OR
+  // - created internally by a signal processor from other signals received.
+  static void RecordSignalType(ExtensionSignalType signal_type);
+
   // Enables/disables the service.
   void SetEnabled(bool enable);
   bool enabled() const { return enabled_; }
diff --git a/chrome/browser/safe_browsing/extension_telemetry/potential_password_theft_signal_processor.cc b/chrome/browser/safe_browsing/extension_telemetry/potential_password_theft_signal_processor.cc
index e178e01..33db2aa9 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/potential_password_theft_signal_processor.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/potential_password_theft_signal_processor.cc
@@ -6,6 +6,7 @@
 
 #include "base/check_op.h"
 #include "base/containers/contains.h"
+#include "chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h"
 #include "chrome/browser/safe_browsing/extension_telemetry/password_reuse_signal.h"
 #include "chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
@@ -200,6 +201,12 @@
     remote_host_url_pb->set_count(remote_host_url_w_count_it.second);
   }
 
+  // Record the combined signal, kPotentialPasswordTheft, which is derived from
+  // kPasswordReuse and kRemoteHostContacted. The signal is created once every
+  // report creation time if there is potential password theft data available.
+  ExtensionTelemetryService::RecordSignalType(
+      ExtensionSignalType::kPotentialPasswordTheft);
+
   // Clear the data in the stores.
   // Following two iters are guaranteed to exist because
   // password_hash_remote_host_url_pair_store_it is not null.
diff --git a/chrome/browser/signin/account_id_from_account_info.cc b/chrome/browser/signin/account_id_from_account_info.cc
index 41f9c94b..8c9bc1a 100644
--- a/chrome/browser/signin/account_id_from_account_info.cc
+++ b/chrome/browser/signin/account_id_from_account_info.cc
@@ -4,16 +4,13 @@
 
 #include "chrome/browser/signin/account_id_from_account_info.h"
 #include "build/chromeos_buildflags.h"
+#include "components/account_id/account_id.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "components/user_manager/known_user.h"
-#endif
-
 AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  return user_manager::known_user::GetAccountId(
-      account_info.email, account_info.gaia, AccountType::GOOGLE);
+  return AccountId::FromNonCanonicalEmail(account_info.email, account_info.gaia,
+                                          AccountType::GOOGLE);
 #else
   if (account_info.email.empty() || account_info.gaia.empty())
     return EmptyAccountId();
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index f8da94a..fb18e59 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -107,8 +107,8 @@
     return enabled;
   }
 
-  virtual std::vector<base::Feature> GetDisabledFeatures() {
-    std::vector<base::Feature> disabled;
+  virtual std::vector<base::test::FeatureRef> GetDisabledFeatures() {
+    std::vector<base::test::FeatureRef> disabled;
     if (!is_storage_partitioned_) {
       disabled.push_back(net::features::kThirdPartyStoragePartitioning);
     }
@@ -1031,7 +1031,7 @@
         enable_standard_storage_access_api_(GetParam()) {}
 
  protected:
-  std::vector<base::Feature> GetDisabledFeatures() override {
+  std::vector<base::test::FeatureRef> GetDisabledFeatures() override {
     // The test should validate that either flag alone disables the API.
     // Note that enabling the extension and not the standard API means both are
     // disabled.
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index 59fa790..f9ba075 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -44,7 +44,7 @@
  public:
   explicit StorageAccessGrantPermissionContextTest(bool saa_enabled) {
     std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> disabled;
     if (saa_enabled) {
       enabled.push_back({net::features::kStorageAccessAPI,
                          {
diff --git a/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.cc b/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.cc
new file mode 100644
index 0000000..ca4a806f
--- /dev/null
+++ b/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.cc
@@ -0,0 +1,72 @@
+// Copyright 2022 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/supervised_user/kids_chrome_management/kids_access_token_fetcher.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/functional/bind.h"
+#include "base/no_destructor.h"
+#include "base/types/expected.h"
+#include "components/signin/public/identity_manager/access_token_fetcher.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_manager.h"
+
+namespace {
+
+using ::base::BindOnce;
+using ::base::expected;
+using ::base::NoDestructor;
+using ::base::unexpected;
+using ::base::Unretained;
+using ::signin::AccessTokenFetcher;
+using ::signin::AccessTokenInfo;
+using ::signin::ConsentLevel;
+using ::signin::IdentityManager;
+using ::signin::PrimaryAccountAccessTokenFetcher;
+
+expected<AccessTokenInfo, GoogleServiceAuthError> ToSingleReturnValue(
+    GoogleServiceAuthError error,
+    AccessTokenInfo access_token_info) {
+  if (error.state() == GoogleServiceAuthError::NONE) {
+    return access_token_info;
+  }
+  return unexpected(error);
+}
+
+}  // namespace
+
+KidsAccessTokenFetcher::KidsAccessTokenFetcher(
+    IdentityManager& identity_manager,
+    Consumer consumer)
+    : consumer_(std::move(consumer)) {
+  // base::Unretained(.) is safe, because no extra on-destroyed semantics are
+  // needed and this instance must outlive the callback execution.
+  primary_account_access_token_fetcher_ =
+      std::make_unique<PrimaryAccountAccessTokenFetcher>(
+          "family_info_fetcher", &identity_manager, Scopes(),
+          BindOnce(&KidsAccessTokenFetcher::OnAccessTokenFetchComplete,
+                   Unretained(this)),
+          PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
+          ConsentLevel::kSignin);
+}
+KidsAccessTokenFetcher::~KidsAccessTokenFetcher() = default;
+
+void KidsAccessTokenFetcher::OnAccessTokenFetchComplete(
+    GoogleServiceAuthError error,
+    signin::AccessTokenInfo access_token_info) {
+  std::move(consumer_).Run(ToSingleReturnValue(error, access_token_info));
+}
+
+const OAuth2AccessTokenManager::ScopeSet& KidsAccessTokenFetcher::Scopes() {
+  static auto nonce = NoDestructor<OAuth2AccessTokenManager::ScopeSet>(
+      {GaiaConstants::kKidFamilyReadonlyOAuth2Scope});
+  return *nonce;
+}
diff --git a/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.h b/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.h
new file mode 100644
index 0000000..04a7028
--- /dev/null
+++ b/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.h
@@ -0,0 +1,47 @@
+// Copyright 2022 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_SUPERVISED_USER_KIDS_CHROME_MANAGEMENT_KIDS_ACCESS_TOKEN_FETCHER_H_
+#define CHROME_BROWSER_SUPERVISED_USER_KIDS_CHROME_MANAGEMENT_KIDS_ACCESS_TOKEN_FETCHER_H_
+
+#include <memory>
+
+#include "base/memory/singleton.h"
+#include "base/types/expected.h"
+#include "components/signin/public/identity_manager/access_token_fetcher.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#include "content/public/browser/browser_context.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_manager.h"
+
+// Responsible for initialing the access token workflow. Executes the consuming
+// callback when the fetch is done, and then becomes disposable.
+class KidsAccessTokenFetcher {
+ public:
+  // For convenience, the interface of signin::PrimaryAccountAccessTokenFetcher
+  // is wrapped into one value, so the decision how to handle errors is up to
+  // consumers of access token fetcher.
+  using Consumer = base::OnceCallback<void(
+      base::expected<signin::AccessTokenInfo, GoogleServiceAuthError>)>;
+  // Non copyable.
+  KidsAccessTokenFetcher() = delete;
+  explicit KidsAccessTokenFetcher(signin::IdentityManager& identity_manager,
+                                  Consumer consumer);
+  KidsAccessTokenFetcher(const KidsAccessTokenFetcher&) = delete;
+  KidsAccessTokenFetcher& operator=(const KidsAccessTokenFetcher&) = delete;
+  ~KidsAccessTokenFetcher();
+
+ private:
+  void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
+                                  signin::AccessTokenInfo access_token_info);
+  static const OAuth2AccessTokenManager::ScopeSet& Scopes();
+  Consumer consumer_;
+  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher>
+      primary_account_access_token_fetcher_;
+};
+
+#endif  // CHROME_BROWSER_SUPERVISED_USER_KIDS_CHROME_MANAGEMENT_KIDS_ACCESS_TOKEN_FETCHER_H_
diff --git a/chrome/browser/supervised_user/kids_chrome_management/kids_management_service_unittest.cc b/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher_unittest.cc
similarity index 98%
rename from chrome/browser/supervised_user/kids_chrome_management/kids_management_service_unittest.cc
rename to chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher_unittest.cc
index fdc35f3..b0c052b 100644
--- a/chrome/browser/supervised_user/kids_chrome_management/kids_management_service_unittest.cc
+++ b/chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/supervised_user/kids_chrome_management/kids_management_service.h"
+#include "chrome/browser/supervised_user/kids_chrome_management/kids_access_token_fetcher.h"
 
 #include "base/bind.h"
 #include "base/functional/callback.h"
diff --git a/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.cc b/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.cc
index f945819..a0de0fe5 100644
--- a/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.cc
+++ b/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.cc
@@ -4,75 +4,13 @@
 
 #include "chrome/browser/supervised_user/kids_chrome_management/kids_management_service.h"
 
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/functional/bind.h"
-#include "base/no_destructor.h"
-#include "base/types/expected.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/public/identity_manager/access_token_fetcher.h"
-#include "components/signin/public/identity_manager/access_token_info.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "content/public/browser/browser_context.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "google_apis/gaia/oauth2_access_token_manager.h"
 
 namespace {
-
-using ::base::BindOnce;
-using ::base::expected;
-using ::base::NoDestructor;
-using ::base::unexpected;
-using ::base::Unretained;
 using ::content::BrowserContext;
-using ::signin::AccessTokenFetcher;
-using ::signin::AccessTokenInfo;
-using ::signin::ConsentLevel;
-using ::signin::IdentityManager;
-using ::signin::PrimaryAccountAccessTokenFetcher;
-
-expected<AccessTokenInfo, GoogleServiceAuthError> ToSingleReturnValue(
-    GoogleServiceAuthError error,
-    AccessTokenInfo access_token_info) {
-  if (error.state() == GoogleServiceAuthError::NONE) {
-    return access_token_info;
-  }
-  return unexpected(error);
-}
-
 }  // namespace
 
-KidsAccessTokenFetcher::KidsAccessTokenFetcher(
-    IdentityManager& identity_manager,
-    Consumer consumer)
-    : consumer_(std::move(consumer)) {
-  // base::Unretained(.) is safe, because no extra on-destroyed semantics are
-  // needed and this instance must outlive the callback execution.
-  primary_account_access_token_fetcher_ =
-      std::make_unique<PrimaryAccountAccessTokenFetcher>(
-          "family_info_fetcher", &identity_manager, Scopes(),
-          BindOnce(&KidsAccessTokenFetcher::OnAccessTokenFetchComplete,
-                   Unretained(this)),
-          PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
-          ConsentLevel::kSignin);
-}
-KidsAccessTokenFetcher::~KidsAccessTokenFetcher() = default;
-
-void KidsAccessTokenFetcher::OnAccessTokenFetchComplete(
-    GoogleServiceAuthError error,
-    signin::AccessTokenInfo access_token_info) {
-  std::move(consumer_).Run(ToSingleReturnValue(error, access_token_info));
-}
-
-const OAuth2AccessTokenManager::ScopeSet& KidsAccessTokenFetcher::Scopes() {
-  static auto nonce = NoDestructor<OAuth2AccessTokenManager::ScopeSet>(
-      {GaiaConstants::kKidFamilyReadonlyOAuth2Scope});
-  return *nonce;
-}
 // Builds the service instance and its local dependencies.
 // The profile dependency is needed to verify the dynamic child account status.
 KeyedService* KidsManagementServiceFactory::BuildServiceInstanceFor(
diff --git a/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.h b/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.h
index a9acb84..9166091 100644
--- a/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.h
+++ b/chrome/browser/supervised_user/kids_chrome_management/kids_management_service.h
@@ -5,46 +5,10 @@
 #ifndef CHROME_BROWSER_SUPERVISED_USER_KIDS_CHROME_MANAGEMENT_KIDS_MANAGEMENT_SERVICE_H_
 #define CHROME_BROWSER_SUPERVISED_USER_KIDS_CHROME_MANAGEMENT_KIDS_MANAGEMENT_SERVICE_H_
 
-#include <memory>
-
 #include "base/memory/singleton.h"
-#include "base/types/expected.h"
 #include "chrome/browser/profiles/profile_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/public/identity_manager/access_token_fetcher.h"
-#include "components/signin/public/identity_manager/access_token_info.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
-#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "content/public/browser/browser_context.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "google_apis/gaia/oauth2_access_token_manager.h"
-
-// Responsible for initialing the access token workflow. Executes the consuming
-// callback when the fetch is done, and then becomes disposable.
-class KidsAccessTokenFetcher {
- public:
-  // For convenience, the interface of signin::PrimaryAccountAccessTokenFetcher
-  // is wrapped into one value, so the decision how to handle errors is up to
-  // consumers of access token fetcher.
-  using Consumer = base::OnceCallback<void(
-      base::expected<signin::AccessTokenInfo, GoogleServiceAuthError>)>;
-  // Non copyable.
-  KidsAccessTokenFetcher() = delete;
-  explicit KidsAccessTokenFetcher(signin::IdentityManager& identity_manager,
-                                  Consumer consumer);
-  KidsAccessTokenFetcher(const KidsAccessTokenFetcher&) = delete;
-  KidsAccessTokenFetcher& operator=(const KidsAccessTokenFetcher&) = delete;
-  ~KidsAccessTokenFetcher();
-
- private:
-  void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
-                                  signin::AccessTokenInfo access_token_info);
-  static const OAuth2AccessTokenManager::ScopeSet& Scopes();
-  Consumer consumer_;
-  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher>
-      primary_account_access_token_fetcher_;
-};
 
 // A keyed service aggregating services for respective RPCs in
 // KidsManagementAPI.
diff --git a/chrome/browser/supervised_user/parental_control_metrics_unittest.cc b/chrome/browser/supervised_user/parental_control_metrics_unittest.cc
index 7d47ec0e..cc47601b 100644
--- a/chrome/browser/supervised_user/parental_control_metrics_unittest.cc
+++ b/chrome/browser/supervised_user/parental_control_metrics_unittest.cc
@@ -154,10 +154,10 @@
 
   // Blocks `kExampleHost0`.
   {
-    DictionaryPrefUpdate hosts_update(GetPrefs(),
+    ScopedDictPrefUpdate hosts_update(GetPrefs(),
                                       prefs::kSupervisedUserManualHosts);
-    base::Value* hosts = hosts_update.Get();
-    hosts->SetBoolKey(kExampleHost0, false);
+    base::Value::Dict& hosts = hosts_update.Get();
+    hosts.Set(kExampleHost0, false);
   }
 
   histogram_tester_.ExpectBucketCount(
@@ -174,10 +174,10 @@
 
   // Approves `kExampleHost0`.
   {
-    DictionaryPrefUpdate hosts_update(GetPrefs(),
+    ScopedDictPrefUpdate hosts_update(GetPrefs(),
                                       prefs::kSupervisedUserManualHosts);
-    base::Value* hosts = hosts_update.Get();
-    hosts->SetBoolKey(kExampleHost0, true);
+    base::Value::Dict& hosts = hosts_update.Get();
+    hosts.Set(kExampleHost0, true);
   }
 
   histogram_tester_.ExpectBucketCount(
@@ -194,10 +194,10 @@
 
   // Blocks `kExampleURL1`.
   {
-    DictionaryPrefUpdate urls_update(GetPrefs(),
+    ScopedDictPrefUpdate urls_update(GetPrefs(),
                                      prefs::kSupervisedUserManualURLs);
-    base::Value* urls = urls_update.Get();
-    urls->SetBoolKey(kExampleURL1, false);
+    base::Value::Dict& urls = urls_update.Get();
+    urls.Set(kExampleURL1, false);
   }
 
   histogram_tester_.ExpectBucketCount(
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 56845ed..64fcf00 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -870,22 +870,20 @@
     const std::string& version,
     ApprovedExtensionChange type) {
   PrefService* pref_service = GetPrefService();
-  DictionaryPrefUpdate update(pref_service,
+  ScopedDictPrefUpdate update(pref_service,
                               prefs::kSupervisedUserApprovedExtensions);
-  base::Value* approved_extensions = update.Get();
-  DCHECK(approved_extensions)
-      << "kSupervisedUserApprovedExtensions pref not found";
+  base::Value::Dict& approved_extensions = update.Get();
   bool success = false;
   switch (type) {
     case ApprovedExtensionChange::kAdd:
-      DCHECK(!approved_extensions->FindStringKey(extension_id));
-      approved_extensions->SetStringKey(extension_id, std::move(version));
+      DCHECK(!approved_extensions.FindString(extension_id));
+      approved_extensions.Set(extension_id, std::move(version));
       SupervisedUserExtensionsMetricsRecorder::RecordExtensionsUmaMetrics(
           SupervisedUserExtensionsMetricsRecorder::UmaExtensionState::
               kApprovalGranted);
       break;
     case ApprovedExtensionChange::kRemove:
-      success = approved_extensions->RemoveKey(extension_id);
+      success = approved_extensions.Remove(extension_id);
       DCHECK(success);
       SupervisedUserExtensionsMetricsRecorder::RecordExtensionsUmaMetrics(
           SupervisedUserExtensionsMetricsRecorder::UmaExtensionState::
diff --git a/chrome/browser/sync/sync_startup_tracker_unittest.cc b/chrome/browser/sync/sync_startup_tracker_unittest.cc
index 5bc05a4f..d1ab027 100644
--- a/chrome/browser/sync/sync_startup_tracker_unittest.cc
+++ b/chrome/browser/sync/sync_startup_tracker_unittest.cc
@@ -58,8 +58,7 @@
   sync_service_.SetDisableReasons(syncer::SyncService::DisableReasonSet());
   sync_service_.SetTransportState(
       syncer::SyncService::TransportState::INITIALIZING);
-  sync_service_.SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  sync_service_.SetPersistentAuthErrorOtherThanWebSignout();
   EXPECT_CALL(callback_, Run(SyncStartupTracker::ServiceStartupState::kError));
   SyncStartupTracker tracker(&sync_service_, callback_.Get());
 }
@@ -89,8 +88,7 @@
   sync_service_.SetDisableReasons(syncer::SyncService::DisableReasonSet());
   sync_service_.SetTransportState(
       syncer::SyncService::TransportState::INITIALIZING);
-  sync_service_.SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  sync_service_.SetPersistentAuthErrorOtherThanWebSignout();
   EXPECT_CALL(callback_, Run(SyncStartupTracker::ServiceStartupState::kError));
   tracker.OnStateChanged(&sync_service_);
 }
diff --git a/chrome/browser/sync/test/integration/password_manager_sync_test.cc b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
index bd5cf07..086ecd0 100644
--- a/chrome/browser/sync/test/integration/password_manager_sync_test.cc
+++ b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/password_manager/password_manager_test_base.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/password_manager/passwords_navigation_observer.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/test/integration/passwords_helper.h"
 #include "chrome/browser/sync/test/integration/secondary_account_helper.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
@@ -32,9 +33,11 @@
 #include "components/password_manager/core/browser/password_manager_features_util.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_store_interface.h"
+#include "components/password_manager/core/browser/password_sync_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/sync/driver/sync_auth_util.h"
 #include "components/sync/driver/sync_service_impl.h"
 #include "components/sync/test/fake_server_nigori_helper.h"
 #include "content/public/browser/browser_context.h"
@@ -112,6 +115,8 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
+    SyncTest::SetUpCommandLine(command_line);
+
     // Make sure that fake Gaia pages served by the test server match the Gaia
     // URL (as specified by GaiaUrls::gaia_url()). Note that even though the
     // hostname is the same, it's necessary to override the port to the one used
@@ -893,4 +898,40 @@
 
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
+IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, SyncUtilApis) {
+  // Username hardcoded in SyncTest.
+  const std::string kExpectedUsername = "user@gmail.com";
+
+  ASSERT_TRUE(SetupSync());
+
+  EXPECT_TRUE(
+      password_manager::sync_util::IsPasswordSyncEnabled(GetSyncService(0)));
+  EXPECT_TRUE(
+      password_manager::sync_util::IsPasswordSyncActive(GetSyncService(0)));
+  EXPECT_EQ(password_manager::sync_util::GetSyncUsernameIfSyncingPasswords(
+                GetSyncService(0),
+                IdentityManagerFactory::GetForProfile(GetProfile(0))),
+            kExpectedUsername);
+
+  // Enter a persistent auth error state (web signout).
+  GetClient(0)->EnterSyncPausedStateForPrimaryAccount();
+  ASSERT_TRUE(syncer::IsWebSignout(GetSyncService(0)->GetAuthError()));
+
+  // Passwords are not sync-ing actively while sync is paused due to a web
+  // signout. Note that this is not the case for other persistent auth errors.
+  // TODO(crbug.com/1156584): Update comments when the logic gets unified for
+  // all persistent auth errors.
+  EXPECT_FALSE(
+      password_manager::sync_util::IsPasswordSyncActive(GetSyncService(0)));
+
+  // In the current implementation, these predicates return true even if sync is
+  // paused.
+  EXPECT_TRUE(
+      password_manager::sync_util::IsPasswordSyncEnabled(GetSyncService(0)));
+  EXPECT_EQ(password_manager::sync_util::GetSyncUsernameIfSyncingPasswords(
+                GetSyncService(0),
+                IdentityManagerFactory::GetForProfile(GetProfile(0))),
+            kExpectedUsername);
+}
+
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_history_sync_test.cc b/chrome/browser/sync/test/integration/single_client_history_sync_test.cc
index c1eb8af..74ea72a 100644
--- a/chrome/browser/sync/test/integration/single_client_history_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_history_sync_test.cc
@@ -46,6 +46,9 @@
 using testing::Not;
 using testing::UnorderedElementsAre;
 
+const char kRedirectFromPath[] = "/redirect.html";
+const char kRedirectToPath[] = "/sync/simple.html";
+
 MATCHER_P(UrlIs, url, "") {
   if (arg.redirect_entries_size() != 1) {
     return false;
@@ -53,14 +56,34 @@
   return arg.redirect_entries(0).url() == url;
 }
 
+MATCHER_P2(UrlsAre, url1, url2, "") {
+  if (arg.redirect_entries_size() != 2) {
+    return false;
+  }
+  return arg.redirect_entries(0).url() == url1 &&
+         arg.redirect_entries(1).url() == url2;
+}
+
 MATCHER_P(CoreTransitionIs, transition, "") {
   return arg.page_transition().core_transition() == transition;
 }
 
+MATCHER(IsChainStart, "") {
+  return !arg.redirect_chain_start_incomplete();
+}
+
+MATCHER(IsChainEnd, "") {
+  return !arg.redirect_chain_end_incomplete();
+}
+
 MATCHER(HasReferringVisit, "") {
   return arg.originator_referring_visit_id() != 0;
 }
 
+MATCHER(HasOpenerVisit, "") {
+  return arg.originator_opener_visit_id() != 0;
+}
+
 MATCHER(HasReferrerURL, "") {
   return !arg.referrer_url().empty();
 }
@@ -73,13 +96,19 @@
   return arg.visit_duration_micros() > 0;
 }
 
+MATCHER(HasHttpResponseCode, "") {
+  return arg.http_response_code() > 0;
+}
+
 MATCHER(StandardFieldsArePopulated, "") {
   // Checks all fields that should never be empty/unset/default. Some fields can
   // be legitimately empty, or are set after an entity is first created.
   // May be legitimately empty:
-  //   redirect_entries.title, redirect_entries.redirect_type,
-  //   originator_referring_visit_id, originator_opener_visit_id,
-  //   root_task_id, parent_task_id
+  //   redirect_entries.title (may simply be empty)
+  //   redirect_entries.redirect_type (empty if it's not a redirect)
+  //   originator_referring_visit_id, originator_opener_visit_id (may not exist)
+  //   root_task_id, parent_task_id (not always set)
+  //   http_response_code (unset for replaced navigations)
   // Populated later:
   //   visit_duration_micros, page_language, password_state
   return arg.visit_time_windows_epoch_micros() > 0 &&
@@ -87,8 +116,7 @@
          arg.redirect_entries_size() > 0 &&
          arg.redirect_entries(0).originator_visit_id() > 0 &&
          !arg.redirect_entries(0).url().empty() && arg.has_browser_type() &&
-         arg.window_id() > 0 && arg.tab_id() > 0 && arg.task_id() > 0 &&
-         arg.http_response_code() > 0;
+         arg.window_id() > 0 && arg.tab_id() > 0 && arg.task_id() > 0;
 }
 
 std::vector<sync_pb::HistorySpecifics> SyncEntitiesToHistorySpecifics(
@@ -160,6 +188,20 @@
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
 
+    // Set up a server redirect from `kRedirectFromPath` to `kRedirectToPath`.
+    embedded_test_server()->RegisterDefaultHandler(base::BindRepeating(
+        [](const net::test_server::HttpRequest& request)
+            -> std::unique_ptr<net::test_server::HttpResponse> {
+          if (request.relative_url != kRedirectFromPath) {
+            return nullptr;
+          }
+          auto response =
+              std::make_unique<net::test_server::BasicHttpResponse>();
+          response->set_code(net::HTTP_TEMPORARY_REDIRECT);
+          response->AddCustomHeader("Location", kRedirectToPath);
+          return response;
+        }));
+
     ASSERT_TRUE(embedded_test_server()->Start());
 
     SyncTest::SetUpOnMainThread();
@@ -208,7 +250,6 @@
         fake_server_->GetSyncEntitiesByModelType(syncer::HISTORY));
   }
 
- private:
   content::WebContents* GetActiveWebContents() {
 #if BUILDFLAG(IS_ANDROID)
     return chrome_test_utils::GetActiveWebContents(this);
@@ -220,6 +261,7 @@
 #endif
   }
 
+ private:
   base::test::ScopedFeatureList features_;
 };
 
@@ -283,11 +325,107 @@
   EXPECT_TRUE(WaitForHistory(UnorderedElementsAre(
       AllOf(StandardFieldsArePopulated(), UrlIs(url1.spec()),
             CoreTransitionIs(sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK),
-            Not(HasReferringVisit()), Not(HasReferrerURL()),
-            HasVisitDuration()),
+            HasHttpResponseCode(), Not(HasReferringVisit()),
+            Not(HasReferrerURL()), HasVisitDuration()),
       AllOf(StandardFieldsArePopulated(), UrlIs(url2.spec()),
             CoreTransitionIs(sync_pb::SyncEnums_PageTransition_LINK),
-            HasReferringVisit(), ReferrerURLIs(url1.spec())))));
+            HasHttpResponseCode(), HasReferringVisit(),
+            ReferrerURLIs(url1.spec())))));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientHistorySyncTest, UploadsServerRedirect) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  // Navigate to a URL which will redirect to another URL via a server redirect
+  // i.e. an HTTP 3xx response (see SetUpOnMainThread()).
+  const GURL url_from =
+      embedded_test_server()->GetURL("www.host.com", kRedirectFromPath);
+  NavigateToURL(url_from, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+
+  const GURL url_to =
+      embedded_test_server()->GetURL("www.host.com", kRedirectToPath);
+
+  // The redirect chain should have been uploaded as a single entity (since
+  // server redirects within a chain all have the same visit_time).
+  EXPECT_TRUE(WaitForHistory(UnorderedElementsAre(AllOf(
+      StandardFieldsArePopulated(), UrlsAre(url_from.spec(), url_to.spec()),
+      IsChainStart(), IsChainEnd(), Not(HasReferringVisit())))));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientHistorySyncTest, UploadsClientMetaRedirect) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  // Navigate to a URL which will redirect to another URL via an html <meta>
+  // tag.
+  const GURL url_from = embedded_test_server()->GetURL(
+      "www.host.com", "/sync/meta_redirect.html");
+  NavigateToURL(url_from, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+
+  const GURL url_to =
+      embedded_test_server()->GetURL("www.host.com", kRedirectToPath);
+
+  // The redirect chain should have been uploaded as two separate entities,
+  // since client redirects result in different visit_times. However, the
+  // chain_start and chain_end markers should indicate that these two entities
+  // belong to the same chain.
+  EXPECT_TRUE(WaitForHistory(UnorderedElementsAre(
+      AllOf(StandardFieldsArePopulated(), UrlIs(url_from.spec()),
+            IsChainStart(), Not(IsChainEnd()), Not(HasReferringVisit())),
+      AllOf(StandardFieldsArePopulated(), UrlIs(url_to.spec()),
+            Not(IsChainStart()), IsChainEnd(), HasReferringVisit()))));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientHistorySyncTest, UploadsClientJSRedirect) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  // Navigate to a page.
+  const GURL url1 =
+      embedded_test_server()->GetURL("www.host1.com", "/sync/simple.html");
+  NavigateToURL(url1, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+
+  // The page sets window.location in JavaScript to redirect to a different URL.
+  const GURL url2 =
+      embedded_test_server()->GetURL("www.host2.com", "/sync/simple.html");
+  ASSERT_TRUE(content::ExecJs(
+      GetActiveWebContents(),
+      base::StringPrintf("window.location = '%s';", url2.spec().c_str())));
+
+  // This kind of "redirect" is not actually considered a redirect by the
+  // history backend, so two separate sync entities should have been uploaded,
+  // each its own complete redirect chain.
+  EXPECT_TRUE(WaitForHistory(UnorderedElementsAre(
+      AllOf(StandardFieldsArePopulated(), UrlIs(url1.spec()), IsChainStart(),
+            IsChainEnd()),
+      AllOf(StandardFieldsArePopulated(), UrlIs(url2.spec()), IsChainStart(),
+            IsChainEnd()))));
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientHistorySyncTest,
+                       UploadsReplaceStateNavigation) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  // Navigate to some page.
+  const GURL url1 =
+      embedded_test_server()->GetURL("www.host1.com", "/sync/simple.html");
+  NavigateToURL(url1, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+
+  // The page uses the JS history.replaceState API to update the URL.
+  const GURL url2 =
+      embedded_test_server()->GetURL("www.host1.com", "/replaced_history.html");
+  ASSERT_TRUE(content::ExecJs(
+      GetActiveWebContents(),
+      base::StringPrintf("history.replaceState({}, 'page 2', '%s')",
+                         url2.spec().c_str())));
+
+  // This results in two visits with different visit_times, which thus gets
+  // mapped to two separate sync entities. There's no redirection link between
+  // the two, but since it was a same-document navigation, the first visit
+  // should be the opener of the second.
+  EXPECT_TRUE(WaitForHistory(UnorderedElementsAre(
+      AllOf(StandardFieldsArePopulated(), UrlIs(url1.spec()), IsChainStart(),
+            IsChainEnd()),
+      AllOf(StandardFieldsArePopulated(), UrlIs(url2.spec()), IsChainStart(),
+            IsChainEnd(), HasOpenerVisit()))));
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc b/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
index 0544f38c..3e753fd 100644
--- a/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
@@ -194,8 +194,8 @@
 class SingleClientSyncInvalidationsTestBase : public SyncTest {
  public:
   SingleClientSyncInvalidationsTestBase(
-      const std::vector<base::Feature>& enabled_features,
-      const std::vector<base::Feature>& disabled_features)
+      const std::vector<base::test::FeatureRef>& enabled_features,
+      const std::vector<base::test::FeatureRef>& disabled_features)
       : SyncTest(SINGLE_CLIENT) {
     override_features_.InitWithFeatures(enabled_features, disabled_features);
   }
diff --git a/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc b/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
index cc27381c..5d03b87 100644
--- a/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
+++ b/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
@@ -24,7 +24,7 @@
 
 WebAppsSyncTestBase::WebAppsSyncTestBase(TestType test_type)
     : SyncTest(test_type) {
-  std::vector<base::Feature> disabled_features;
+  std::vector<base::test::FeatureRef> disabled_features;
 
 #if BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/1357905): Update test driver to work with new UI.
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_autofill_profile_item.xml b/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_autofill_profile_item.xml
index 8b0639f..f64d774 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_autofill_profile_item.xml
+++ b/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_autofill_profile_item.xml
@@ -49,17 +49,23 @@
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
-        <TextView
-          android:id="@+id/fast_checkout_autofill_profile_item_email"
+        <LinearLayout
+          android:id="@+id/fast_checkout_autofill_profile_sub_section"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
-          android:layout_marginTop="@dimen/fast_checkout_address_item_email_margin_top"
-          android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
-        <TextView
-          android:id="@+id/fast_checkout_autofill_profile_item_phone_number"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+          android:orientation="vertical"
+          android:layout_marginTop="@dimen/fast_checkout_address_item_email_margin_top">
+            <TextView
+              android:id="@+id/fast_checkout_autofill_profile_item_email"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+            <TextView
+              android:id="@+id/fast_checkout_autofill_profile_item_phone_number"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+        </LinearLayout>
     </LinearLayout>
     <ImageView
         android:id="@+id/fast_checkout_autofill_profile_item_selected_icon"
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_home_screen_sheet.xml b/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_home_screen_sheet.xml
index 74e19633..92d5fee 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_home_screen_sheet.xml
+++ b/chrome/browser/ui/android/fast_checkout/internal/java/res/layout/fast_checkout_home_screen_sheet.xml
@@ -74,17 +74,23 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
-            <TextView
-              android:id="@+id/fast_checkout_home_sheet_profile_email"
+            <LinearLayout
+              android:id="@+id/fast_checkout_home_sheet_profile_sub_section"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_marginTop="@dimen/fast_checkout_address_item_email_margin_top"
-              android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
-            <TextView
-              android:id="@+id/fast_checkout_home_sheet_profile_phone_number"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+              android:orientation="vertical"
+              android:layout_marginTop="@dimen/fast_checkout_address_item_email_margin_top">
+              <TextView
+                android:id="@+id/fast_checkout_home_sheet_profile_email"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+              <TextView
+                android:id="@+id/fast_checkout_home_sheet_profile_phone_number"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
+            </LinearLayout>
         </LinearLayout>
         <ImageView
             android:id="@+id/fast_checkout_expand_icon_autofill_profile"
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/AutofillProfileItemViewBinder.java b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/AutofillProfileItemViewBinder.java
index 23ff207..b448be2 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/AutofillProfileItemViewBinder.java
+++ b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/AutofillProfileItemViewBinder.java
@@ -30,19 +30,43 @@
     static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
         if (propertyKey == AUTOFILL_PROFILE) {
             FastCheckoutAutofillProfile profile = model.get(AUTOFILL_PROFILE);
-            ((TextView) view.findViewById(R.id.fast_checkout_autofill_profile_item_name))
-                    .setText(profile.getFullName());
-            ((TextView) view.findViewById(R.id.fast_checkout_autofill_profile_item_street_address))
-                    .setText(profile.getStreetAddress());
-            ((TextView) view.findViewById(
-                     R.id.fast_checkout_autofill_profile_item_city_and_postal_code))
-                    .setText(getCityAndPostalCode(profile));
-            ((TextView) view.findViewById(R.id.fast_checkout_autofill_profile_item_country))
-                    .setText(profile.getCountryName());
-            ((TextView) view.findViewById(R.id.fast_checkout_autofill_profile_item_email))
-                    .setText(profile.getEmailAddress());
-            ((TextView) view.findViewById(R.id.fast_checkout_autofill_profile_item_phone_number))
-                    .setText(profile.getPhoneNumber());
+            TextView fullNameView =
+                    view.findViewById(R.id.fast_checkout_autofill_profile_item_name);
+            fullNameView.setText(profile.getFullName());
+
+            TextView streetAddressView =
+                    view.findViewById(R.id.fast_checkout_autofill_profile_item_street_address);
+            streetAddressView.setText(profile.getStreetAddress());
+
+            TextView postalCodeView = view.findViewById(
+                    R.id.fast_checkout_autofill_profile_item_city_and_postal_code);
+            postalCodeView.setText(getLocalityAndPostalCode(profile));
+
+            TextView countryView =
+                    view.findViewById(R.id.fast_checkout_autofill_profile_item_country);
+            countryView.setText(profile.getCountryName());
+
+            TextView emailView = view.findViewById(R.id.fast_checkout_autofill_profile_item_email);
+            emailView.setText(profile.getEmailAddress());
+
+            TextView phoneNumber =
+                    view.findViewById(R.id.fast_checkout_autofill_profile_item_phone_number);
+            phoneNumber.setText(profile.getPhoneNumber());
+
+            hideIfEmpty(fullNameView);
+            hideIfEmpty(streetAddressView);
+            hideIfEmpty(postalCodeView);
+            hideIfEmpty(countryView);
+            hideIfEmpty(emailView);
+            hideIfEmpty(phoneNumber);
+
+            // Hide address profile subsection if empty.
+            View profileSubSectionView =
+                    view.findViewById(R.id.fast_checkout_autofill_profile_sub_section);
+            profileSubSectionView.setVisibility(
+                    profile.getEmailAddress().isEmpty() && profile.getPhoneNumber().isEmpty()
+                            ? View.GONE
+                            : View.VISIBLE);
         } else if (propertyKey == ON_CLICK_LISTENER) {
             view.setOnClickListener((v) -> model.get(ON_CLICK_LISTENER).run());
         } else if (propertyKey == IS_SELECTED) {
@@ -55,11 +79,18 @@
      * Returns the properly formatted combination of city and postal code. For now,
      * that means adhering to US formatting.
      */
-    private static String getCityAndPostalCode(FastCheckoutAutofillProfile profile) {
+    private static String getLocalityAndPostalCode(FastCheckoutAutofillProfile profile) {
         StringBuilder builder = new StringBuilder();
         builder.append(profile.getLocality());
-        builder.append(", ");
+        // Add divider only if both elements exist.
+        if (!profile.getLocality().isEmpty() && !profile.getPostalCode().isEmpty()) {
+            builder.append(", ");
+        }
         builder.append(profile.getPostalCode());
         return builder.toString();
     }
+
+    private static void hideIfEmpty(TextView view) {
+        view.setVisibility(view.length() == 0 ? View.GONE : View.VISIBLE);
+    }
 }
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/CreditCardItemViewBinder.java b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/CreditCardItemViewBinder.java
index a05f98c..415be10 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/CreditCardItemViewBinder.java
+++ b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/detail_screen/CreditCardItemViewBinder.java
@@ -35,22 +35,22 @@
         if (propertyKey == CREDIT_CARD) {
             FastCheckoutCreditCard card = model.get(CREDIT_CARD);
 
-            ((TextView) view.findViewById(R.id.fast_checkout_credit_card_item_number))
-                    .setText(card.getObfuscatedNumber());
+            TextView numberView = view.findViewById(R.id.fast_checkout_credit_card_item_number);
+            numberView.setText(card.getObfuscatedNumber());
 
-            // Only show the name line if a name is set.
             TextView nameView = view.findViewById(R.id.fast_checkout_credit_card_item_name);
-            if (card.getName().isEmpty()) {
-                nameView.setVisibility(View.INVISIBLE);
-            } else {
-                nameView.setText(card.getName());
-                nameView.setVisibility(View.VISIBLE);
-            }
+            nameView.setText(card.getName());
 
-            ((TextView) view.findViewById(R.id.fast_checkout_credit_card_item_expiration_date))
-                    .setText(card.getFormattedExpirationDate(view.getContext()));
+            TextView expirationDateViewView =
+                    view.findViewById(R.id.fast_checkout_credit_card_item_expiration_date);
+            expirationDateViewView.setText(card.getFormattedExpirationDate(view.getContext()));
 
             ImageView icon = view.findViewById(R.id.fast_checkout_credit_card_icon);
+
+            hideIfEmpty(numberView);
+            hideIfEmpty(nameView);
+            hideIfEmpty(expirationDateViewView);
+
             try {
                 icon.setImageDrawable(AppCompatResources.getDrawable(
                         icon.getContext(), card.getIssuerIconDrawableId()));
@@ -64,4 +64,8 @@
                     .setVisibility(model.get(IS_SELECTED) ? View.VISIBLE : View.GONE);
         }
     }
+
+    private static void hideIfEmpty(TextView view) {
+        view.setVisibility(view.length() == 0 ? View.GONE : View.VISIBLE);
+    }
 }
diff --git a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/home_screen/HomeScreenViewBinder.java b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/home_screen/HomeScreenViewBinder.java
index cdbce279..e2974db 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/home_screen/HomeScreenViewBinder.java
+++ b/chrome/browser/ui/android/fast_checkout/internal/java/src/org/chromium/chrome/browser/ui/fast_checkout/home_screen/HomeScreenViewBinder.java
@@ -35,6 +35,7 @@
         final Context mContext;
         final TextView mFullNameTextView;
         final TextView mStreetAddressTextView;
+        final LinearLayout mProfileSubsectionView;
         final TextView mEmailAddressTextView;
         final TextView mPhoneNumberTextView;
         final TextView mCreditCardHeaderTextView;
@@ -49,6 +50,8 @@
                     contentView.findViewById(R.id.fast_checkout_home_sheet_profile_name);
             mStreetAddressTextView =
                     contentView.findViewById(R.id.fast_checkout_home_sheet_profile_street);
+            mProfileSubsectionView =
+                    contentView.findViewById(R.id.fast_checkout_home_sheet_profile_sub_section);
             mEmailAddressTextView =
                     contentView.findViewById(R.id.fast_checkout_home_sheet_profile_email);
             mPhoneNumberTextView =
@@ -79,7 +82,14 @@
     }
 
     private static String getFullStreetAddress(FastCheckoutAutofillProfile profile) {
-        return profile.getStreetAddress() + ", " + profile.getPostalCode();
+        StringBuilder builder = new StringBuilder();
+        builder.append(profile.getStreetAddress());
+        // Add divider only if both elements exist.
+        if (!profile.getStreetAddress().isEmpty() && !profile.getPostalCode().isEmpty()) {
+            builder.append(", ");
+        }
+        builder.append(profile.getPostalCode());
+        return builder.toString();
     }
 
     private static void updateProfile(PropertyModel model, ViewHolder view) {
@@ -88,6 +98,16 @@
         view.mStreetAddressTextView.setText(getFullStreetAddress(profile));
         view.mEmailAddressTextView.setText(profile.getEmailAddress());
         view.mPhoneNumberTextView.setText(profile.getPhoneNumber());
+        hideIfEmpty(view.mFullNameTextView);
+        hideIfEmpty(view.mStreetAddressTextView);
+        hideIfEmpty(view.mEmailAddressTextView);
+        hideIfEmpty(view.mPhoneNumberTextView);
+
+        // Hide address profile subsection if empty.
+        view.mProfileSubsectionView.setVisibility(
+                profile.getEmailAddress().isEmpty() && profile.getPhoneNumber().isEmpty()
+                        ? View.GONE
+                        : View.VISIBLE);
     }
 
     private static void updateCreditCard(PropertyModel model, ViewHolder view) {
@@ -100,4 +120,8 @@
             view.mCreditCardImageView.setImageDrawable(null);
         }
     }
+
+    private static void hideIfEmpty(TextView view) {
+        view.setVisibility(view.length() == 0 ? View.GONE : View.VISIBLE);
+    }
 }
diff --git a/chrome/browser/ui/android/fast_checkout/internal/junit/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutDetailScreenViewTest.java b/chrome/browser/ui/android/fast_checkout/internal/junit/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutDetailScreenViewTest.java
index 9f63825e..791eff68 100644
--- a/chrome/browser/ui/android/fast_checkout/internal/junit/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutDetailScreenViewTest.java
+++ b/chrome/browser/ui/android/fast_checkout/internal/junit/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutDetailScreenViewTest.java
@@ -188,6 +188,12 @@
     @Test
     @SmallTest
     public void testRecyclerViewBindsProfileDataToItemView() {
+        FastCheckoutAutofillProfile emptyFieldsProfile =
+                FastCheckoutTestUtils.createDetailedProfile(
+                        /*guid=*/"111", /*name=*/"", /*streetAddress=*/"",
+                        /*city=*/"", /*postalCode=*/"", /*email=*/"",
+                        /*phoneNumber=*/"");
+
         ModelList models = mModel.get(PROFILE_MODEL_LIST);
         models.add(new ListItem(DetailItemType.PROFILE,
                 AutofillProfileItemProperties.create(sSampleProfile1, /*isSelected=*/false,
@@ -195,23 +201,44 @@
         models.add(new ListItem(DetailItemType.PROFILE,
                 AutofillProfileItemProperties.create(sSampleProfile2, /*isSelected=*/true,
                         /*onClickListener=*/() -> {})));
+
+        models.add(new ListItem(DetailItemType.PROFILE,
+                AutofillProfileItemProperties.create(emptyFieldsProfile, /*isSelected=*/false,
+                        /*onClickListener=*/() -> {})));
+
         mModel.set(DETAIL_SCREEN_MODEL_LIST, models);
 
         // Check that the sheet is populated properly.
         ShadowLooper.shadowMainLooper().idle();
-        assertThat(getListItems().getChildCount(), is(2));
+        assertThat(getListItems().getAdapter().getItemCount(), is(3));
 
         assertThatProfileItemLayoutIsCorrectAt(0, sSampleProfile1, /*isSelected=*/false);
         assertThatProfileItemLayoutIsCorrectAt(1, sSampleProfile2, /*isSelected=*/true);
+        assertThatProfileItemLayoutIsCorrectAt(2, emptyFieldsProfile, /*isSelected=*/false);
 
         // Update the selection.
         models.get(0).model.set(AutofillProfileItemProperties.IS_SELECTED, true);
         models.get(1).model.set(AutofillProfileItemProperties.IS_SELECTED, false);
+        models.get(2).model.set(AutofillProfileItemProperties.IS_SELECTED, false);
 
         ShadowLooper.shadowMainLooper().idle();
 
         assertThatProfileItemLayoutIsCorrectAt(0, sSampleProfile1, /*isSelected=*/true);
         assertThatProfileItemLayoutIsCorrectAt(1, sSampleProfile2, /*isSelected=*/false);
+        assertThatProfileItemLayoutIsCorrectAt(2, emptyFieldsProfile, /*isSelected=*/false);
+
+        // Update the selection.
+        models.get(0).model.set(AutofillProfileItemProperties.IS_SELECTED, false);
+        models.get(1).model.set(AutofillProfileItemProperties.IS_SELECTED, false);
+        models.get(2).model.set(AutofillProfileItemProperties.IS_SELECTED, true);
+
+        ShadowLooper.shadowMainLooper().idle();
+
+        assertThatProfileItemLayoutIsCorrectAt(0, sSampleProfile1, /*isSelected=*/false);
+        assertThatProfileItemLayoutIsCorrectAt(1, sSampleProfile2, /*isSelected=*/false);
+        assertThatProfileItemLayoutIsCorrectAt(2, emptyFieldsProfile, /*isSelected=*/true);
+
+        ShadowLooper.shadowMainLooper().idle();
     }
 
     @Test
@@ -223,35 +250,41 @@
                         /*origin=*/"https://example.at", /*name=*/"", /*number=*/"23423423432",
                         /*obfuscatedNumber=*/"34326", /*month=*/"05", /*year=*/"2035",
                         /*issuerIconString=*/"visaCC");
+        FastCheckoutCreditCard sampleCardEmptyFields =
+                FastCheckoutTestUtils.createDetailedCreditCard(/*guid=*/"7534",
+                        /*origin=*/"", /*name=*/"", /*number=*/"",
+                        /*obfuscatedNumber=*/"", /*month=*/"05", /*year=*/"2035",
+                        /*issuerIconString=*/"visaCC");
 
         models.add(new ListItem(DetailItemType.CREDIT_CARD,
-                CreditCardItemProperties.create(sSampleCard1, /*isSelected=*/false,
-                        /*onClickListener=*/() -> {})));
-        models.add(new ListItem(DetailItemType.CREDIT_CARD,
-                CreditCardItemProperties.create(sSampleCard2, /*isSelected=*/true,
+                CreditCardItemProperties.create(sSampleCard1, /*isSelected=*/true,
                         /*onClickListener=*/() -> {})));
         models.add(new ListItem(DetailItemType.CREDIT_CARD,
                 CreditCardItemProperties.create(sampleCardNoName, /*isSelected=*/false,
                         /*onClickListener=*/() -> {})));
+        models.add(new ListItem(DetailItemType.CREDIT_CARD,
+                CreditCardItemProperties.create(sampleCardEmptyFields, /*isSelected=*/false,
+                        /*onClickListener=*/() -> {})));
         mModel.set(DETAIL_SCREEN_MODEL_LIST, models);
 
         // Check that the sheet is populated properly.
         ShadowLooper.shadowMainLooper().idle();
-        assertThat(getListItems().getChildCount(), is(3));
+        assertThat(getListItems().getAdapter().getItemCount(), is(3));
 
-        assertThatCreditCardItemLayoutIsCorrectAt(0, sSampleCard1, /*isSelected=*/false);
-        assertThatCreditCardItemLayoutIsCorrectAt(1, sSampleCard2, /*isSelected=*/true);
-        assertThatCreditCardItemLayoutIsCorrectAt(2, sampleCardNoName, /*isSelected=*/false);
+        assertThatCreditCardItemLayoutIsCorrectAt(0, sSampleCard1, /*isSelected=*/true);
+        assertThatCreditCardItemLayoutIsCorrectAt(1, sampleCardNoName, /*isSelected=*/false);
+        assertThatCreditCardItemLayoutIsCorrectAt(2, sampleCardEmptyFields, /*isSelected=*/false);
 
         // Update the selection.
-        models.get(0).model.set(CreditCardItemProperties.IS_SELECTED, true);
-        models.get(1).model.set(CreditCardItemProperties.IS_SELECTED, false);
+        models.get(0).model.set(CreditCardItemProperties.IS_SELECTED, false);
+        models.get(1).model.set(CreditCardItemProperties.IS_SELECTED, true);
+        models.get(2).model.set(CreditCardItemProperties.IS_SELECTED, false);
 
         ShadowLooper.shadowMainLooper().idle();
 
-        assertThatCreditCardItemLayoutIsCorrectAt(0, sSampleCard1, /*isSelected=*/true);
-        assertThatCreditCardItemLayoutIsCorrectAt(1, sSampleCard2, /*isSelected=*/false);
-        assertThatCreditCardItemLayoutIsCorrectAt(2, sampleCardNoName, /*isSelected=*/false);
+        assertThatCreditCardItemLayoutIsCorrectAt(0, sSampleCard1, /*isSelected=*/false);
+        assertThatCreditCardItemLayoutIsCorrectAt(1, sampleCardNoName, /*isSelected=*/true);
+        assertThatCreditCardItemLayoutIsCorrectAt(2, sampleCardEmptyFields, /*isSelected=*/false);
     }
 
     @Test
@@ -298,25 +331,35 @@
         return textView.getText().toString();
     }
 
+    /** Returns the TextView with resId inside the item at this index. */
+    private TextView getTextViewFromListItemWithId(int index, int resId) {
+        return getListItemAt(index).findViewById(resId);
+    }
+
     /** Asserts that the layout of the profile item at the given index is correct. */
     private void assertThatProfileItemLayoutIsCorrectAt(
             int index, FastCheckoutAutofillProfile profile, boolean isSelected) {
-        assertThat(getTextFromListItemWithId(index, R.id.fast_checkout_autofill_profile_item_name),
-                equalTo(profile.getFullName()));
-        assertThat(getTextFromListItemWithId(
-                           index, R.id.fast_checkout_autofill_profile_item_street_address),
-                equalTo(profile.getStreetAddress()));
-        assertThat(getTextFromListItemWithId(
-                           index, R.id.fast_checkout_autofill_profile_item_city_and_postal_code),
-                equalTo(profile.getLocality() + ", " + profile.getPostalCode()));
-        assertThat(
-                getTextFromListItemWithId(index, R.id.fast_checkout_autofill_profile_item_country),
-                equalTo(profile.getCountryName()));
-        assertThat(getTextFromListItemWithId(index, R.id.fast_checkout_autofill_profile_item_email),
-                equalTo(profile.getEmailAddress()));
-        assertThat(getTextFromListItemWithId(
-                           index, R.id.fast_checkout_autofill_profile_item_phone_number),
-                equalTo(profile.getPhoneNumber()));
+        assertViewShowsTextOrInvisible(
+                getTextViewFromListItemWithId(index, R.id.fast_checkout_autofill_profile_item_name),
+                profile.getFullName());
+        assertViewShowsTextOrInvisible(
+                getTextViewFromListItemWithId(
+                        index, R.id.fast_checkout_autofill_profile_item_street_address),
+                profile.getStreetAddress());
+        assertViewShowsTextOrInvisible(
+                getTextViewFromListItemWithId(
+                        index, R.id.fast_checkout_autofill_profile_item_city_and_postal_code),
+                getLocalityAndPostalCode(profile));
+        assertViewShowsTextOrInvisible(getTextViewFromListItemWithId(index,
+                                               R.id.fast_checkout_autofill_profile_item_country),
+                profile.getCountryName());
+        assertViewShowsTextOrInvisible(getTextViewFromListItemWithId(index,
+                                               R.id.fast_checkout_autofill_profile_item_email),
+                profile.getEmailAddress());
+        assertViewShowsTextOrInvisible(
+                getTextViewFromListItemWithId(
+                        index, R.id.fast_checkout_autofill_profile_item_phone_number),
+                profile.getPhoneNumber());
 
         View icon = getListItemAt(index).findViewById(
                 R.id.fast_checkout_autofill_profile_item_selected_icon);
@@ -329,22 +372,18 @@
     /** Asserts that the layout of the credit card item at the given index is correct. */
     private void assertThatCreditCardItemLayoutIsCorrectAt(
             int index, FastCheckoutCreditCard card, boolean isSelected) {
-        assertThat(getTextFromListItemWithId(index, R.id.fast_checkout_credit_card_item_number),
-                equalTo(card.getObfuscatedNumber()));
+        assertViewShowsTextOrInvisible(
+                getTextViewFromListItemWithId(index, R.id.fast_checkout_credit_card_item_number),
+                card.getObfuscatedNumber());
 
         // The name row should get hidden if the name is empty.
-        TextView nameView =
-                getListItemAt(index).findViewById(R.id.fast_checkout_credit_card_item_name);
-        if (card.getName().isEmpty()) {
-            assertThat(nameView.getVisibility(), is(View.INVISIBLE));
-        } else {
-            assertThat(nameView.getVisibility(), is(View.VISIBLE));
-            assertThat(nameView.getText().toString(), equalTo(card.getName()));
-        }
+        assertViewShowsTextOrInvisible(
+                getListItemAt(index).findViewById(R.id.fast_checkout_credit_card_item_name),
+                card.getName());
 
-        assertThat(getTextFromListItemWithId(
-                           index, R.id.fast_checkout_credit_card_item_expiration_date),
-                equalTo(card.getMonth() + "/" + card.getYear()));
+        assertViewShowsTextOrInvisible(getTextViewFromListItemWithId(index,
+                                               R.id.fast_checkout_credit_card_item_expiration_date),
+                card.getMonth() + "/" + card.getYear());
 
         View icon = getListItemAt(index).findViewById(
                 R.id.fast_checkout_credit_card_item_selected_icon);
@@ -356,4 +395,28 @@
         assertThat(shadowOf(paymentIcon.getDrawable()).getCreatedFromResId(),
                 is(card.getIssuerIconDrawableId()));
     }
+
+    private void assertViewShowsTextOrInvisible(TextView view, String text) {
+        if (text.isEmpty()) {
+            assertThat(view.getVisibility(), is(View.GONE));
+        } else {
+            assertThat(view.getVisibility(), is(View.VISIBLE));
+            assertThat(view.getText().toString(), equalTo(text));
+        }
+    }
+
+    /**
+     * Returns the properly formatted combination of city and postal code. For now,
+     * that means adhering to US formatting.
+     */
+    private static String getLocalityAndPostalCode(FastCheckoutAutofillProfile profile) {
+        StringBuilder builder = new StringBuilder();
+        builder.append(profile.getLocality());
+        // Add divider only if both elements exist.
+        if (!profile.getLocality().isEmpty() && !profile.getPostalCode().isEmpty()) {
+            builder.append(", ");
+        }
+        builder.append(profile.getPostalCode());
+        return builder.toString();
+    }
 }
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc
index bc23e61b..f900d654 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.cc
@@ -77,6 +77,17 @@
   return model_->GetColorsModel();
 }
 
+void ReadAnythingController::OnLineSpacingChanged(int new_index) {
+  if (!model_->GetLineSpacingModel()->IsValidLineSpacingIndex(new_index))
+    return;
+
+  model_->SetSelectedLineSpacingByIndex(new_index);
+}
+
+ui::ComboboxModel* ReadAnythingController::GetLineSpacingModel() {
+  return model_->GetLineSpacingModel();
+}
+
 void ReadAnythingController::OnLetterSpacingChanged(int new_index) {
   if (!model_->GetLetterSpacingModel()->IsValidLetterSpacingIndex(new_index))
     return;
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h
index 49e35bf..c28776e 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller.h
@@ -60,6 +60,8 @@
   void OnFontSizeChanged(bool increase) override;
   void OnColorsChanged(int new_index) override;
   ui::ComboboxModel* GetColorsModel() override;
+  void OnLineSpacingChanged(int new_index) override;
+  ui::ComboboxModel* GetLineSpacingModel() override;
   void OnLetterSpacingChanged(int new_index) override;
   ui::ComboboxModel* GetLetterSpacingModel() override;
 
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller_unittest.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller_unittest.cc
index b371f1c..e929338 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller_unittest.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_controller_unittest.cc
@@ -32,7 +32,7 @@
         (int)read_anything::mojom::Colors::kDefaultValue);
     browser()->profile()->GetPrefs()->SetInteger(
         prefs::kAccessibilityReadAnythingLetterSpacing,
-        (int)read_anything::mojom::LetterSpacing::kDefaultValue);
+        (int)read_anything::mojom::Spacing::kDefault);
   }
 
   void MockOnFontChoiceChanged(int index) {
@@ -45,6 +45,10 @@
 
   void MockOnColorsChanged(int index) { controller_->OnColorsChanged(index); }
 
+  void MockOnLineSpacingChanged(int index) {
+    controller_->OnLineSpacingChanged(index);
+  }
+
   void MockOnLetterSpacingChanged(int index) {
     controller_->OnLetterSpacingChanged(index);
   }
@@ -52,8 +56,9 @@
   void MockModelInit(std::string font_name,
                      double font_scale,
                      read_anything::mojom::Colors colors,
-                     read_anything::mojom::LetterSpacing letter_spacing) {
-    model_->Init(font_name, font_scale, colors, letter_spacing);
+                     read_anything::mojom::Spacing line_spacing,
+                     read_anything::mojom::Spacing letter_spacing) {
+    model_->Init(font_name, font_scale, colors, line_spacing, letter_spacing);
   }
 
   std::string GetPrefFontName() {
@@ -109,9 +114,9 @@
   EXPECT_NEAR(GetPrefFontScale(), 1.0, 0.01);
 
   std::string font_name;
-
   MockModelInit(font_name, 4.5, read_anything::mojom::Colors::kDefaultValue,
-                read_anything::mojom::LetterSpacing::kDefaultValue);
+                read_anything::mojom::Spacing::kDefault,
+                read_anything::mojom::Spacing::kDefault);
 
   MockOnFontSizeChanged(true);
 
@@ -122,9 +127,9 @@
   EXPECT_NEAR(GetPrefFontScale(), 1.0, 0.01);
 
   std::string font_name;
-
   MockModelInit(font_name, 0.5, read_anything::mojom::Colors::kDefaultValue,
-                read_anything::mojom::LetterSpacing::kDefaultValue);
+                read_anything::mojom::Spacing::kDefault,
+                read_anything::mojom::Spacing::kDefault);
 
   MockOnFontSizeChanged(false);
 
@@ -142,7 +147,7 @@
 TEST_F(ReadAnythingControllerTest, OnLetterSpacingChangedUpdatesPref) {
   EXPECT_EQ(GetPrefsLetterSpacing(), 1);
 
-  MockOnLetterSpacingChanged((int)read_anything::mojom::LetterSpacing::kLoose);
+  MockOnLetterSpacingChanged((int)read_anything::mojom::Spacing::kLoose);
 
   EXPECT_EQ(GetPrefsLetterSpacing(), 2);
 }
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
index b476b564..b42be3e 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_coordinator.cc
@@ -50,8 +50,12 @@
       browser->profile()->GetPrefs()->GetInteger(
           prefs::kAccessibilityReadAnythingColorInfo));
 
-  read_anything::mojom::LetterSpacing prefs_letter_spacing;
-  prefs_letter_spacing = static_cast<read_anything::mojom::LetterSpacing>(
+  read_anything::mojom::Spacing prefs_line_spacing;
+  // TODO Get pref from browser profile (future CL)
+  prefs_line_spacing = read_anything::mojom::Spacing::kDefault;
+
+  read_anything::mojom::Spacing prefs_letter_spacing;
+  prefs_letter_spacing = static_cast<read_anything::mojom::Spacing>(
       browser->profile()->GetPrefs()->GetInteger(
           prefs::kAccessibilityReadAnythingLetterSpacing));
 
@@ -59,6 +63,7 @@
       /* font name = */ prefs_font_name,
       /* font scale = */ prefs_font_scale,
       /* colors = */ prefs_colors,
+      /* line spacing = */ prefs_line_spacing,
       /* letter spacing = */ prefs_letter_spacing);
 }
 
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.cc
index ca24ad79..c668853 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.cc
@@ -20,28 +20,29 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
 
 using read_anything::mojom::ReadAnythingTheme;
+using read_anything::mojom::Spacing;
 
 ReadAnythingModel::ReadAnythingModel()
     : font_name_(kReadAnythingDefaultFontName),
       font_scale_(kReadAnythingDefaultFontScale),
       font_model_(std::make_unique<ReadAnythingFontModel>()),
       colors_model_(std::make_unique<ReadAnythingColorsModel>()),
+      lines_model_(std::make_unique<ReadAnythingLineSpacingModel>()),
       letter_spacing_model_(
           std::make_unique<ReadAnythingLetterSpacingModel>()) {}
 
 ReadAnythingModel::~ReadAnythingModel() = default;
 
-void ReadAnythingModel::Init(
-    std::string& font_name,
-    double font_scale,
-    read_anything::mojom::Colors colors,
-    read_anything::mojom::LetterSpacing letter_spacing) {
+void ReadAnythingModel::Init(std::string& font_name,
+                             double font_scale,
+                             read_anything::mojom::Colors colors,
+                             Spacing line_spacing,
+                             Spacing letter_spacing) {
   // If this profile has previously selected choices that were saved to
   // prefs, check they are still a valid, and then assign if so.
   if (font_model_->IsValidFontName(font_name)) {
@@ -49,17 +50,17 @@
     font_name_ = font_name;
   }
 
-  size_t letter_spacing_index = static_cast<size_t>((int)letter_spacing);
+  size_t letter_spacing_index = static_cast<size_t>(letter_spacing);
   if (letter_spacing_model_->IsValidLetterSpacingIndex(letter_spacing_index)) {
     letter_spacing_model_->SetDefaultLetterSpacingIndexFromPref(
         letter_spacing_index);
     letter_spacing_ =
-        (int)(letter_spacing_model_->GetLetterSpacingAt(letter_spacing_index));
+        letter_spacing_model_->GetLetterSpacingAt(letter_spacing_index);
   }
 
   font_scale_ = font_scale;
 
-  size_t colors_index = static_cast<size_t>((int)colors);
+  size_t colors_index = static_cast<size_t>(colors);
   if (colors_model_->IsValidColorsIndex(colors_index)) {
     colors_model_->SetDefaultColorsIndexFromPref(colors_index);
   }
@@ -68,6 +69,11 @@
       colors_model_->GetColorsAt(colors_model_->GetStartingStateIndex());
   foreground_color_ = initial_colors.foreground;
   background_color_ = initial_colors.background;
+
+  size_t line_spacing_index = static_cast<size_t>(line_spacing);
+  if (lines_model_->IsValidLineSpacingIndex(line_spacing_index)) {
+    line_spacing_ = lines_model_->GetLineSpacingAt(line_spacing_index);
+  }
 }
 
 void ReadAnythingModel::AddObserver(Observer* obs) {
@@ -99,11 +105,19 @@
   NotifyThemeChanged();
 }
 
+void ReadAnythingModel::SetSelectedLineSpacingByIndex(size_t new_index) {
+  // Check that the index is valid.
+  DCHECK(lines_model_->IsValidLineSpacingIndex(new_index));
+
+  line_spacing_ = lines_model_->GetLineSpacingAt(new_index);
+  NotifyThemeChanged();
+}
+
 void ReadAnythingModel::SetSelectedLetterSpacingByIndex(size_t new_index) {
   // Check that the index is valid.
   DCHECK(letter_spacing_model_->IsValidLetterSpacingIndex(new_index));
 
-  letter_spacing_ = (int)(letter_spacing_model_->GetLetterSpacingAt(new_index));
+  letter_spacing_ = letter_spacing_model_->GetLetterSpacingAt(new_index);
   NotifyThemeChanged();
 }
 
@@ -143,9 +157,9 @@
 
 void ReadAnythingModel::NotifyThemeChanged() {
   for (Observer& obs : observers_) {
-    obs.OnReadAnythingThemeChanged(
-        ReadAnythingTheme::New(font_name_, font_scale_, foreground_color_,
-                               background_color_, letter_spacing_));
+    obs.OnReadAnythingThemeChanged(ReadAnythingTheme::New(
+        font_name_, font_scale_, foreground_color_, background_color_,
+        line_spacing_, letter_spacing_));
   }
 }
 
@@ -295,18 +309,88 @@
 ReadAnythingColorsModel::~ReadAnythingColorsModel() = default;
 
 ///////////////////////////////////////////////////////////////////////////////
+// ReadAnythingLineSpacingModel
+///////////////////////////////////////////////////////////////////////////////
+
+ReadAnythingLineSpacingModel::ReadAnythingLineSpacingModel() {
+  // Define the line spacing options available to the user.
+  lines_choices_.emplace_back(Spacing::kTight);
+  lines_choices_.emplace_back(Spacing::kDefault);
+  lines_choices_.emplace_back(Spacing::kLoose);
+  lines_choices_.emplace_back(Spacing::kVeryLoose);
+  lines_choices_.shrink_to_fit();
+}
+
+bool ReadAnythingLineSpacingModel::IsValidLineSpacingIndex(size_t index) {
+  return index < GetItemCount();
+}
+
+void ReadAnythingLineSpacingModel::SetDefaultLineSpacingIndexFromPref(
+    size_t index) {
+  default_index_ = index;
+}
+
+Spacing ReadAnythingLineSpacingModel::GetLineSpacingAt(size_t index) {
+  DCHECK_LT(index, GetItemCount());
+
+  return lines_choices_[index];
+}
+
+absl::optional<size_t> ReadAnythingLineSpacingModel::GetDefaultIndex() const {
+  return default_index_;
+}
+
+size_t ReadAnythingLineSpacingModel::GetItemCount() const {
+  return lines_choices_.size();
+}
+
+ui::ImageModel ReadAnythingLineSpacingModel::GetIconAt(size_t index) const {
+  // The dropdown should always show the line spacing icon.
+  return ui::ImageModel::FromImageSkia(gfx::CreateVectorIcon(
+      kLineSpacingIcon, kColorsIconSize, gfx::kPlaceholderColor));
+}
+
+ui::ImageModel ReadAnythingLineSpacingModel::GetDropDownIconAt(
+    size_t index) const {
+  return ui::ImageModel();
+}
+
+std::u16string ReadAnythingLineSpacingModel::GetItemAt(size_t index) const {
+  // Only display the icon in the toolbar, so return empty string here.
+  return std::u16string();
+}
+
+std::u16string ReadAnythingLineSpacingModel::GetLineSpacingName(
+    Spacing line_spacing) const {
+  switch (line_spacing) {
+    case Spacing::kTight:
+      return u"Tight";
+    case Spacing::kDefault:
+      return u"Default";
+    case Spacing::kLoose:
+      return u"Loose";
+    case Spacing::kVeryLoose:
+      return u"Very Loose";
+  }
+}
+
+std::u16string ReadAnythingLineSpacingModel::GetDropDownTextAt(
+    size_t index) const {
+  DCHECK_LT(index, GetItemCount());
+  return GetLineSpacingName(lines_choices_[index]);
+}
+
+ReadAnythingLineSpacingModel::~ReadAnythingLineSpacingModel() = default;
+
+///////////////////////////////////////////////////////////////////////////////
 // ReadAnythingLetterSpacingModel
 ///////////////////////////////////////////////////////////////////////////////
 
 ReadAnythingLetterSpacingModel::ReadAnythingLetterSpacingModel() {
-  letter_spacing_choices_.emplace_back(
-      read_anything::mojom::LetterSpacing::kTight);
-  letter_spacing_choices_.emplace_back(
-      read_anything::mojom::LetterSpacing::kDefault);
-  letter_spacing_choices_.emplace_back(
-      read_anything::mojom::LetterSpacing::kLoose);
-  letter_spacing_choices_.emplace_back(
-      read_anything::mojom::LetterSpacing::kVeryLoose);
+  letter_spacing_choices_.emplace_back(Spacing::kTight);
+  letter_spacing_choices_.emplace_back(Spacing::kDefault);
+  letter_spacing_choices_.emplace_back(Spacing::kLoose);
+  letter_spacing_choices_.emplace_back(Spacing::kVeryLoose);
   letter_spacing_choices_.shrink_to_fit();
 }
 
@@ -327,26 +411,25 @@
   return letter_spacing_choices_.size();
 }
 
-read_anything::mojom::LetterSpacing
-ReadAnythingLetterSpacingModel::GetLetterSpacingAt(size_t index) {
+Spacing ReadAnythingLetterSpacingModel::GetLetterSpacingAt(size_t index) {
   return letter_spacing_choices_[index];
 }
 
 // TODO (crbug.com/1266555): Change to translatable messages
 std::u16string ReadAnythingLetterSpacingModel::GetLetterSpacingName(
-    read_anything::mojom::LetterSpacing letter_spacing) const {
+    Spacing letter_spacing) const {
   int label;
   switch (letter_spacing) {
-    case read_anything::mojom::LetterSpacing::kTight:
+    case Spacing::kTight:
       label = IDS_READ_ANYTHING_SPACING_COMBOBOX_TIGHT;
       break;
-    case read_anything::mojom::LetterSpacing::kDefault:
+    case Spacing::kDefault:
       label = IDS_READ_ANYTHING_SPACING_COMBOBOX_DEFAULT;
       break;
-    case read_anything::mojom::LetterSpacing::kLoose:
+    case Spacing::kLoose:
       label = IDS_READ_ANYTHING_SPACING_COMBOBOX_LOOSE;
       break;
-    case read_anything::mojom::LetterSpacing::kVeryLoose:
+    case Spacing::kVeryLoose:
       label = IDS_READ_ANYTHING_SPACING_COMBOBOX_VERY_LOOSE;
   }
   return l10n_util::GetStringUTF16(label);
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.h
index be3fe1a..69fafd41 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_model.h
@@ -105,6 +105,49 @@
   size_t default_index_ = 0;
 };
 
+//////////////////////////////////////////////////////////////////////////////
+// ReadAnythingLineSpacingModel
+//
+//  A class that stores the data for the colors combobox.
+//  This class is owned by the ReadAnythingModel and has the same lifetime as
+//  the browser.
+//
+class ReadAnythingLineSpacingModel : public ui::ComboboxModel {
+ public:
+  ReadAnythingLineSpacingModel();
+  ReadAnythingLineSpacingModel(const ReadAnythingLineSpacingModel&) = delete;
+  ReadAnythingLineSpacingModel& operator=(const ReadAnythingLineSpacingModel&) =
+      delete;
+  ~ReadAnythingLineSpacingModel() override;
+
+  bool IsValidLineSpacingIndex(size_t index);
+  void SetDefaultLineSpacingIndexFromPref(size_t index);
+  read_anything::mojom::Spacing GetLineSpacingAt(size_t index);
+
+  // Simple pass-through method so Init can set the starting state.
+  size_t GetStartingStateIndex() { return GetDefaultIndex().value(); }
+
+ protected:
+  // ui::Combobox implementation:
+  absl::optional<size_t> GetDefaultIndex() const override;
+  size_t GetItemCount() const override;
+  std::u16string GetItemAt(size_t index) const override;
+  ui::ImageModel GetIconAt(size_t index) const override;
+  ui::ImageModel GetDropDownIconAt(size_t index) const override;
+  std::u16string GetDropDownTextAt(size_t index) const override;
+
+ private:
+  // Names for the drop down options in front-end.
+  std::vector<read_anything::mojom::Spacing> lines_choices_;
+
+  // Default index for drop down, either one or populated from prefs.
+  size_t default_index_ = 1;
+
+  // Display names for line spacing choices
+  std::u16string GetLineSpacingName(
+      read_anything::mojom::Spacing line_spacing) const;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // ReadAnythingLetterSpacingModel
 //
@@ -123,7 +166,7 @@
 
   bool IsValidLetterSpacingIndex(size_t index);
   void SetDefaultLetterSpacingIndexFromPref(size_t index);
-  read_anything::mojom::LetterSpacing GetLetterSpacingAt(size_t index);
+  read_anything::mojom::Spacing GetLetterSpacingAt(size_t index);
 
  protected:
   // ui::Combobox implementation:
@@ -136,7 +179,7 @@
 
  private:
   // Letter spacing choices for the drop down options in front-end.
-  std::vector<read_anything::mojom::LetterSpacing> letter_spacing_choices_;
+  std::vector<read_anything::mojom::Spacing> letter_spacing_choices_;
 
   // Default index for drop down, either one (normal spacing) or populated from
   // prefs.
@@ -144,7 +187,7 @@
 
   // Display names for each letter spacing choice
   std::u16string GetLetterSpacingName(
-      read_anything::mojom::LetterSpacing letter_spacing) const;
+      read_anything::mojom::Spacing letter_spacing) const;
 };
 ///////////////////////////////////////////////////////////////////////////////
 // ReadAnythingModel
@@ -172,7 +215,8 @@
   void Init(std::string& font_name,
             double font_scale,
             read_anything::mojom::Colors colors,
-            read_anything::mojom::LetterSpacing letter_spacing);
+            read_anything::mojom::Spacing line_spacing,
+            read_anything::mojom::Spacing letter_spacing);
 
   void AddObserver(Observer* obs);
   void RemoveObserver(Observer* obs);
@@ -184,11 +228,15 @@
   void DecreaseTextSize();
   void IncreaseTextSize();
   void SetSelectedColorsByIndex(size_t new_index);
+  void SetSelectedLineSpacingByIndex(size_t new_index);
   void SetSelectedLetterSpacingByIndex(size_t new_index);
 
   ReadAnythingFontModel* GetFontModel() { return font_model_.get(); }
   double GetFontScale() { return font_scale_; }
   ReadAnythingColorsModel* GetColorsModel() { return colors_model_.get(); }
+  ReadAnythingLineSpacingModel* GetLineSpacingModel() {
+    return lines_model_.get();
+  }
   ReadAnythingLetterSpacingModel* GetLetterSpacingModel() {
     return letter_spacing_model_.get();
   }
@@ -207,7 +255,8 @@
   // A scale multiplier for font size (internal use only, not shown to user).
   float font_scale_;
 
-  int letter_spacing_;
+  read_anything::mojom::Spacing line_spacing_;
+  read_anything::mojom::Spacing letter_spacing_;
 
   // TODO(crbug.com/1266555): Use |snapshot_| and |content_node_ids_| to keep
   // scrolls in sync.
@@ -217,6 +266,7 @@
   base::ObserverList<Observer> observers_;
   const std::unique_ptr<ReadAnythingFontModel> font_model_;
   const std::unique_ptr<ReadAnythingColorsModel> colors_model_;
+  const std::unique_ptr<ReadAnythingLineSpacingModel> lines_model_;
   const std::unique_ptr<ReadAnythingLetterSpacingModel> letter_spacing_model_;
 };
 
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_model_unittest.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_model_unittest.cc
index a39b804..d86a793 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_model_unittest.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_model_unittest.cc
@@ -135,6 +135,14 @@
   model_->SetSelectedColorsByIndex(2);
 }
 
+TEST_F(ReadAnythingModelTest, NotificationsOnSetSelectedLineSpacingIndex) {
+  model_->AddObserver(&model_observer_1_);
+
+  EXPECT_CALL(model_observer_1_, OnReadAnythingThemeChanged(_)).Times(1);
+
+  model_->SetSelectedLineSpacingByIndex(2);
+}
+
 TEST_F(ReadAnythingModelTest, NotificationsOnSetSelectedLetterSpacingIndex) {
   model_->AddObserver(&model_observer_1_);
 
@@ -146,7 +154,8 @@
 TEST_F(ReadAnythingModelTest, MinimumFontScaleIsEnforced) {
   std::string font_name;
   model_->Init(font_name, 0.5, read_anything::mojom::Colors::kDefaultValue,
-               read_anything::mojom::LetterSpacing::kDefaultValue);
+               read_anything::mojom::Spacing::kDefault,
+               read_anything::mojom::Spacing::kDefault);
   model_->DecreaseTextSize();
   EXPECT_NEAR(model_->GetFontScale(), 0.5, 0.01);
 }
@@ -154,7 +163,8 @@
 TEST_F(ReadAnythingModelTest, MaximumFontScaleIsEnforced) {
   std::string font_name;
   model_->Init(font_name, 4.5, read_anything::mojom::Colors::kDefaultValue,
-               read_anything::mojom::LetterSpacing::kDefaultValue);
+               read_anything::mojom::Spacing::kDefault,
+               read_anything::mojom::Spacing::kDefault);
   model_->IncreaseTextSize();
   EXPECT_NEAR(model_->GetFontScale(), 4.5, 0.01);
 }
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.cc
index ecb9e468..dea367d 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.cc
@@ -76,6 +76,18 @@
   colors_combobox->SetShouldShowArrow(false);
   colors_combobox->SetBorderColorId(ui::kColorSidePanelComboboxBorder);
 
+  // Create line spacing combobox
+  auto lines_combobox = std::make_unique<views::Combobox>();
+  lines_combobox->SetModel(delegate_->GetLineSpacingModel());
+  lines_combobox->SetTooltipTextAndAccessibleName(
+      l10n_util::GetStringUTF16(IDS_READ_ANYTHING_LINE_SPACING_COMBOBOX_LABEL));
+  lines_combobox->SetSizeToLargestLabel(true);
+  lines_combobox->SetCallback(
+      base::BindRepeating(&ReadAnythingToolbarView::ChangeLineSpacingCallback,
+                          weak_pointer_factory_.GetWeakPtr()));
+  lines_combobox->SetShouldShowArrow(false);
+  lines_combobox->SetBorderColorId(ui::kColorSidePanelComboboxBorder);
+
   // Create letter spacing selection combobox.
   auto letter_spacing_combobox = std::make_unique<views::Combobox>();
   letter_spacing_combobox->SetModel(delegate_->GetLetterSpacingModel());
@@ -96,6 +108,7 @@
   increase_text_size_button_ = AddChildView(std::move(increase_size_button));
   AddChildView(Separator());
   colors_combobox_ = AddChildView(std::move(colors_combobox));
+  lines_combobox_ = AddChildView(std::move(lines_combobox));
   letter_spacing_combobox_ = AddChildView(std::move(letter_spacing_combobox));
 
   // Start observing model after views creation so initial theme is applied.
@@ -118,6 +131,12 @@
         colors_combobox_->GetSelectedIndex().value_or(0));
 }
 
+void ReadAnythingToolbarView::ChangeLineSpacingCallback() {
+  if (delegate_)
+    delegate_->OnLineSpacingChanged(
+        lines_combobox_->GetSelectedIndex().value_or(1));
+}
+
 void ReadAnythingToolbarView::ChangeLetterSpacingCallback() {
   if (delegate_)
     delegate_->OnLetterSpacingChanged(
@@ -138,6 +157,8 @@
       views::CreateSolidBackground(new_theme->background_color));
   colors_combobox_->SetBackground(
       views::CreateSolidBackground(new_theme->background_color));
+  lines_combobox_->SetBackground(
+      views::CreateSolidBackground(new_theme->background_color));
   letter_spacing_combobox_->SetBackground(
       views::CreateSolidBackground(new_theme->background_color));
 
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.h b/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.h
index 42efb2a..c2b4520 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.h
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view.h
@@ -31,6 +31,8 @@
     virtual void OnFontSizeChanged(bool increase) = 0;
     virtual void OnColorsChanged(int new_index) = 0;
     virtual ui::ComboboxModel* GetColorsModel() = 0;
+    virtual void OnLineSpacingChanged(int new_index) = 0;
+    virtual ui::ComboboxModel* GetLineSpacingModel() = 0;
     virtual void OnLetterSpacingChanged(int new_index) = 0;
     virtual ui::ComboboxModel* GetLetterSpacingModel() = 0;
   };
@@ -56,6 +58,7 @@
   void DecreaseFontSizeCallback();
   void IncreaseFontSizeCallback();
   void ChangeColorsCallback();
+  void ChangeLineSpacingCallback();
   void ChangeLetterSpacingCallback();
 
   // views::View:
@@ -67,6 +70,7 @@
   raw_ptr<ReadAnythingButtonView> decrease_text_size_button_;
   raw_ptr<ReadAnythingButtonView> increase_text_size_button_;
   raw_ptr<views::Combobox> colors_combobox_;
+  raw_ptr<views::Combobox> lines_combobox_;
   raw_ptr<views::Combobox> letter_spacing_combobox_;
 
   raw_ptr<ReadAnythingToolbarView::Delegate> delegate_;
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view_browsertest.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view_browsertest.cc
index a38a80e6..49807ac 100644
--- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_toolbar_view_browsertest.cc
@@ -19,6 +19,8 @@
   MOCK_METHOD(void, OnFontSizeChanged, (bool increase), (override));
   MOCK_METHOD(void, OnColorsChanged, (int new_index), (override));
   MOCK_METHOD(ui::ComboboxModel*, GetColorsModel, (), (override));
+  MOCK_METHOD(void, OnLineSpacingChanged, (int new_index), (override));
+  MOCK_METHOD(ui::ComboboxModel*, GetLineSpacingModel, (), (override));
   MOCK_METHOD(void, OnLetterSpacingChanged, (int new_index), (override));
   MOCK_METHOD(ui::ComboboxModel*, GetLetterSpacingModel, (), (override));
 };
@@ -71,6 +73,10 @@
 
   void ChangeColorsCallback() { toolbar_view_->ChangeColorsCallback(); }
 
+  void ChangeLineSpacingCallback() {
+    toolbar_view_->ChangeLineSpacingCallback();
+  }
+
   void ChangeLetterSpacingCallback() {
     toolbar_view_->ChangeLetterSpacingCallback();
   }
@@ -104,6 +110,12 @@
   ChangeColorsCallback();
 }
 
+IN_PROC_BROWSER_TEST_F(ReadAnythingToolbarViewTest, ChangeLineSpacingCallback) {
+  EXPECT_CALL(toolbar_delegate_, OnLineSpacingChanged(1)).Times(1);
+
+  ChangeLineSpacingCallback();
+}
+
 IN_PROC_BROWSER_TEST_F(ReadAnythingToolbarViewTest,
                        ChangeLetterSpacingCallback) {
   EXPECT_CALL(toolbar_delegate_, OnLetterSpacingChanged(1)).Times(1);
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index bb545046..6aec26f 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -158,6 +158,17 @@
   helper_.CheckAppInListWindowed(Site::kScreenshots);
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckBrowserNavigation) {
+  helper_.CreateShortcut(Site::kStandalone, WindowOptions::kBrowser);
+  helper_.CheckBrowserNavigation(Site::kStandalone);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppIntegration, CheckBrowserNavigationFails) {
+  helper_.CreateShortcut(Site::kStandaloneNestedA, WindowOptions::kBrowser);
+  EXPECT_NONFATAL_FAILURE(helper_.CheckBrowserNavigation(Site::kStandalone),
+                          "webapps_integration/standalone/foo/basic.html");
+}
+
 // Generated tests:
 
 IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index 7638e6b1..a7a70b60 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -1999,6 +1999,15 @@
   AfterStateCheckAction();
 }
 
+void WebAppIntegrationTestDriver::CheckBrowserNavigation(Site site) {
+  if (!BeforeStateCheckAction(__FUNCTION__))
+    return;
+  ASSERT_TRUE(browser());
+  GURL url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
+  EXPECT_EQ(url, GetUrlForSite(site));
+  AfterStateCheckAction();
+}
+
 void WebAppIntegrationTestDriver::CheckBrowserNavigationIsAppSettings(
     Site site) {
 #if !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index 452c0b6..fd554b73 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -277,6 +277,7 @@
   void CheckAppInListTabbed(Site site);
   void CheckAppNavigation(Site site);
   void CheckAppNavigationIsStartUrl();
+  void CheckBrowserNavigation(Site site);
   void CheckBrowserNavigationIsAppSettings(Site site);
   void CheckAppNotInList(Site site);
   void CheckAppIcon(Site site, Color color);
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc
index 44d03ef..40c0d63 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/password_change_handler.cc
@@ -47,19 +47,15 @@
     const base::Value::List& params) {
   const base::Value& old_passwords = params[0];
   const base::Value& new_passwords = params[1];
-  VLOG(4) << "Scraped " << old_passwords.GetListDeprecated().size()
-          << " old passwords";
-  VLOG(4) << "Scraped " << new_passwords.GetListDeprecated().size()
-          << " new passwords";
+  VLOG(4) << "Scraped " << old_passwords.GetList().size() << " old passwords";
+  VLOG(4) << "Scraped " << new_passwords.GetList().size() << " new passwords";
 
-  const std::string old_password =
-      (old_passwords.GetListDeprecated().size() > 0)
-          ? old_passwords.GetListDeprecated()[0].GetString()
-          : "";
-  const std::string new_password =
-      (new_passwords.GetListDeprecated().size() == 1)
-          ? new_passwords.GetListDeprecated()[0].GetString()
-          : "";
+  const std::string old_password = (old_passwords.GetList().size() > 0)
+                                       ? old_passwords.GetList()[0].GetString()
+                                       : "";
+  const std::string new_password = (new_passwords.GetList().size() == 1)
+                                       ? new_passwords.GetList()[0].GetString()
+                                       : "";
 
   InSessionPasswordChangeManager::Get()->OnSamlPasswordChanged(old_password,
                                                                new_password);
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 1984be4..a50fd235 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -17,6 +17,7 @@
 #include "base/feature_list.h"
 #include "base/i18n/message_formatter.h"
 #include "base/i18n/number_formatting.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
@@ -1032,6 +1033,8 @@
 void SiteSettingsHandler::HandleGetAllSites(const base::Value::List& args) {
   AllowJavascript();
 
+  request_started_time_ = base::TimeTicks::Now();
+
   CHECK_EQ(1U, args.size());
   std::string callback_id = args[0].GetString();
 
@@ -1223,6 +1226,12 @@
 
 void SiteSettingsHandler::OnStorageFetched() {
   AllowJavascript();
+
+  // Record how long does it take to fetch the storage and return complete
+  // information to the UI.
+  DCHECK(!request_started_time_.is_null());
+  base::UmaHistogramTimes("WebsiteSettings.GetAllSitesLoadTime",
+                          base::TimeTicks::Now() - request_started_time_);
   FireWebUIListener("onStorageListFetched",
                     PopulateCookiesAndUsageData(profile_));
 }
@@ -2020,6 +2029,7 @@
     std::unique_ptr<CookiesTreeModel> cookies_tree_model) {
   cookies_tree_model_ = std::move(cookies_tree_model);
   tree_model_set_for_testing_ = true;
+  request_started_time_ = base::TimeTicks::Now();
 }
 
 void SiteSettingsHandler::ClearAllSitesMapForTesting() {
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index 9b6064a..7995117 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -361,6 +361,10 @@
 
   // Whether to send site detail data on cookie tree model update.
   bool update_site_details_ = false;
+
+  // Time when all sites list was requested. Used to record metrics on how long
+  // does it take to fetch storage.
+  base::TimeTicks request_started_time_;
 };
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc
index 2faf723..f3db09c3 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.cc
@@ -43,7 +43,7 @@
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterIntegerPref(
       prefs::kAccessibilityReadAnythingLetterSpacing,
-      (int)read_anything::mojom::LetterSpacing::kDefaultValue,
+      (int)read_anything::mojom::Spacing::kDefaultValue,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 }
 
diff --git a/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h b/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
index f199703b2..45720b1 100644
--- a/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
+++ b/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
@@ -23,7 +23,7 @@
 class WebUIMessageHandler;
 }
 
-// A tab-modal dialog to ask the user to confirm his email before signing in.
+// A tab-modal dialog to ask the user to confirm their email before signing in.
 class SigninEmailConfirmationDialog : public ui::WebDialogDelegate,
                                       public SigninViewControllerDelegate {
  public:
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 4eea11c..640393b 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1664857638-4c1b0a36ea397a1ae2151d06d18fc74455377550.profdata
+chrome-linux-main-1664881835-5d7cd6055354b19eef48e95dfac92b65af747ae1.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index cefff89..a797ee3 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1664841456-3da53ffb56c67b1b7a8a3c50a5ec0546f638715b.profdata
+chrome-mac-main-1664857638-c57aaf35f44486c9583ff1cb20b2c8d810b8acb9.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index e58ebff..321b01e5 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1664841456-6e91453cac2e734626ffc2b01a75c5cf0f56a190.profdata
+chrome-win32-main-1664857638-cf9ce713fb7b36aa64a895e474ebc980474ffbbc.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 63be3125..3a3daa5 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1664841456-9235d616af2d3484228f317fc208f8f5a271db3f.profdata
+chrome-win64-main-1664857638-03f4d9e49a57b4ccc243d0d256e19c6e33798b60.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index c7157a9..1b2aa02 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -583,7 +583,6 @@
   ]
 
   deps = [
-    ":channel_info",
     ":version_header",
     "//base",
     "//base/third_party/dynamic_annotations",
@@ -626,6 +625,7 @@
     public_deps += [ "//chromeos/crosapi/mojom" ]
   } else if (is_linux || is_chromeos) {
     sources += [ "chrome_paths_linux.cc" ]
+    deps += [ ":channel_info" ]
   }
   if (is_mac) {
     sources += [ "chrome_paths_mac.mm" ]
diff --git a/chrome/common/accessibility/read_anything.mojom b/chrome/common/accessibility/read_anything.mojom
index 9888eb8..16cb97b 100644
--- a/chrome/common/accessibility/read_anything.mojom
+++ b/chrome/common/accessibility/read_anything.mojom
@@ -21,8 +21,11 @@
   skia.mojom.SkColor foreground_color;
   skia.mojom.SkColor background_color;
 
+  // The enum value of the user's line spacing choice
+  read_anything.mojom.Spacing line_spacing;
+
   // The enum value of the user's letter spacing choice.
-  int32 letter_spacing;
+  read_anything.mojom.Spacing letter_spacing;
 };
 
 // Used by the WebUI page to bootstrap bidirectional communication.
@@ -42,8 +45,9 @@
   kYellow = 3,
 };
 
-[Extensible, Stable, Uuid="2B5C793A-D81E-4C76-8CDF-695C7E0A30E2"]
-enum LetterSpacing {
+[Extensible, Stable, Uuid="2B5C793A-D81E-4C76-8CDF-695C7E0A30E2",
+ RenamedFrom="read_anything.mojom.LetterSpacing"]
+enum Spacing {
   kTight = 0,
   [Default]kDefault = 1,
   kLoose = 2,
diff --git a/chrome/common/chromeos/extensions/api/telemetry.idl b/chrome/common/chromeos/extensions/api/telemetry.idl
index 79e029f..ce42bd224 100644
--- a/chrome/common/chromeos/extensions/api/telemetry.idl
+++ b/chrome/common/chromeos/extensions/api/telemetry.idl
@@ -167,6 +167,62 @@
 
   callback StatefulPartitionInfoCallback = void (StatefulPartitionInfo statefulPartitionInfo);
 
+  enum TpmGSCVersion {
+    not_gsc,
+    cr50,
+    ti50
+  };
+
+  dictionary TpmVersion {
+    // GSC version.
+    TpmGSCVersion? gscVersion;
+    // TPM family. We use the TPM 2.0 style encoding, e.g.:
+    //  * TPM 1.2: "1.2" -> 0x312e3200
+    //  * TPM 2.0: "2.0" -> 0x322e3000
+    long? family;
+    // TPM spec level.
+    double? specLevel;
+    // Manufacturer code.
+    long? manufacturer;
+    // TPM model number.
+    long? tpmModel;
+    // Firmware version.
+    double? firmwareVersion;
+    // Vendor specific information.
+    DOMString? vendorSpecific;
+  };
+
+  dictionary TpmStatus {
+    // Whether a TPM is enabled on the system.
+    boolean? enabled;
+    // Whether the TPM has been owned.
+    boolean? owned;
+    // Whether the owner password is still retained.
+    boolean? ownerPasswordIsPresent;
+  };
+
+  dictionary TpmDictionaryAttack {
+    // The current dictionary attack counter value.
+    long? counter;
+    // The current dictionary attack counter threshold.
+    long? threshold;
+    // Whether the TPM is in some form of dictionary attack lockout.
+    boolean? lockoutInEffect;
+    // The number of seconds remaining in the lockout.
+    long? lockoutSecondsRemaining;
+  };
+
+  dictionary TpmInfo {
+    // TPM version related information.
+    TpmVersion version;
+    // TPM status related information.
+    TpmStatus status;
+    // TPM dictionary attack (DA) related information.
+    TpmDictionaryAttack dictionaryAttack;
+  };
+
+  callback TpmInfoCallback = void (TpmInfo tpmInfo);
+
   interface Functions {
     [supportsPromises] static void getBatteryInfo(BatteryInfoCallback callback);
 
@@ -174,6 +230,8 @@
 
     [supportsPromises] static void getCpuInfo(CpuInfoCallback callback);
 
+    [supportsPromises] static void getInternetConnectivityInfo(InternetConnectivityInfoCallback callback);
+
     [supportsPromises] static void getMemoryInfo(MemoryInfoCallback callback);
 
     [supportsPromises] static void getOemData(OemDataCallback callback);
@@ -184,6 +242,6 @@
 
     [supportsPromises] static void getStatefulPartitionInfo(StatefulPartitionInfoCallback callback);
 
-    [supportsPromises] static void getInternetConnectivityInfo(InternetConnectivityInfoCallback callback);
+    [supportsPromises] static void getTpmInfo(TpmInfoCallback callback);
   };
 };
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index f60e989..e317ef5 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -297,11 +297,12 @@
 }
 
 void ReadAnythingAppController::OnThemeChanged(ReadAnythingThemePtr new_theme) {
+  background_color_ = new_theme->background_color;
   font_name_ = new_theme->font_name;
   font_size_ = new_theme->font_size;
-  letter_spacing_ = GetLetterSpacingValue(new_theme->letter_spacing);
   foreground_color_ = new_theme->foreground_color;
-  background_color_ = new_theme->background_color;
+  letter_spacing_ = GetLetterSpacingValue(new_theme->letter_spacing);
+  line_spacing_ = GetLineSpacingValue(new_theme->line_spacing);
 
   // TODO(abigailbklein): Use v8::Function rather than javascript. If possible,
   // replace this function call with firing an event.
@@ -313,14 +314,15 @@
     v8::Isolate* isolate) {
   return gin::Wrappable<ReadAnythingAppController>::GetObjectTemplateBuilder(
              isolate)
+      .SetProperty("backgroundColor",
+                   &ReadAnythingAppController::BackgroundColor)
       .SetProperty("displayNodeIds", &ReadAnythingAppController::DisplayNodeIds)
       .SetProperty("fontName", &ReadAnythingAppController::FontName)
       .SetProperty("fontSize", &ReadAnythingAppController::FontSize)
-      .SetProperty("letterSpacing", &ReadAnythingAppController::LetterSpacing)
       .SetProperty("foregroundColor",
                    &ReadAnythingAppController::ForegroundColor)
-      .SetProperty("backgroundColor",
-                   &ReadAnythingAppController::BackgroundColor)
+      .SetProperty("letterSpacing", &ReadAnythingAppController::LetterSpacing)
+      .SetProperty("lineSpacing", &ReadAnythingAppController::LineSpacing)
       .SetMethod("getChildren", &ReadAnythingAppController::GetChildren)
       .SetMethod("getHtmlTag", &ReadAnythingAppController::GetHtmlTag)
       .SetMethod("getLanguage", &ReadAnythingAppController::GetLanguage)
@@ -339,6 +341,10 @@
   return content_node_ids_;
 }
 
+SkColor ReadAnythingAppController::BackgroundColor() {
+  return background_color_;
+}
+
 std::string ReadAnythingAppController::FontName() {
   return font_name_;
 }
@@ -347,16 +353,16 @@
   return font_size_;
 }
 
-float ReadAnythingAppController::LetterSpacing() {
-  return letter_spacing_;
-}
-
 SkColor ReadAnythingAppController::ForegroundColor() {
   return foreground_color_;
 }
 
-SkColor ReadAnythingAppController::BackgroundColor() {
-  return background_color_;
+float ReadAnythingAppController::LetterSpacing() {
+  return letter_spacing_;
+}
+
+float ReadAnythingAppController::LineSpacing() {
+  return line_spacing_;
 }
 
 std::vector<ui::AXNodeID> ReadAnythingAppController::GetChildren(
@@ -428,9 +434,15 @@
                                                    float font_size,
                                                    SkColor foreground_color,
                                                    SkColor background_color,
+                                                   int line_spacing,
                                                    int letter_spacing) {
+  auto line_spacing_enum =
+      static_cast<read_anything::mojom::Spacing>(line_spacing);
+  auto letter_spacing_enum =
+      static_cast<read_anything::mojom::Spacing>(letter_spacing);
   OnThemeChanged(ReadAnythingTheme::New(font_name, font_size, foreground_color,
-                                        background_color, letter_spacing));
+                                        background_color, line_spacing_enum,
+                                        letter_spacing_enum));
 }
 
 void ReadAnythingAppController::SetContentForTesting(
@@ -442,20 +454,37 @@
   OnAXTreeDistilled(snapshot, content_node_ids);
 }
 
-double ReadAnythingAppController::GetLetterSpacingValue(int letter_spacing) {
-  auto ls = static_cast<read_anything::mojom::LetterSpacing>(letter_spacing);
-  switch (ls) {
-    case read_anything::mojom::LetterSpacing::kTight:
+double ReadAnythingAppController::GetLetterSpacingValue(
+    read_anything::mojom::Spacing letter_spacing) {
+  // auto ls = static_cast<read_anything::mojom::Spacing>(letter_spacing);
+  switch (letter_spacing) {
+    case read_anything::mojom::Spacing::kTight:
       return -0.05;
-    case read_anything::mojom::LetterSpacing::kDefault:
+    case read_anything::mojom::Spacing::kDefault:
       return 0;
-    case read_anything::mojom::LetterSpacing::kLoose:
+    case read_anything::mojom::Spacing::kLoose:
       return 0.05;
-    case read_anything::mojom::LetterSpacing::kVeryLoose:
+    case read_anything::mojom::Spacing::kVeryLoose:
       return 0.1;
   }
 }
 
+double ReadAnythingAppController::GetLineSpacingValue(
+    read_anything::mojom::Spacing line_spacing) {
+  // auto ls = static_cast<read_anything::mojom::Spacing>(line_spacing);
+  switch (line_spacing) {
+    case read_anything::mojom::Spacing::kTight:
+      return 1.0;
+    case read_anything::mojom::Spacing::kDefault:
+    default:
+      return 1.15;
+    case read_anything::mojom::Spacing::kLoose:
+      return 1.5;
+    case read_anything::mojom::Spacing::kVeryLoose:
+      return 2.0;
+  }
+}
+
 ui::AXNode* ReadAnythingAppController::GetAXNode(ui::AXNodeID ax_node_id) {
   if (!tree_)
     return nullptr;
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index a667cba..a16c09d 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -72,11 +72,12 @@
 
   // gin templates:
   std::vector<ui::AXNodeID> DisplayNodeIds();
+  SkColor BackgroundColor();
   std::string FontName();
   float FontSize();
-  float LetterSpacing();
   SkColor ForegroundColor();
-  SkColor BackgroundColor();
+  float LetterSpacing();
+  float LineSpacing();
   std::vector<ui::AXNodeID> GetChildren(ui::AXNodeID ax_node_id);
   std::string GetHtmlTag(ui::AXNodeID ax_node_id);
   std::string GetLanguage(ui::AXNodeID ax_node_id);
@@ -107,8 +108,10 @@
                           float font_size,
                           SkColor foreground_color,
                           SkColor background_color,
+                          int line_spacing,
                           int letter_spacing);
-  double GetLetterSpacingValue(int letter_spacing);
+  double GetLetterSpacingValue(read_anything::mojom::Spacing letter_spacing);
+  double GetLineSpacingValue(read_anything::mojom::Spacing line_spacing);
 
   ui::AXNode* GetAXNode(ui::AXNodeID ax_node_id);
 
@@ -132,11 +135,13 @@
   ui::AXNode* end_node_ = nullptr;
   int32_t start_offset_ = -1;
   int32_t end_offset_ = -1;
+
+  SkColor background_color_;
   std::string font_name_;
   float font_size_;
-  float letter_spacing_;
   SkColor foreground_color_;
-  SkColor background_color_;
+  float letter_spacing_;
+  float line_spacing_;
 };
 
 #endif  // CHROME_RENDERER_ACCESSIBILITY_READ_ANYTHING_APP_CONTROLLER_H_
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index 38681d2..ef98d78 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -44,9 +44,11 @@
                           float font_size,
                           SkColor foreground_color,
                           SkColor background_color,
+                          int line_spacing,
                           int letter_spacing) {
     controller_->SetThemeForTesting(font_name, font_size, foreground_color,
-                                    background_color, letter_spacing);
+                                    background_color, line_spacing,
+                                    letter_spacing);
   }
   void OnAXTreeDistilled(const ui::AXTreeUpdate& snapshot,
                          const std::vector<ui::AXNodeID>& content_node_ids) {
@@ -65,6 +67,8 @@
 
   SkColor BackgroundColor() { return controller_->BackgroundColor(); }
 
+  float LineSpacing() { return controller_->LineSpacing(); }
+
   float LetterSpacing() { return controller_->LetterSpacing(); }
 
   std::vector<ui::AXNodeID> GetChildren(ui::AXNodeID ax_node_id) {
@@ -104,12 +108,15 @@
   SkColor background = SkColorSetRGB(0xFD, 0xE2, 0x93);
   int letter_spacing = 0;  // enum value, kTight
   float letter_spacing_value = -0.05;
-  SetThemeForTesting(font_name, font_size, foreground, background,
+  int line_spacing = 1;  // enum value, kDefault
+  float line_spacing_value = 1.15;
+  SetThemeForTesting(font_name, font_size, foreground, background, line_spacing,
                      letter_spacing);
   EXPECT_EQ(font_name, FontName());
   EXPECT_EQ(font_size, FontSize());
   EXPECT_EQ(foreground, ForegroundColor());
   EXPECT_EQ(background, BackgroundColor());
+  EXPECT_EQ(line_spacing_value, LineSpacing());
   EXPECT_EQ(letter_spacing_value, LetterSpacing());
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bf81823..dbcb821 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8915,8 +8915,8 @@
       "../browser/supervised_user/child_accounts/child_account_service_unittest.cc",
       "../browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc",
       "../browser/supervised_user/child_accounts/permission_request_creator_apiary_unittest.cc",
+      "../browser/supervised_user/kids_chrome_management/kids_access_token_fetcher_unittest.cc",
       "../browser/supervised_user/kids_chrome_management/kids_external_fetcher_unittest.cc",
-      "../browser/supervised_user/kids_chrome_management/kids_management_service_unittest.cc",
       "../browser/supervised_user/kids_management_url_checker_client_unittest.cc",
       "../browser/supervised_user/parental_control_metrics_unittest.cc",
       "../browser/supervised_user/supervised_user_favicon_request_handler_unittest.cc",
diff --git a/chrome/test/data/sync/meta_redirect.html b/chrome/test/data/sync/meta_redirect.html
new file mode 100644
index 0000000..32827b8
--- /dev/null
+++ b/chrome/test/data/sync/meta_redirect.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <meta http-equiv="refresh" content="0;url=simple.html">
+  </head>
+  <body>
+    Redirecting...
+  </body>
+</html>
diff --git a/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts b/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts
index 93c9f3e..5781b3b 100644
--- a/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts
@@ -20,7 +20,7 @@
     document.body.innerHTML = '';
     readAnythingApp = document.createElement('read-anything-app');
     document.body.appendChild(readAnythingApp);
-    chrome.readAnything.setThemeForTesting('default', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('default', 18.0, 0, 0, 1, 0);
   });
 
   function assertFontName(fontFamily: string) {
@@ -33,6 +33,11 @@
     assertEquals(fontSize, getComputedStyle(container!).fontSize);
   }
 
+  function assertLineSpacing(lineSpacing: string) {
+    const container = readAnythingApp.shadowRoot!.getElementById('container');
+    assertEquals(lineSpacing, getComputedStyle(container!).lineHeight);
+  }
+
   function assertContainerInnerHTML(expected: string) {
     const actual: string =
         readAnythingApp.shadowRoot!.getElementById('container')!.innerHTML;
@@ -40,36 +45,36 @@
   }
 
   test('updateTheme fontName', () => {
-    chrome.readAnything.setThemeForTesting('Standard font', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Standard font', 18.0, 0, 0, 1, 0);
     assertFontName('"Standard font"');
 
-    chrome.readAnything.setThemeForTesting('Sans-serif', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Sans-serif', 18.0, 0, 0, 1, 0);
     assertFontName('sans-serif');
 
-    chrome.readAnything.setThemeForTesting('Serif', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Serif', 18.0, 0, 0, 1, 0);
     assertFontName('serif');
 
-    chrome.readAnything.setThemeForTesting('Avenir', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Avenir', 18.0, 0, 0, 1, 0);
     assertFontName('avenir');
 
-    chrome.readAnything.setThemeForTesting('Comic Neue', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Comic Neue', 18.0, 0, 0, 1, 0);
     assertFontName('"Comic Neue"');
 
-    chrome.readAnything.setThemeForTesting('Comic Sans MS', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Comic Sans MS', 18.0, 0, 0, 1, 0);
     assertFontName('"Comic Sans MS"');
 
-    chrome.readAnything.setThemeForTesting('Poppins', 18.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Poppins', 18.0, 0, 0, 1, 0);
     assertFontName('poppins');
   });
 
   test('updateTheme fontSize', () => {
-    chrome.readAnything.setThemeForTesting('Standard font', 1.0, 0, 0, 0);
+    chrome.readAnything.setThemeForTesting('Standard font', 1.0, 0, 0, 1, 0);
     assertFontSize('16px');  // 1em = 16px
   });
 
   test('updateTheme foregroundColor', () => {
     chrome.readAnything.setThemeForTesting(
-        'f', 1, /* SkColorSetRGB(0x33, 0x36, 0x39) = */ 4281546297, 0, 0);
+        'f', 1, /* SkColorSetRGB(0x33, 0x36, 0x39) = */ 4281546297, 0, 1, 0);
     const container = readAnythingApp.shadowRoot!.getElementById('container');
     assertEquals(
         /* #333639 = */ 'rgb(51, 54, 57)', getComputedStyle(container!).color);
@@ -77,15 +82,20 @@
 
   test('updateTheme backgroundColor', () => {
     chrome.readAnything.setThemeForTesting(
-        'f', 1, 0, /* SkColorSetRGB(0xFD, 0xE2, 0x93) = */ 4294828691, 0);
+        'f', 1, 0, /* SkColorSetRGB(0xFD, 0xE2, 0x93) = */ 4294828691, 1, 0);
     const container = readAnythingApp.shadowRoot!.getElementById('container');
     assertEquals(
         /* #FDE293 = */ 'rgb(253, 226, 147)',
         getComputedStyle(container!).backgroundColor);
   });
 
+  test('updateTheme lineSpacing', () => {
+    chrome.readAnything.setThemeForTesting('Standard font', 1.0, 0, 0, 2, 0);
+    assertLineSpacing('24px');  // 1.5 times the 1em (16px) font size
+  });
+
   test('updateTheme letterSpacing', () => {
-    chrome.readAnything.setThemeForTesting('f', 1, 0, 0, 3);
+    chrome.readAnything.setThemeForTesting('f', 1, 0, 0, 1, 3);
     const container = readAnythingApp.shadowRoot!.getElementById('container');
     // very loose letter letter spacing = 0.1em, font size = 1em = 16px
     assertEquals('1.6px', getComputedStyle(container!).letterSpacing);
diff --git a/chrome/test/webapps/data/actions.md b/chrome/test/webapps/data/actions.md
index c9ffbe41..cc05236 100644
--- a/chrome/test/webapps/data/actions.md
+++ b/chrome/test/webapps/data/actions.md
@@ -20,7 +20,7 @@
 
 TODO(dmurph): Possibly this table up into markdown-header section.
 
-| # Action base name | Argument Types | Output Actions | Unique Identifier (next: 133) | Status (WIP, Implemented, Not Implemented, Parameterized) | Description | Metadata, implementation bug, etc |
+| # Action base name | Argument Types | Output Actions | Unique Identifier (next: 135) | Status (WIP, Implemented, Not Implemented, Parameterized) | Description | Metadata, implementation bug, etc |
 | --- | --- | --- | --- | --- | --- | --- |
 | # Badging |
 | check_app_badge_empty | Site |  | 2 | Not Implemented | Check that the 'badge' on the app icon is empty |  |
@@ -90,6 +90,7 @@
 | check_app_in_list_icon_correct | Site |  | 75 | Implemented | Find the app in the app list (on desktop, this is chrome://apps, and on ChromeOS, this is the app drawer). Check that the icon for the given app in the app list is correct. | P2 (fetch icon using web request for chrome://app-icon/<app-id>/<icon-size>) |
 | check_theme_color | Site |  | 76 | Not Implemented | Asserts that the theme color of the given app window is correct. | P3 |
 | # Misc UX |
+| check_browser_navigation | Site |  | 134 | Implemented | Check the current browser navigation is the given site |  |
 | check_browser_navigation_is_app_settings | Site |  | 109 | Implemented | Check the current browser navigation is chrome://app-settings/<app-id> | phillis@ |
 | check_create_shortcut_not_shown |  |  | 85 | WIP | Check that the "Create Shortcut" menu option (3-dot->"More Tools"->"Create Shortcut) is greyed out |  |
 | check_create_shortcut_shown |  |  | 86 | WIP | Check that the "Create Shortcut" menu option (3-dot->"More Tools"->"Create Shortcut) is shown |  |
diff --git a/chromeos/ash/components/login/auth/auth_factor_editor.cc b/chromeos/ash/components/login/auth/auth_factor_editor.cc
index 2db789d..cfd7f9b 100644
--- a/chromeos/ash/components/login/auth/auth_factor_editor.cc
+++ b/chromeos/ash/components/login/auth/auth_factor_editor.cc
@@ -376,6 +376,7 @@
   }
   CHECK(reply.has_value());
   LOGIN_LOG(EVENT) << "Successfully added credentials";
+  context->ClearAuthFactorsConfiguration();
   std::move(callback).Run(std::move(context), absl::nullopt);
   // TODO(crbug.com/1310312): Think if we should update SessionAuthFactors in
   // context after such operation.
@@ -393,6 +394,7 @@
   }
   CHECK(reply.has_value());
   LOGIN_LOG(EVENT) << "Successfully added auth factor";
+  context->ClearAuthFactorsConfiguration();
   std::move(callback).Run(std::move(context), absl::nullopt);
   // TODO(crbug.com/1310312): Think if we should update SessionAuthFactors in
   // context after such operation.
@@ -410,6 +412,7 @@
   }
   CHECK(reply.has_value());
   LOGIN_LOG(EVENT) << "Successfully updated credential";
+  context->ClearAuthFactorsConfiguration();
   std::move(callback).Run(std::move(context), absl::nullopt);
 }
 
@@ -425,6 +428,7 @@
   }
   CHECK(reply.has_value());
   LOGIN_LOG(EVENT) << "Successfully updated auth factor";
+  context->ClearAuthFactorsConfiguration();
   std::move(callback).Run(std::move(context), absl::nullopt);
 }
 
@@ -441,6 +445,7 @@
 
   CHECK(reply.has_value());
   LOGIN_LOG(EVENT) << "Successfully added recovery key";
+  context->ClearAuthFactorsConfiguration();
   std::move(callback).Run(std::move(context), absl::nullopt);
 }
 
@@ -457,6 +462,7 @@
 
   CHECK(reply.has_value());
   LOGIN_LOG(EVENT) << "Successfully removed recovery key";
+  context->ClearAuthFactorsConfiguration();
   std::move(callback).Run(std::move(context), absl::nullopt);
 }
 
diff --git a/chromeos/ash/components/login/auth/public/user_context.cc b/chromeos/ash/components/login/auth/public/user_context.cc
index d32876a..d03aae3 100644
--- a/chromeos/ash/components/login/auth/public/user_context.cc
+++ b/chromeos/ash/components/login/auth/public/user_context.cc
@@ -315,6 +315,10 @@
   auth_factors_configuration_ = std::move(auth_factors);
 }
 
+void UserContext::ClearAuthFactorsConfiguration() {
+  auth_factors_configuration_ = absl::nullopt;
+}
+
 const AuthFactorsConfiguration& UserContext::GetAuthFactorsConfiguration() {
   DCHECK(features::IsUseAuthFactorsEnabled());
   if (!auth_factors_configuration_.has_value()) {
diff --git a/chromeos/ash/components/login/auth/public/user_context.h b/chromeos/ash/components/login/auth/public/user_context.h
index d1c592f..f3ecf437 100644
--- a/chromeos/ash/components/login/auth/public/user_context.h
+++ b/chromeos/ash/components/login/auth/public/user_context.h
@@ -163,6 +163,7 @@
   void SetCanLockManagedGuestSession(bool can_lock_managed_guest_session);
   void SetSessionAuthFactors(SessionAuthFactors keys);
   void SetAuthFactorsConfiguration(AuthFactorsConfiguration auth_factors);
+  void ClearAuthFactorsConfiguration();
   // We need to pull input method used to log in into the user session to make
   // it consistent. This method will remember given input method to be used
   // when session starts.
diff --git a/chromeos/system/name_value_pairs_parser.cc b/chromeos/system/name_value_pairs_parser.cc
index f33604429..b6518d2 100644
--- a/chromeos/system/name_value_pairs_parser.cc
+++ b/chromeos/system/name_value_pairs_parser.cc
@@ -28,16 +28,13 @@
 
 // Runs a tool and capture its standard output into |output|. Returns false
 // if the tool cannot be run.
-bool GetToolOutput(const std::vector<std::string>& args, std::string* output) {
-  DCHECK_GE(args.size(), 1u);
-
-  if (!base::PathExists(base::FilePath(args[0]))) {
-    LOG(WARNING) << "Tool for statistics not found: " << args[0];
+bool GetToolOutput(const base::CommandLine& command, std::string* output) {
+  if (!base::PathExists(command.GetProgram())) {
+    LOG(WARNING) << "Tool for statistics not found: " << command.GetProgram();
     return false;
   }
-
-  if (!base::GetAppOutput(args, output)) {
-    LOG(WARNING) << "Error executing " << args[0];
+  if (!base::GetAppOutput(command, output)) {
+    LOG(WARNING) << "Error executing " << command.GetProgram();
     return false;
   }
 
@@ -156,15 +153,14 @@
 }
 
 bool NameValuePairsParser::ParseNameValuePairsFromTool(
-    const std::vector<std::string>& args,
+    const base::CommandLine& command,
     NameValuePairsFormat format) {
-  DCHECK_GE(args.size(), 1u);
-
   std::string output_string;
-  if (!GetToolOutput(args, &output_string))
+  if (!GetToolOutput(command, &output_string))
     return false;
 
-  return ParseNameValuePairs(output_string, format, /*debug_source=*/args[0]);
+  return ParseNameValuePairs(output_string, format,
+                             /*debug_source=*/command.GetProgram().value());
 }
 
 void NameValuePairsParser::DeletePairsWithValue(const std::string& value) {
diff --git a/chromeos/system/name_value_pairs_parser.h b/chromeos/system/name_value_pairs_parser.h
index fa16da9b..1757aef 100644
--- a/chromeos/system/name_value_pairs_parser.h
+++ b/chromeos/system/name_value_pairs_parser.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 
@@ -57,11 +58,11 @@
                                    NameValuePairsFormat format);
 
   // Parses name-value pairs in the specified |format| from the standard output
-  // of a tool invocation specified by |args|.
+  // of a tool invocation specified by |command|.
   //
   // Returns false if there was any error in the command invocation or when
   // parsing its output. Valid pairs will still be added to the map.
-  bool ParseNameValuePairsFromTool(const std::vector<std::string>& args,
+  bool ParseNameValuePairsFromTool(const base::CommandLine& command,
                                    NameValuePairsFormat format);
 
   // Delete all pairs with |value|.
diff --git a/chromeos/system/name_value_pairs_parser_unittest.cc b/chromeos/system/name_value_pairs_parser_unittest.cc
index db0ae37b1..0bc714d 100644
--- a/chromeos/system/name_value_pairs_parser_unittest.cc
+++ b/chromeos/system/name_value_pairs_parser_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/system/name_value_pairs_parser.h"
 
+#include "base/command_line.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -140,19 +141,20 @@
 }
 
 TEST(NameValuePairsParser, TestParseNameValuePairsFromCrossystemTool) {
-  // Sample output is taken from the /usr/bin/crosssytem tool.
-  const std::vector<std::string> command = {
-      "/bin/echo",
-      "arch                   = x86           # Platform architecture\n"
-      "cros_debug             = 1             # OS should allow debug\n"
-      "dbg_reset              = (error)       # Debug reset mode request\n"
-      "key#with_comment       = some value    # Multiple # comment # delims\n"
-      "key                    =               # No value.\n"
-      "vdat_timers            = "
-      "LFS=0,0 LF=1784220250,2971030570 LK=9064076660,9342689170 "
-      "# Timer values from VbSharedData\n"
-      "wpsw_cur               = 1             # Firmware hardware WP switch "
-      "pos\n"};
+  // Sample output is taken from the /usr/bin/crossystem tool.
+  const base::CommandLine command(
+      {"/bin/echo",
+       // Below is single string argument.
+       "arch                   = x86           # Platform architecture\n"
+       "cros_debug             = 1             # OS should allow debug\n"
+       "dbg_reset              = (error)       # Debug reset mode request\n"
+       "key#with_comment       = some value    # Multiple # comment # delims\n"
+       "key                    =               # No value.\n"
+       "vdat_timers            = "
+       "LFS=0,0 LF=1784220250,2971030570 LK=9064076660,9342689170 "
+       "# Timer values from VbSharedData\n"
+       "wpsw_cur               = 1             # Firmware hardware WP switch "
+       "pos\n"});
 
   NameValuePairsParser::NameValueMap map;
   NameValuePairsParser parser(&map);
@@ -169,6 +171,20 @@
   EXPECT_EQ("1", map["wpsw_cur"]);
 }
 
+TEST(NameValuePairsParser, TestParseNameValuePairsFromEmptyTool) {
+  // Sample output is taken from the /usr/bin/crossystem tool.
+  const base::CommandLine command(
+      {"/bin/echo",
+       // Use empty string argument to check that tool caller can handle it.
+       ""});
+
+  NameValuePairsParser::NameValueMap map;
+  NameValuePairsParser parser(&map);
+  parser.ParseNameValuePairsFromTool(command,
+                                     NameValuePairsFormat::kCrossystem);
+  EXPECT_TRUE(map.empty());
+}
+
 TEST(NameValuePairsParser, DeletePairsWithValue) {
   NameValuePairsParser::NameValueMap map = {
       {"foo", "good"}, {"bar", "bad"}, {"baz", "good"}, {"end", "bad"},
diff --git a/chromeos/system/statistics_provider_impl.cc b/chromeos/system/statistics_provider_impl.cc
index 30d08807..6d21b20 100644
--- a/chromeos/system/statistics_provider_impl.cc
+++ b/chromeos/system/statistics_provider_impl.cc
@@ -152,7 +152,7 @@
 
 StatisticsProviderImpl::StatisticsSources CreateDefaultSources() {
   StatisticsProviderImpl::StatisticsSources sources;
-  sources.crossystem_tool = {kCrosSystemTool};
+  sources.crossystem_tool = base::CommandLine(base::FilePath(kCrosSystemTool));
   sources.machine_info_filepath = GetFilePathIgnoreFailure(FILE_MACHINE_INFO);
   sources.vpd_echo_filepath = base::FilePath(kEchoCouponFile);
   sources.vpd_filepath = GetFilePathIgnoreFailure(FILE_VPD);
@@ -199,8 +199,6 @@
       oem_manifest_loaded_(false),
       statistics_loaded_(base::WaitableEvent::ResetPolicy::MANUAL,
                          base::WaitableEvent::InitialState::NOT_SIGNALED) {
-  DCHECK(!sources_.crossystem_tool.empty());
-
   regional_data_extractors_[kInitialLocaleKey] =
       &GetInitialLocaleFromRegionalData;
   regional_data_extractors_[kKeyboardLayoutKey] =
@@ -373,7 +371,7 @@
     if (!parser.ParseNameValuePairsFromTool(
             sources_.crossystem_tool, NameValuePairsFormat::kCrossystem)) {
       LOG(ERROR) << "Errors parsing output from: "
-                 << sources_.crossystem_tool[0];
+                 << sources_.crossystem_tool.GetProgram();
     }
     // Drop useless "(error)" values so they don't displace valid values
     // supplied later by other tools: https://crbug.com/844258
diff --git a/chromeos/system/statistics_provider_impl.h b/chromeos/system/statistics_provider_impl.h
index 31426f3..30d38a2 100644
--- a/chromeos/system/statistics_provider_impl.h
+++ b/chromeos/system/statistics_provider_impl.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/callback_forward.h"
+#include "base/command_line.h"
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
@@ -41,7 +42,7 @@
     StatisticsSources& operator=(StatisticsSources&& other);
 
     // Binary to fake crossystem tool with arguments. E.g. echo.
-    std::vector<std::string> crossystem_tool;
+    base::CommandLine crossystem_tool{base::CommandLine::NO_PROGRAM};
 
     base::FilePath machine_info_filepath;
     base::FilePath vpd_echo_filepath;
diff --git a/chromeos/system/statistics_provider_impl_unittest.cc b/chromeos/system/statistics_provider_impl_unittest.cc
index c3c508e8..606a5f4 100644
--- a/chromeos/system/statistics_provider_impl_unittest.cc
+++ b/chromeos/system/statistics_provider_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "chromeos/system/statistics_provider_impl.h"
 
 #include "ash/constants/ash_switches.h"
+#include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -63,10 +64,8 @@
   SourcesBuilder(const SourcesBuilder&) = delete;
   SourcesBuilder& operator=(const SourcesBuilder&) = delete;
 
-  SourcesBuilder& set_crossystem_tool(const std::string& tool_cmd,
-                                      const std::string& tool_args) {
-    EXPECT_FALSE(tool_args.empty());
-    sources_.crossystem_tool = {tool_cmd, tool_args};
+  SourcesBuilder& set_crossystem_tool(const base::CommandLine& tool_cmd) {
+    sources_.crossystem_tool = tool_cmd;
     return *this;
   }
 
@@ -96,8 +95,8 @@
   }
 
   StatisticsProviderImpl::StatisticsSources Build() {
-    if (sources_.crossystem_tool.empty()) {
-      sources_.crossystem_tool = {kEchoCmd};
+    if (sources_.crossystem_tool.GetProgram().empty()) {
+      sources_.crossystem_tool = base::CommandLine(base::FilePath(kEchoCmd));
     }
 
     if (sources_.machine_info_filepath.empty()) {
@@ -167,7 +166,7 @@
 
   StatisticsProviderImpl::StatisticsSources testing_sources =
       SourcesBuilder(temp_dir())
-          .set_crossystem_tool(kEchoCmd, kEchoArgs)
+          .set_crossystem_tool(base::CommandLine({kEchoCmd, kEchoArgs}))
           .Build();
 
   // Load statistics.
@@ -226,7 +225,7 @@
 
   StatisticsProviderImpl::StatisticsSources testing_sources =
       SourcesBuilder(temp_dir())
-          .set_crossystem_tool(kEchoCmd, kEchoArgs)
+          .set_crossystem_tool(base::CommandLine({kEchoCmd, kEchoArgs}))
           .set_machine_info(
               CreateFileInTempDir(kMachineInfoStatistics, temp_dir()))
           .Build();
@@ -265,7 +264,7 @@
 
   StatisticsProviderImpl::StatisticsSources testing_sources =
       SourcesBuilder(temp_dir())
-          .set_crossystem_tool(kEchoCmd, kEchoArgs)
+          .set_crossystem_tool(base::CommandLine({kEchoCmd, kEchoArgs}))
           .set_machine_info(
               CreateFileInTempDir(kMachineInfoStatistics, temp_dir()))
           .Build();
@@ -300,7 +299,7 @@
 
   StatisticsProviderImpl::StatisticsSources testing_sources =
       SourcesBuilder(temp_dir())
-          .set_crossystem_tool(kEchoCmd, kEchoArgs)
+          .set_crossystem_tool(base::CommandLine({kEchoCmd, kEchoArgs}))
           .Build();
 
   // Load statistics.
diff --git a/components/autofill/core/browser/autofill_experiments_unittest.cc b/components/autofill/core/browser/autofill_experiments_unittest.cc
index 7535f7b3..6bef837 100644
--- a/components/autofill/core/browser/autofill_experiments_unittest.cc
+++ b/components/autofill/core/browser/autofill_experiments_unittest.cc
@@ -105,8 +105,7 @@
 }
 
 TEST_F(AutofillExperimentsTest, IsCardUploadEnabled_AuthError) {
-  sync_service_.SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  sync_service_.SetPersistentAuthErrorOtherThanWebSignout();
   EXPECT_FALSE(IsCreditCardUploadEnabled(AutofillSyncSigninState::kSyncPaused));
   histogram_tester.ExpectUniqueSample(
       "Autofill.CardUploadEnabled",
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index fbb2b443..e794ab6 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -4778,14 +4778,12 @@
 
   // Call OnSyncServiceInitialized with a sync service in auth error.
   syncer::TestSyncService sync_service;
-  sync_service.SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+  sync_service.SetPersistentAuthErrorOtherThanWebSignout();
   personal_data_->OnSyncServiceInitialized(&sync_service);
   WaitForOnPersonalDataChanged();
 
   // Remove the auth error to be able to get the server cards.
-  sync_service.SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  sync_service.ClearAuthError();
 
   // Check that cards were masked and other were untouched.
   EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
diff --git a/components/autofill_assistant/browser/features.cc b/components/autofill_assistant/browser/features.cc
index 1c2a58f..08c232a 100644
--- a/components/autofill_assistant/browser/features.cc
+++ b/components/autofill_assistant/browser/features.cc
@@ -29,13 +29,13 @@
 // |GetActions| calls.
 BASE_FEATURE(kAutofillAssistantVerifyGetActionsResponses,
              "AutofillAssistantVerifyGetActionsResponses",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Controls whether RPC requests to the backend should be signed for
 // |GetActions| calls.
 BASE_FEATURE(kAutofillAssistantSignGetActionsRequests,
              "AutofillAssistantSignGetActionsRequests",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Controls whether RPC requests to the backend should be signed for
 // |GetNoRoundTripScriptsByHash| calls.
diff --git a/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc b/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc
index 398d392..beefd9e 100644
--- a/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc
+++ b/components/autofill_assistant/browser/service/service_request_sender_impl_unittest.cc
@@ -100,6 +100,7 @@
 };
 
 TEST_F(ServiceRequestSenderImplTest, SendUnauthenticatedRequest) {
+  InitCupFeatures(false, false);
   auto cup_factory =
       std::make_unique<NiceMock<autofill_assistant::cup::MockCUPFactory>>();
   auto loader_factory =
@@ -136,6 +137,7 @@
 }
 
 TEST_F(ServiceRequestSenderImplTest, SendAuthenticatedRequest) {
+  InitCupFeatures(false, false);
   auto cup_factory =
       std::make_unique<NiceMock<autofill_assistant::cup::MockCUPFactory>>();
   auto loader_factory =
@@ -227,6 +229,7 @@
 
 TEST_F(ServiceRequestSenderImplTest,
        AuthRequestFallsBackToApiKeyOnEmptyAccessToken) {
+  InitCupFeatures(false, false);
   EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
       .WillOnce(RunOnceCallback<0>(true, /*access_token = */ ""));
 
@@ -268,6 +271,7 @@
 
 TEST_F(ServiceRequestSenderImplTest,
        AuthRequestFallsBackToApiKeyIfFetchingAccessTokenFails) {
+  InitCupFeatures(false, false);
   EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
       .WillOnce(
           RunOnceCallback<0>(/*success = */ false, /*access_token = */ ""));
@@ -387,8 +391,7 @@
       RpcType::GET_ACTIONS);
 }
 
-TEST_F(ServiceRequestSenderImplTest, ValidatesGetActionsResponsesWhenEnabled) {
-  InitCupFeatures(true, true);
+TEST_F(ServiceRequestSenderImplTest, ValidatesGetActionsResponsesByDefault) {
   auto cup_factory =
       std::make_unique<NiceMock<autofill_assistant::cup::MockCUPFactory>>();
   auto cup = std::make_unique<NiceMock<autofill_assistant::cup::MockCUP>>();
diff --git a/components/content_settings/core/browser/cookie_settings_unittest.cc b/components/content_settings/core/browser/cookie_settings_unittest.cc
index 3d5aba9..19a1ed579 100644
--- a/components/content_settings/core/browser/cookie_settings_unittest.cc
+++ b/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -90,8 +90,8 @@
         kHttpsSubdomainSite("https://www.example.com"),
         kHttpsSite8080("https://example.com:8080"),
         kAllHttpsSitesPattern(ContentSettingsPattern::FromString("https://*")) {
-    std::vector<base::Feature> enabled_features;
-    std::vector<base::Feature> disabled_features;
+    std::vector<base::test::FeatureRef> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
 #if BUILDFLAG(IS_IOS)
     enabled_features.push_back(kImprovedCookieControls);
 #endif
diff --git a/components/content_settings/core/common/cookie_settings_base_unittest.cc b/components/content_settings/core/common/cookie_settings_base_unittest.cc
index 5ff86f72..4bd31680 100644
--- a/components/content_settings/core/common/cookie_settings_base_unittest.cc
+++ b/components/content_settings/core/common/cookie_settings_base_unittest.cc
@@ -233,7 +233,7 @@
  public:
   CookieSettingsBaseStorageAccessAPITest() {
     std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> disabled;
     if (IsStorageAccessAPIEnabled()) {
       enabled.push_back({net::features::kStorageAccessAPI,
                          {{"storage-access-api-grants-unpartitioned-storage",
diff --git a/components/google/core/common/google_util.cc b/components/google/core/common/google_util.cc
index a80af757..51f1a81 100644
--- a/components/google/core/common/google_util.cc
+++ b/components/google/core/common/google_util.cc
@@ -115,8 +115,6 @@
       base::MakeFixedFlatSet<base::StringPiece>({YOUTUBE_TLD_LIST});
 
   return IsValidHostName(canonical_host, "youtube", subdomain_permission,
-                         youtube_tlds) ||
-         IsValidHostName(canonical_host, "youtubekids", subdomain_permission,
                          youtube_tlds);
 }
 
diff --git a/components/google/core/common/google_util_unittest.cc b/components/google/core/common/google_util_unittest.cc
index 66bd3818..4b34a51f 100644
--- a/components/google/core/common/google_util_unittest.cc
+++ b/components/google/core/common/google_util_unittest.cc
@@ -401,10 +401,13 @@
                                   google_util::ALLOW_SUBDOMAIN,
                                   google_util::DISALLOW_NON_STANDARD_PORTS));
 
-  // YouTube Kids
-  EXPECT_TRUE(IsYoutubeDomainUrl(GURL("http://www.youtubekids.com"),
-                                 google_util::ALLOW_SUBDOMAIN,
-                                 google_util::DISALLOW_NON_STANDARD_PORTS));
+  // YouTube Kids is not a youtube domain as it does not use the standard Google
+  // auth stack.
+  //
+  // Regression test for b/247647476
+  EXPECT_FALSE(IsYoutubeDomainUrl(GURL("http://www.youtubekids.com"),
+                                  google_util::ALLOW_SUBDOMAIN,
+                                  google_util::DISALLOW_NON_STANDARD_PORTS));
 
   // TLD checks.
   EXPECT_TRUE(IsYoutubeDomainUrl(GURL("http://www.youtube.ca"),
diff --git a/components/history/core/browser/sync/history_sync_bridge.cc b/components/history/core/browser/sync/history_sync_bridge.cc
index 285e602..7dedf70 100644
--- a/components/history/core/browser/sync/history_sync_bridge.cc
+++ b/components/history/core/browser/sync/history_sync_bridge.cc
@@ -161,12 +161,15 @@
   if (specifics.page_transition().home_page()) {
     page_transition |= ui::PAGE_TRANSITION_HOME_PAGE;
   }
-  // Then add redirect markers as appropriate - first chain start/end markers.
-  if (redirect_index == 0) {
+  // Then add redirect markers as appropriate.
+  // First, chain start/end markers. Note that these only apply to the
+  // first/last visit per entity, respectively.
+  if (redirect_index == 0 && !specifics.redirect_chain_start_incomplete()) {
     page_transition |= ui::PAGE_TRANSITION_CHAIN_START;
   }
   // No "else" - a visit can be both the start and end of a chain!
-  if (redirect_index == specifics.redirect_entries_size() - 1) {
+  if (redirect_index == specifics.redirect_entries_size() - 1 &&
+      !specifics.redirect_chain_end_incomplete()) {
     page_transition |= ui::PAGE_TRANSITION_CHAIN_END;
   }
   // Finally, add the redirect type (if any).
@@ -307,6 +310,17 @@
   history->mutable_page_transition()->set_home_page(
       (first_visit.transition & ui::PAGE_TRANSITION_HOME_PAGE) != 0);
 
+  // The chain_start/end markers are inverted in the proto.
+  history->set_redirect_chain_start_incomplete(
+      (first_visit.transition & ui::PAGE_TRANSITION_CHAIN_START) == 0);
+  // Exception: The chain *end* marker needs to be taken from the last visit!
+  history->set_redirect_chain_end_incomplete(
+      (last_visit.transition & ui::PAGE_TRANSITION_CHAIN_END) == 0);
+  // Note: Typically, chain_start_incomplete and chain_end_incomplete will both
+  // end up being false here. However, in some cases (notably, client
+  // redirects), a single redirect chain may be split up over multiple entities,
+  // in which case one (or even both) might be true.
+
   // Referring visit and opener visit are taken from the *first* visit in the
   // chain, since they only make sense for that one.
   history->set_originator_referring_visit_id(first_visit.referring_visit);
@@ -549,9 +563,21 @@
       continue;
     }
 
-    std::unique_ptr<syncer::EntityData> entity_data =
+    std::vector<std::unique_ptr<syncer::EntityData>> entity_data_list =
         QueryRedirectChainAndMakeEntityData(final_visit);
-    if (!entity_data) {
+    // Typically, `entity_data_list` will have exactly one entry. In some error
+    // cases (corrupted DB), it may be empty, and in some cases the redirect
+    // chain may have been split into multiple entities. In that case, the last
+    // entity should be the one corresponding to the `key`.
+    if (entity_data_list.empty()) {
+      continue;
+    }
+    std::unique_ptr<syncer::EntityData> entity_data =
+        std::move(entity_data_list.back());
+    // The last entity's visit time should almost always match the desired one,
+    // but again, in some rare DB error cases it may not.
+    if (entity_data->specifics.history().visit_time_windows_epoch_micros() !=
+        visit_time.ToDeltaSinceWindowsEpoch().InMicroseconds()) {
       continue;
     }
     batch->Put(key, std::move(entity_data));
@@ -644,16 +670,17 @@
     return;
   }
 
-  std::unique_ptr<syncer::EntityData> entity_data =
+  std::vector<std::unique_ptr<syncer::EntityData>> entity_data_list =
       QueryRedirectChainAndMakeEntityData(visit_row);
-  if (!entity_data) {
-    return;
-  }
 
   std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
       CreateMetadataChangeList();
-  change_processor()->Put(GetStorageKeyFromVisitRow(visit_row),
-                          std::move(entity_data), metadata_change_list.get());
+
+  for (auto& entity_data : entity_data_list) {
+    std::string storage_key = GetStorageKey(*entity_data);
+    change_processor()->Put(storage_key, std::move(entity_data),
+                            metadata_change_list.get());
+  }
 
   // `metadata_change_list` must have been created via
   // CreateMetadataChangeList(), so downcasting is safe.
@@ -740,16 +767,17 @@
     return;
   }
 
-  std::unique_ptr<syncer::EntityData> entity_data =
+  std::vector<std::unique_ptr<syncer::EntityData>> entity_data_list =
       QueryRedirectChainAndMakeEntityData(visit_row);
-  if (!entity_data) {
-    return;
-  }
 
   std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
       CreateMetadataChangeList();
-  change_processor()->Put(GetStorageKeyFromVisitRow(visit_row),
-                          std::move(entity_data), metadata_change_list.get());
+
+  for (auto& entity_data : entity_data_list) {
+    std::string storage_key = GetStorageKey(*entity_data);
+    change_processor()->Put(storage_key, std::move(entity_data),
+                            metadata_change_list.get());
+  }
 
   // `metadata_change_list` must have been created via
   // CreateMetadataChangeList(), so downcasting is safe.
@@ -807,7 +835,7 @@
   change_processor()->ModelReadyToSync(std::move(batch));
 }
 
-std::unique_ptr<syncer::EntityData>
+std::vector<std::unique_ptr<syncer::EntityData>>
 HistorySyncBridge::QueryRedirectChainAndMakeEntityData(
     const VisitRow& final_visit) {
   // Query the redirect chain that ended in this visit.
@@ -816,26 +844,53 @@
   if (redirect_visits.empty()) {
     // This can happen if there's invalid data in the DB (e.g. broken referrer
     // "links").
-    return nullptr;
+    return {};
   }
   DCHECK_EQ(redirect_visits.back().visit_id, final_visit.visit_id);
 
-  std::vector<AnnotatedVisit> annotated_visits =
-      history_backend_->ToAnnotatedVisits(redirect_visits);
-  if (annotated_visits.empty()) {
-    // Again, this can happen if there's invalid data in the DB.
-    return nullptr;
+  // Typically, all visits in a redirect chain have the same timestamp. However,
+  // in some cases, a redirect chain may be extended retroactively (with visits
+  // with a different timestamp). In that case, split the chain into multiple
+  // subchains, which will become separate Sync entities.
+  std::vector<std::unique_ptr<syncer::EntityData>> entities;
+  auto subchain_begin = redirect_visits.begin();
+  while (subchain_begin != redirect_visits.end()) {
+    // `subchain_begin` points to the beginning of the current subchain.
+    base::Time chain_time = subchain_begin->visit_time;
+    auto subchain_end = subchain_begin + 1;
+    while (subchain_end != redirect_visits.end() &&
+           subchain_end->visit_time == chain_time) {
+      ++subchain_end;
+    }
+    // Now `subchain_end` points just beyond the end of the current subchain
+    // (i.e. first entry with a different timestamp or `redirect_visits.end()`).
+
+    // Grab the current subchain.
+    std::vector<VisitRow> subchain_visits(
+        std::make_move_iterator(subchain_begin),
+        std::make_move_iterator(subchain_end));
+
+    // Make `subchain_begin` point to the beginning of the *next* subchain, for
+    // the next iteration.
+    subchain_begin = subchain_end;
+
+    // Convert the current subchain into a SyncEntity.
+    std::vector<AnnotatedVisit> annotated_visits =
+        history_backend_->ToAnnotatedVisits(subchain_visits);
+    if (annotated_visits.empty()) {
+      // Again, this can happen if there's invalid data in the DB. In that case,
+      // skip this subchain but still try to handle any others.
+      continue;
+    }
+    GURL referrer_url =
+        GetURLForVisit(annotated_visits.front().visit_row.referring_visit);
+    std::vector<GURL> favicon_urls = history_backend_->GetFaviconURLsForURL(
+        annotated_visits.back().url_row.url());
+    // Note: `favicon_urls` may legitimately be empty, that's fine.
+    entities.push_back(MakeEntityData(GetLocalCacheGuid(), annotated_visits,
+                                      referrer_url, favicon_urls));
   }
-
-  GURL referrer_url =
-      GetURLForVisit(annotated_visits.front().visit_row.referring_visit);
-
-  std::vector<GURL> favicon_urls = history_backend_->GetFaviconURLsForURL(
-      annotated_visits.back().url_row.url());
-  // Note: `favicon_urls` may legitimately be empty, that's fine.
-
-  return MakeEntityData(GetLocalCacheGuid(), annotated_visits, referrer_url,
-                        favicon_urls);
+  return entities;
 }
 
 GURL HistorySyncBridge::GetURLForVisit(VisitID visit_id) {
diff --git a/components/history/core/browser/sync/history_sync_bridge.h b/components/history/core/browser/sync/history_sync_bridge.h
index cb3c4dc..f156e03a 100644
--- a/components/history/core/browser/sync/history_sync_bridge.h
+++ b/components/history/core/browser/sync/history_sync_bridge.h
@@ -84,10 +84,12 @@
   void LoadMetadata();
 
   // Queries the redirect chain ending in `final_visit` from the HistoryBackend,
-  // and creates the corresponding EntityData. May return null in case of
+  // and creates the corresponding EntityData(s). Typically returns a single
+  // EntityData, but in some cases the redirect chain may have to be split up
+  // into multiple entities. May return no entities at all in case of
   // HistoryBackend failure (e.g. corrupted DB).
-  std::unique_ptr<syncer::EntityData> QueryRedirectChainAndMakeEntityData(
-      const VisitRow& final_visit);
+  std::vector<std::unique_ptr<syncer::EntityData>>
+  QueryRedirectChainAndMakeEntityData(const VisitRow& final_visit);
 
   GURL GetURLForVisit(VisitID visit_id);
 
diff --git a/components/history/core/browser/sync/history_sync_bridge_unittest.cc b/components/history/core/browser/sync/history_sync_bridge_unittest.cc
index 28f65f6..b99af14 100644
--- a/components/history/core/browser/sync/history_sync_bridge_unittest.cc
+++ b/components/history/core/browser/sync/history_sync_bridge_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/sync/model/model_type_change_processor.h"
 #include "components/sync/protocol/entity_metadata.pb.h"
 #include "components/sync/protocol/history_specifics.pb.h"
+#include "components/sync/protocol/proto_value_conversions.h"
 #include "components/sync/test/forwarding_model_type_change_processor.h"
 #include "sql/database.h"
 #include "sql/meta_table.h"
@@ -250,6 +251,8 @@
   FakeModelTypeChangeProcessor* processor() { return &fake_processor_; }
   HistorySyncBridge* bridge() { return bridge_.get(); }
 
+  void AdvanceClock() { task_environment_.FastForwardBy(base::Seconds(1)); }
+
   std::pair<URLRow, VisitRow> AddVisitToBackendAndAdvanceClock(
       const GURL& url,
       ui::PageTransition transition,
@@ -257,7 +260,7 @@
     // After grabbing the visit time, advance the mock time so that the next
     // visit will get a unique time.
     base::Time visit_time = base::Time::Now();
-    task_environment_.FastForwardBy(base::Seconds(1));
+    AdvanceClock();
 
     URLRow url_row;
     const URLRow* existing_url_row = backend()->FindURLRow(url);
@@ -626,21 +629,23 @@
   visit_row1.transition = ui::PageTransitionFromInt(
       ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_START |
       ui::PAGE_TRANSITION_CLIENT_REDIRECT);
-  VisitID visit_id1 = backend()->AddVisit(visit_row1);
+  visit_row1.visit_id = backend()->AddVisit(visit_row1);
+
   VisitRow visit_row2;
-  visit_row2.referring_visit = visit_id1;
+  visit_row2.referring_visit = visit_row1.visit_id;
   visit_row2.url_id = url_id2;
   visit_row2.visit_time = visit_time;
   visit_row2.transition = ui::PageTransitionFromInt(
       ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_SERVER_REDIRECT);
-  VisitID visit_id2 = backend()->AddVisit(visit_row2);
+  visit_row2.visit_id = backend()->AddVisit(visit_row2);
+
   VisitRow visit_row3;
-  visit_row3.referring_visit = visit_id2;
+  visit_row3.referring_visit = visit_row2.visit_id;
   visit_row3.url_id = url_id3;
   visit_row3.visit_time = visit_time;
   visit_row3.transition = ui::PageTransitionFromInt(
       ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_END);
-  backend()->AddVisit(visit_row3);
+  visit_row3.visit_id = backend()->AddVisit(visit_row3);
 
   // Notify the bridge about all of the visits.
   bridge()->OnURLVisited(
@@ -650,7 +655,7 @@
   bridge()->OnURLVisited(
       /*history_backend=*/nullptr, url_row3, visit_row3);
 
-  // The whole chain should have resulting in a single entity being Put().
+  // The whole chain should have resulted in a single entity being Put().
   const std::string storage_key =
       HistorySyncMetadataDatabase::StorageKeyFromVisitTime(visit_time);
   EXPECT_EQ(processor()->GetEntities().size(), 1u);
@@ -674,6 +679,123 @@
       ui::PAGE_TRANSITION_LINK));
 }
 
+TEST_F(HistorySyncBridgeTest, SplitsRedirectChainWithDifferentTimestamps) {
+  // Start syncing (with no data yet).
+  ApplyInitialSyncChanges({});
+
+  // Create a redirect chain with 2 entries.
+  URLRow url_row1(GURL("https://url1.com"));
+  URLID url_id1 = backend()->AddURL(url_row1);
+  url_row1.set_id(url_id1);
+  URLRow url_row2(GURL("https://url2.com"));
+  URLID url_id2 = backend()->AddURL(url_row2);
+  url_row2.set_id(url_id2);
+
+  const base::Time visit_time_chain1 = base::Time::Now();
+
+  VisitRow visit_row1;
+  visit_row1.url_id = url_id1;
+  visit_row1.visit_time = visit_time_chain1;
+  visit_row1.transition = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_START |
+      ui::PAGE_TRANSITION_SERVER_REDIRECT);
+  visit_row1.visit_id = backend()->AddVisit(visit_row1);
+
+  VisitRow visit_row2;
+  visit_row2.referring_visit = visit_row1.visit_id;
+  visit_row2.url_id = url_id2;
+  visit_row2.visit_time = visit_time_chain1;
+  visit_row2.transition = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_END);
+  visit_row2.visit_id = backend()->AddVisit(visit_row2);
+
+  // Notify the bridge about the visits.
+  bridge()->OnURLVisited(
+      /*history_backend=*/nullptr, url_row1, visit_row1);
+  bridge()->OnURLVisited(
+      /*history_backend=*/nullptr, url_row2, visit_row2);
+
+  // The chain should have resulted in an entity being Put().
+  const std::string storage_key1 =
+      HistorySyncMetadataDatabase::StorageKeyFromVisitTime(visit_time_chain1);
+  ASSERT_EQ(processor()->GetEntities().size(), 1u);
+  ASSERT_EQ(processor()->GetEntities().count(storage_key1), 1u);
+  sync_pb::HistorySpecifics history1 =
+      processor()->GetEntities().at(storage_key1).specifics.history();
+  ASSERT_EQ(history1.redirect_entries_size(), 2);
+  ASSERT_FALSE(history1.redirect_chain_start_incomplete());
+  ASSERT_FALSE(history1.redirect_chain_end_incomplete());
+
+  // Now, the existing chain gets extended.
+  // First, the PAGE_TRANSITION_CHAIN_END bit gets removed from the existing
+  // visit.
+  visit_row2.transition = ui::PAGE_TRANSITION_LINK;
+  ASSERT_TRUE(backend()->UpdateVisit(visit_row2));
+  // The bridge gets notified about the updated visit, but this should have no
+  // effect since it's not a chain end anymore.
+  bridge()->OnVisitUpdated(visit_row2);
+
+  // Two more visits get appended to the chain.
+  URLRow url_row3(GURL("https://url3.com"));
+  URLID url_id3 = backend()->AddURL(url_row3);
+  url_row3.set_id(url_id3);
+  URLRow url_row4(GURL("https://url4.com"));
+  URLID url_id4 = backend()->AddURL(url_row4);
+  url_row4.set_id(url_id4);
+
+  AdvanceClock();
+  const base::Time visit_time_chain2 = base::Time::Now();
+
+  VisitRow visit_row3;
+  // Link to the previous chain!
+  visit_row3.referring_visit = visit_row2.visit_id;
+  visit_row3.url_id = url_id3;
+  visit_row3.visit_time = visit_time_chain2;
+  // Note: No PAGE_TRANSITION_CHAIN_START.
+  visit_row3.transition = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
+  visit_row3.visit_id = backend()->AddVisit(visit_row3);
+
+  VisitRow visit_row4;
+  visit_row4.referring_visit = visit_row3.visit_id;
+  visit_row4.url_id = url_id4;
+  visit_row4.visit_time = visit_time_chain2;
+  visit_row4.transition = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CHAIN_END);
+  visit_row4.visit_id = backend()->AddVisit(visit_row4);
+
+  // Notify the bridge about the new visits.
+  bridge()->OnURLVisited(
+      /*history_backend=*/nullptr, url_row3, visit_row3);
+  bridge()->OnURLVisited(
+      /*history_backend=*/nullptr, url_row4, visit_row4);
+
+  // Now, there should be two entities: The one from the initial chain, and a
+  // separate one for the later addition.
+  const std::string storage_key2 =
+      HistorySyncMetadataDatabase::StorageKeyFromVisitTime(visit_time_chain2);
+  ASSERT_EQ(processor()->GetEntities().size(), 2u);
+  ASSERT_EQ(processor()->GetEntities().count(storage_key1), 1u);
+  ASSERT_EQ(processor()->GetEntities().count(storage_key2), 1u);
+  // The initial chain should not have the chain_end marker anymore, but be
+  // otherwise unmodified.
+  sync_pb::HistorySpecifics history1_expected = history1;
+  history1_expected.set_redirect_chain_end_incomplete(true);
+  sync_pb::HistorySpecifics history1_updated =
+      processor()->GetEntities().at(storage_key1).specifics.history();
+  EXPECT_EQ(*syncer::HistorySpecificsToValue(history1_expected),
+            *syncer::HistorySpecificsToValue(history1_updated));
+  // The second chain should contain only the last two entries.
+  sync_pb::HistorySpecifics history2 =
+      processor()->GetEntities().at(storage_key2).specifics.history();
+  ASSERT_EQ(history2.redirect_entries_size(), 2);
+  EXPECT_EQ(history2.redirect_entries(0).url(), url_row3.url());
+  EXPECT_EQ(history2.redirect_entries(1).url(), url_row4.url());
+  EXPECT_EQ(history2.originator_referring_visit_id(), visit_row2.visit_id);
+  EXPECT_TRUE(history2.redirect_chain_start_incomplete());
+  EXPECT_FALSE(history2.redirect_chain_end_incomplete());
+}
+
 TEST_F(HistorySyncBridgeTest, UntracksEntitiesAfterCommit) {
   // Start syncing (with no data yet).
   ApplyInitialSyncChanges({});
diff --git a/components/metrics/demographics/demographic_metrics_provider_unittest.cc b/components/metrics/demographics/demographic_metrics_provider_unittest.cc
index fb445e0..5bb8dfd 100644
--- a/components/metrics/demographics/demographic_metrics_provider_unittest.cc
+++ b/components/metrics/demographics/demographic_metrics_provider_unittest.cc
@@ -75,12 +75,7 @@
       case SYNC_FEATURE_ENABLED_BUT_PAUSED:
         sync_service_ = std::make_unique<syncer::TestSyncService>();
         // Mimic the user signing out from content are (sync paused).
-        sync_service_->SetAuthError(
-            GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
-                GoogleServiceAuthError::InvalidGaiaCredentialsReason::
-                    CREDENTIALS_REJECTED_BY_CLIENT));
-        sync_service_->SetTransportState(
-            syncer::SyncService::TransportState::PAUSED);
+        sync_service_->SetPersistentAuthErrorWithWebSignout();
 
         CHECK(sync_service_->GetUserSettings()->IsSyncRequested());
         CHECK(sync_service_->GetDisableReasons().Empty());
diff --git a/components/ownership/mock_owner_key_util.cc b/components/ownership/mock_owner_key_util.cc
index 851d772c..1ac2f95 100644
--- a/components/ownership/mock_owner_key_util.cc
+++ b/components/ownership/mock_owner_key_util.cc
@@ -14,13 +14,35 @@
 
 namespace ownership {
 
+static const uint16_t kKeySizeInBits = 2048;
+
 MockOwnerKeyUtil::MockOwnerKeyUtil() = default;
 
 MockOwnerKeyUtil::~MockOwnerKeyUtil() = default;
 
-bool MockOwnerKeyUtil::ImportPublicKey(std::vector<uint8_t>* output) {
-  *output = public_key_;
-  return !public_key_.empty();
+scoped_refptr<PublicKey> MockOwnerKeyUtil::ImportPublicKey() {
+  return public_key_.empty() ? nullptr
+                             : base::MakeRefCounted<ownership::PublicKey>(
+                                   /*is_persisted=*/true, /*data=*/public_key_);
+}
+
+crypto::ScopedSECKEYPrivateKey MockOwnerKeyUtil::GenerateKeyPair(
+    PK11SlotInfo* slot) {
+  if (generate_key_fail_times_ > 0) {
+    --generate_key_fail_times_;
+    return nullptr;
+  }
+
+  PK11RSAGenParams param;
+  param.keySizeInBits = kKeySizeInBits;
+  param.pe = 65537L;
+  SECKEYPublicKey* public_key_ptr = nullptr;
+
+  crypto::ScopedSECKEYPrivateKey key(PK11_GenerateKeyPair(
+      slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &param, &public_key_ptr,
+      PR_TRUE /* permanent */, PR_TRUE /* sensitive */, nullptr));
+  crypto::ScopedSECKEYPublicKey public_key(public_key_ptr);
+  return key;
 }
 
 crypto::ScopedSECKEYPrivateKey MockOwnerKeyUtil::FindPrivateKeyInSlot(
@@ -66,4 +88,8 @@
   CHECK(private_key_);
 }
 
+void MockOwnerKeyUtil::SimulateGenerateKeyFailure(int fail_times) {
+  generate_key_fail_times_ = fail_times;
+}
+
 }  // namespace ownership
diff --git a/components/ownership/mock_owner_key_util.h b/components/ownership/mock_owner_key_util.h
index 360244cb..fb96799 100644
--- a/components/ownership/mock_owner_key_util.h
+++ b/components/ownership/mock_owner_key_util.h
@@ -30,7 +30,8 @@
   MockOwnerKeyUtil& operator=(const MockOwnerKeyUtil&) = delete;
 
   // OwnerKeyUtil implementation:
-  bool ImportPublicKey(std::vector<uint8_t>* output) override;
+  scoped_refptr<PublicKey> ImportPublicKey() override;
+  crypto::ScopedSECKEYPrivateKey GenerateKeyPair(PK11SlotInfo* slot) override;
   crypto::ScopedSECKEYPrivateKey FindPrivateKeyInSlot(
       const std::vector<uint8_t>& key,
       PK11SlotInfo* slot) override;
@@ -52,9 +53,14 @@
   void ImportPrivateKeyAndSetPublicKey(
       std::unique_ptr<crypto::RSAPrivateKey> key);
 
+  // Makes next `fail_times` number of calls to OwnerKeyUtil::GenerateKeyPair
+  // fail.
+  void SimulateGenerateKeyFailure(int fail_times);
+
  private:
   ~MockOwnerKeyUtil() override;
 
+  int generate_key_fail_times_ = 0;
   std::vector<uint8_t> public_key_;
   crypto::ScopedSECKEYPrivateKey private_key_;
 };
diff --git a/components/ownership/owner_key_util.cc b/components/ownership/owner_key_util.cc
index 5ce3513..177e233 100644
--- a/components/ownership/owner_key_util.cc
+++ b/components/ownership/owner_key_util.cc
@@ -11,10 +11,13 @@
 ///////////////////////////////////////////////////////////////////////////
 // PublicKey
 
-PublicKey::PublicKey() {
-}
+PublicKey::PublicKey(bool is_persisted, std::vector<uint8_t> data)
+    : is_persisted_(is_persisted), data_(std::move(data)) {}
 
-PublicKey::~PublicKey() {
+PublicKey::~PublicKey() = default;
+
+scoped_refptr<PublicKey> PublicKey::clone() {
+  return base::MakeRefCounted<ownership::PublicKey>(is_persisted_, data_);
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -23,7 +26,6 @@
 PrivateKey::PrivateKey(crypto::ScopedSECKEYPrivateKey key)
     : key_(std::move(key)) {}
 
-PrivateKey::~PrivateKey() {
-}
+PrivateKey::~PrivateKey() = default;
 
 }  // namespace ownership
diff --git a/components/ownership/owner_key_util.h b/components/ownership/owner_key_util.h
index b135480..2d386686 100644
--- a/components/ownership/owner_key_util.h
+++ b/components/ownership/owner_key_util.h
@@ -23,14 +23,27 @@
 class OWNERSHIP_EXPORT PublicKey
     : public base::RefCountedThreadSafe<PublicKey> {
  public:
-  PublicKey();
+  // `is_persisted` should be true for keys that are loaded from disk and false
+  // for newly generated ones. `data` is the binary representation of the key
+  // itself.
+  PublicKey(bool is_persisted, std::vector<uint8_t> data);
 
   PublicKey(const PublicKey&) = delete;
   PublicKey& operator=(const PublicKey&) = delete;
 
+  scoped_refptr<PublicKey> clone();
+
   std::vector<uint8_t>& data() { return data_; }
 
-  bool is_loaded() const { return !data_.empty(); }
+  bool is_empty() const { return data_.empty(); }
+
+  // Returns true if the key was read from the filesystem or it was already
+  // saved on disk. Returns false for recently generated keys that still need
+  // to be sent to session_manager for saving on disk.
+  bool is_persisted() { return is_persisted_; }
+
+  // Marks that the key was saved on disk.
+  void mark_persisted() { is_persisted_ = true; }
 
   std::string as_string() {
     return std::string(reinterpret_cast<const char*>(data_.data()),
@@ -42,6 +55,7 @@
 
   virtual ~PublicKey();
 
+  bool is_persisted_ = false;
   std::vector<uint8_t> data_;
 };
 
@@ -70,9 +84,13 @@
 class OWNERSHIP_EXPORT OwnerKeyUtil
     : public base::RefCountedThreadSafe<OwnerKeyUtil> {
  public:
-  // Attempts to read the public key from the file system.  Upon success,
-  // returns true and populates |output|.  False on failure.
-  virtual bool ImportPublicKey(std::vector<uint8_t>* output) = 0;
+  // Attempts to read the public key from the file system. Returns nullptr on
+  // failure and a populated key on success.
+  virtual scoped_refptr<PublicKey> ImportPublicKey() = 0;
+
+  // Generates a new key pair in the `slot`.
+  virtual crypto::ScopedSECKEYPrivateKey GenerateKeyPair(
+      PK11SlotInfo* slot) = 0;
 
   // Looks for the private key associated with |key| in the |slot|
   // and returns it if it can be found.  Returns NULL otherwise.
diff --git a/components/ownership/owner_key_util_impl.cc b/components/ownership/owner_key_util_impl.cc
index 875c761..bb5c1dd41 100644
--- a/components/ownership/owner_key_util_impl.cc
+++ b/components/ownership/owner_key_util_impl.cc
@@ -16,14 +16,14 @@
 
 namespace ownership {
 
+static const uint16_t kKeySizeInBits = 2048;
+
 OwnerKeyUtilImpl::OwnerKeyUtilImpl(const base::FilePath& public_key_file)
-    : public_key_file_(public_key_file) {
-}
+    : public_key_file_(public_key_file) {}
 
-OwnerKeyUtilImpl::~OwnerKeyUtilImpl() {
-}
+OwnerKeyUtilImpl::~OwnerKeyUtilImpl() = default;
 
-bool OwnerKeyUtilImpl::ImportPublicKey(std::vector<uint8_t>* output) {
+scoped_refptr<PublicKey> OwnerKeyUtilImpl::ImportPublicKey() {
   // Get the file size (must fit in a 32 bit int for NSS).
   int64_t file_size;
   if (!base::GetFileSize(public_key_file_, &file_size)) {
@@ -31,27 +31,49 @@
     LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
         << "Could not get size of " << public_key_file_.value();
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-    return false;
+    return nullptr;
   }
   if (file_size > static_cast<int64_t>(std::numeric_limits<int>::max())) {
     LOG(ERROR) << public_key_file_.value() << "is " << file_size
                << "bytes!!!  Too big!";
-    return false;
+    return nullptr;
   }
   int32_t safe_file_size = static_cast<int32_t>(file_size);
 
-  output->resize(safe_file_size);
+  std::vector<uint8_t> key_data;
+  key_data.resize(safe_file_size);
 
   if (safe_file_size == 0) {
     LOG(WARNING) << "Public key file is empty. This seems wrong.";
-    return false;
+    return nullptr;
   }
 
   // Get the key data off of disk
   int data_read =
-      base::ReadFile(public_key_file_, reinterpret_cast<char*>(output->data()),
+      base::ReadFile(public_key_file_, reinterpret_cast<char*>(key_data.data()),
                      safe_file_size);
-  return data_read == safe_file_size;
+  if (data_read != safe_file_size) {
+    return nullptr;
+  }
+
+  return base::MakeRefCounted<ownership::PublicKey>(
+      /*is_persisted=*/true, std::move(key_data));
+}
+
+crypto::ScopedSECKEYPrivateKey OwnerKeyUtilImpl::GenerateKeyPair(
+    PK11SlotInfo* slot) {
+  DCHECK(slot);
+
+  PK11RSAGenParams param;
+  param.keySizeInBits = kKeySizeInBits;
+  param.pe = 65537L;
+  SECKEYPublicKey* public_key_ptr = nullptr;
+
+  crypto::ScopedSECKEYPrivateKey key(PK11_GenerateKeyPair(
+      slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &param, &public_key_ptr,
+      PR_TRUE /* permanent */, PR_TRUE /* sensitive */, nullptr));
+  crypto::ScopedSECKEYPublicKey public_key(public_key_ptr);
+  return key;
 }
 
 crypto::ScopedSECKEYPrivateKey OwnerKeyUtilImpl::FindPrivateKeyInSlot(
diff --git a/components/ownership/owner_key_util_impl.h b/components/ownership/owner_key_util_impl.h
index 7b950f7..b4f6334 100644
--- a/components/ownership/owner_key_util_impl.h
+++ b/components/ownership/owner_key_util_impl.h
@@ -24,7 +24,8 @@
   OwnerKeyUtilImpl& operator=(const OwnerKeyUtilImpl&) = delete;
 
   // OwnerKeyUtil implementation:
-  bool ImportPublicKey(std::vector<uint8_t>* output) override;
+  scoped_refptr<PublicKey> ImportPublicKey() override;
+  crypto::ScopedSECKEYPrivateKey GenerateKeyPair(PK11SlotInfo* slot) override;
   crypto::ScopedSECKEYPrivateKey FindPrivateKeyInSlot(
       const std::vector<uint8_t>& key,
       PK11SlotInfo* slot) override;
diff --git a/components/ownership/owner_key_util_impl_unittest.cc b/components/ownership/owner_key_util_impl_unittest.cc
index 182fa47..ba72900 100644
--- a/components/ownership/owner_key_util_impl_unittest.cc
+++ b/components/ownership/owner_key_util_impl_unittest.cc
@@ -76,24 +76,22 @@
                             public_key.size()));
   EXPECT_TRUE(util_->IsPublicKeyPresent());
 
-  std::vector<uint8_t> from_disk;
-  EXPECT_TRUE(util_->ImportPublicKey(&from_disk));
+  scoped_refptr<PublicKey> from_disk = util_->ImportPublicKey();
+  EXPECT_TRUE(from_disk);
 
-  EXPECT_EQ(public_key, from_disk);
+  EXPECT_EQ(public_key, from_disk->data());
+  EXPECT_EQ(true, from_disk->is_persisted());
 }
 
 TEST_F(OwnerKeyUtilImplTest, ImportPublicKeyFailed) {
   // First test the case where the file is missing which should fail.
   EXPECT_FALSE(util_->IsPublicKeyPresent());
-  std::vector<uint8_t> from_disk;
-  EXPECT_FALSE(util_->ImportPublicKey(&from_disk));
+  EXPECT_FALSE(util_->ImportPublicKey());
 
-  // Next try empty file. This should fail and the array should be empty.
-  from_disk.resize(10);
+  // Next try empty file. This should fail as well.
   ASSERT_EQ(0, base::WriteFile(key_file_, "", 0));
   EXPECT_TRUE(util_->IsPublicKeyPresent());
-  EXPECT_FALSE(util_->ImportPublicKey(&from_disk));
-  EXPECT_FALSE(from_disk.size());
+  EXPECT_FALSE(util_->ImportPublicKey());
 }
 
 }  // namespace ownership
diff --git a/components/ownership/owner_settings_service.cc b/components/ownership/owner_settings_service.cc
index b2c4dc97..ddbd7f1 100644
--- a/components/ownership/owner_settings_service.cc
+++ b/components/ownership/owner_settings_service.cc
@@ -168,7 +168,9 @@
   // loading (even if unsuccessfully). Absence of the actual data inside can
   // indicate that the keys are unavailable.
   public_key_ =
-      public_key ? public_key : base::MakeRefCounted<ownership::PublicKey>();
+      public_key ? public_key
+                 : base::MakeRefCounted<ownership::PublicKey>(
+                       /*is_persisted=*/false, /*data=*/std::vector<uint8_t>());
   private_key_ = private_key
                      ? private_key
                      : base::MakeRefCounted<ownership::PrivateKey>(nullptr);
diff --git a/components/password_manager/content/browser/content_password_manager_driver_factory_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_factory_unittest.cc
index 05eb2c1d..71cf094 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_factory_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_factory_unittest.cc
@@ -29,7 +29,7 @@
  public:
   ContentPasswordManagerDriverFactoryFencedFramesTest() {
     std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> disabled;
     enabled.push_back(
         {blink::features::kFencedFrames, {{"implementation_type", "mparch"}}});
     if (password_manager_enabled_in_fencedframe()) {
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
index 1339559..9dc0438 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -139,7 +139,7 @@
 // server-predicted clear-text fields is enabled.
 bool IsPasswordGenerationForClearTextFieldsEnabled() {
   return base::FeatureList::IsEnabled(
-      password_manager::features::KEnablePasswordGenerationForClearTextFields);
+      password_manager::features::kEnablePasswordGenerationForClearTextFields);
 }
 
 // Returns true iff |field_type| is one of password types.
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 9b8ab8f9..0fe0bce 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -1145,7 +1145,7 @@
 TEST(FormParserTest, ServerPredictionsForClearTextPasswordFields) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
-      password_manager::features::KEnablePasswordGenerationForClearTextFields);
+      password_manager::features::kEnablePasswordGenerationForClearTextFields);
   CheckTestData({
       {
           .description_for_logging = "Server prediction for account change "
diff --git a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
index b380068..55319a3a 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
@@ -88,7 +88,7 @@
 class LeakDetectionDelegateTest : public testing::Test {
  public:
   explicit LeakDetectionDelegateTest(
-      const std::vector<base::Feature>& enabled_features) {
+      const std::vector<base::test::FeatureRef>& enabled_features) {
     features_.InitWithFeatures(enabled_features, {});
 
     auto mock_factory =
@@ -105,7 +105,7 @@
   }
 
   LeakDetectionDelegateTest()
-      : LeakDetectionDelegateTest(std::vector<base::Feature>()) {}
+      : LeakDetectionDelegateTest(std::vector<base::test::FeatureRef>()) {}
 
   ~LeakDetectionDelegateTest() override = default;
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index fba084e1..5746abf 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -147,7 +147,7 @@
 bool HasNewPasswordVote(const FormPredictions& form) {
   if (!base::FeatureList::IsEnabled(
           password_manager::features::
-              KEnablePasswordGenerationForClearTextFields))
+              kEnablePasswordGenerationForClearTextFields))
     return false;
   for (const auto& field : form.fields) {
     if (field.type == ACCOUNT_CREATION_PASSWORD || field.type == NEW_PASSWORD)
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 0909268..f6c2495 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -3513,7 +3513,7 @@
        MarkServerPredictedClearTextPasswordFieldEligibleForGeneration) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
-      password_manager::features::KEnablePasswordGenerationForClearTextFields);
+      password_manager::features::kEnablePasswordGenerationForClearTextFields);
 
   PasswordFormManager::set_wait_for_server_predictions_for_filling(true);
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(_))
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
index f97ca9e..42646191 100644
--- a/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
+++ b/components/password_manager/core/browser/password_store_backend_migration_decorator_unittest.cc
@@ -494,8 +494,7 @@
   // Set password sync to be active and have no auth errors.
   InitSyncService(/*is_password_sync_enabled=*/true);
   sync_service().SetActiveDataTypes(syncer::ModelTypeSet(syncer::PASSWORDS));
-  sync_service().SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  sync_service().ClearAuthError();
 
   // Migration attemot will start and will trigger logins retrieval from the
   // built-in backend.
@@ -530,8 +529,7 @@
   // Set password sync to be enabled in settings, but inactive.
   InitSyncService(/*is_password_sync_enabled=*/true);
   sync_service().SetActiveDataTypes({});
-  sync_service().SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
+  sync_service().SetPersistentAuthErrorOtherThanWebSignout();
 
   // Reenrolling migration attempt should not happen, logins should not be
   // retrieved.
@@ -572,8 +570,7 @@
   // Set password sync to be active and have no auth errors.
   InitSyncService(/*is_password_sync_enabled=*/true);
   sync_service().SetActiveDataTypes(syncer::ModelTypeSet(syncer::PASSWORDS));
-  sync_service().SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  sync_service().ClearAuthError();
 
   // Reenrolling migration attempt should not happen, logins should not be
   // retrieved.
@@ -614,8 +611,7 @@
   // Set password sync to be active and have no auth errors.
   InitSyncService(/*is_password_sync_enabled=*/true);
   sync_service().SetActiveDataTypes(syncer::ModelTypeSet(syncer::PASSWORDS));
-  sync_service().SetAuthError(
-      GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  sync_service().ClearAuthError();
 
   // Reenrolling migration attempt should not happen, logins should not be
   // retrieved.
diff --git a/components/password_manager/core/browser/password_sync_util_unittest.cc b/components/password_manager/core/browser/password_sync_util_unittest.cc
index 0086f49..609bdb1 100644
--- a/components/password_manager/core/browser/password_sync_util_unittest.cc
+++ b/components/password_manager/core/browser/password_sync_util_unittest.cc
@@ -139,6 +139,7 @@
   sync_service.SetTransportState(syncer::SyncService::TransportState::DISABLED);
   sync_service.SetHasSyncConsent(false);
   EXPECT_FALSE(IsPasswordSyncEnabled(&sync_service));
+  EXPECT_FALSE(IsPasswordSyncActive(&sync_service));
   EXPECT_EQ(absl::nullopt, GetSyncingAccount(&sync_service));
 }
 
@@ -150,6 +151,7 @@
       ->SetSelectedTypes(/*sync_everything=*/false,
                          {syncer::UserSelectableType::kHistory});
   EXPECT_FALSE(IsPasswordSyncEnabled(&sync_service));
+  EXPECT_FALSE(IsPasswordSyncActive(&sync_service));
   EXPECT_EQ(absl::nullopt, GetSyncingAccount(&sync_service));
 }
 
@@ -161,9 +163,35 @@
   active_info.email = "test@email.com";
   sync_service.SetAccountInfo(active_info);
   EXPECT_TRUE(IsPasswordSyncEnabled(&sync_service));
+  EXPECT_TRUE(IsPasswordSyncActive(&sync_service));
   EXPECT_TRUE(GetSyncingAccount(&sync_service).has_value());
   EXPECT_EQ(active_info.email, GetSyncingAccount(&sync_service).value());
 }
 
+TEST_F(PasswordSyncUtilTest, SyncPausedDueToWebSignout) {
+  syncer::TestSyncService sync_service;
+  sync_service.SetHasSyncConsent(true);
+  sync_service.SetPersistentAuthErrorWithWebSignout();
+  ASSERT_EQ(sync_service.GetTransportState(),
+            syncer::SyncService::TransportState::PAUSED);
+  EXPECT_TRUE(IsPasswordSyncEnabled(&sync_service));
+  EXPECT_FALSE(IsPasswordSyncActive(&sync_service));
+  EXPECT_NE(absl::nullopt, GetSyncingAccount(&sync_service));
+}
+
+TEST_F(PasswordSyncUtilTest, SyncWithPersistentAuthErrorOtherThanWebSignout) {
+  syncer::TestSyncService sync_service;
+  sync_service.SetHasSyncConsent(true);
+  sync_service.SetPersistentAuthErrorOtherThanWebSignout();
+  ASSERT_NE(sync_service.GetTransportState(),
+            syncer::SyncService::TransportState::PAUSED);
+  EXPECT_TRUE(IsPasswordSyncEnabled(&sync_service));
+  // As opposed to web signout, other persistent auth errors don't cause Sync to
+  // become inactive.
+  // TODO(crbug.com/1156584): Unify the behavior for all persistent auth errors.
+  EXPECT_TRUE(IsPasswordSyncActive(&sync_service));
+  EXPECT_NE(absl::nullopt, GetSyncingAccount(&sync_service));
+}
+
 }  // namespace sync_util
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc b/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc
index 9186562c..957aff91 100644
--- a/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc
+++ b/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.cc
@@ -8,15 +8,14 @@
 #include <iterator>
 #include <map>
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/containers/cxx20_erase.h"
-#include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/scoped_observation.h"
 #include "base/stl_util.h"
 #include "base/values.h"
-#include "components/password_manager/core/browser/password_store_interface.h"
 #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "url/gurl.h"
@@ -25,10 +24,11 @@
 
 SavedPasswordsCapabilitiesFetcher::SavedPasswordsCapabilitiesFetcher(
     std::unique_ptr<CapabilitiesService> fetcher,
-    scoped_refptr<password_manager::PasswordStoreInterface> password_store)
-    : fetcher_(std::move(fetcher)), saved_passwords_presenter_(password_store) {
-  observed_saved_password_presenter_.Observe(&saved_passwords_presenter_);
-  saved_passwords_presenter_.Init();
+    std::unique_ptr<SavedPasswordsPresenter> saved_passwords_presenter)
+    : fetcher_(std::move(fetcher)),
+      saved_passwords_presenter_(std::move(saved_passwords_presenter)) {
+  observed_saved_password_presenter_.Observe(saved_passwords_presenter_.get());
+  saved_passwords_presenter_->Init();
 }
 
 SavedPasswordsCapabilitiesFetcher::~SavedPasswordsCapabilitiesFetcher() =
@@ -111,7 +111,7 @@
 
 void SavedPasswordsCapabilitiesFetcher::OnEdited(const PasswordForm& form) {
   // OnEdited() only gets called if a the password was edited via
-  // |saved_passwords_presenter_|, so even if the password gets edited
+  // `saved_passwords_presenter_`, so even if the password gets edited
   // elsewhere, we wouldn't end up here.
   NOTREACHED();
 }
@@ -243,7 +243,7 @@
 std::vector<url::Origin>
 SavedPasswordsCapabilitiesFetcher::GetOriginsOfStoredPasswords() const {
   std::vector<url::Origin> origins;
-  for (const auto& form : saved_passwords_presenter_.GetSavedPasswords()) {
+  for (const auto& form : saved_passwords_presenter_->GetSavedPasswords()) {
     if (form.url.SchemeIs(url::kHttpScheme)) {
       // Http schemes are not supported.
       continue;
diff --git a/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h b/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h
index 6c02dedb1..5bd4b3c5 100644
--- a/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h
+++ b/components/password_manager/core/browser/saved_passwords_capabilities_fetcher.h
@@ -32,7 +32,8 @@
 
   SavedPasswordsCapabilitiesFetcher(
       std::unique_ptr<CapabilitiesService> fetcher,
-      scoped_refptr<password_manager::PasswordStoreInterface> password_store);
+      std::unique_ptr<password_manager::SavedPasswordsPresenter>
+          saved_passwords_presenter);
 
   SavedPasswordsCapabilitiesFetcher(const SavedPasswordsCapabilitiesFetcher&) =
       delete;
@@ -90,7 +91,8 @@
   std::unique_ptr<CapabilitiesService> fetcher_;
 
   // Manages the list of saved passwords, including updates.
-  password_manager::SavedPasswordsPresenter saved_passwords_presenter_;
+  std::unique_ptr<password_manager::SavedPasswordsPresenter>
+      saved_passwords_presenter_;
 
   // Stores the callbacks that are waiting for the refresh capabilities request
   // to finish.
diff --git a/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc b/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc
index 74f8a1c..2dd4d57 100644
--- a/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc
+++ b/components/password_manager/core/browser/saved_passwords_capabilities_fetcher_unittest.cc
@@ -16,6 +16,7 @@
 #include "components/password_manager/core/browser/capabilities_service.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -111,12 +112,14 @@
  public:
   SavedPasswordsCapabilitiesFetcherTest() {
     store_->Init(/*prefs=*/nullptr, /*affiliated_match_helper=*/nullptr);
+    FillPasswordStore();
     auto capabilities_service =
         std::make_unique<NiceMock<MockCapabilitiesService>>();
     mock_capabilities_service_ = capabilities_service.get();
     fetcher_ = std::make_unique<SavedPasswordsCapabilitiesFetcher>(
-        std::move(capabilities_service), store_);
-    FillPasswordStore();
+        std::move(capabilities_service),
+        std::make_unique<SavedPasswordsPresenter>(store_));
+    RunUntilIdle();
   }
 
   ~SavedPasswordsCapabilitiesFetcherTest() override {
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 8a80ee4..23ef8e6 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -69,9 +69,9 @@
 #endif
 );
 
-const base::Feature KEnablePasswordGenerationForClearTextFields = {
-    "EnablePasswordGenerationForClearTextFields",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+BASE_FEATURE(kEnablePasswordGenerationForClearTextFields,
+             "EnablePasswordGenerationForClearTextFields",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // By default, Password Manager is disabled in fenced frames for now.
 // TODO(crbug.com/1294378): Remove once launched.
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 245da59..932926b 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -34,7 +34,7 @@
 BASE_DECLARE_FEATURE(kEnableFaviconForPasswords);
 BASE_DECLARE_FEATURE(kEnableOverwritingPlaceholderUsernames);
 BASE_DECLARE_FEATURE(kEnablePasswordsAccountStorage);
-extern const base::Feature KEnablePasswordGenerationForClearTextFields;
+BASE_DECLARE_FEATURE(kEnablePasswordGenerationForClearTextFields);
 BASE_DECLARE_FEATURE(kEnablePasswordManagerWithinFencedFrame);
 BASE_DECLARE_FEATURE(kFillingAcrossAffiliatedWebsites);
 BASE_DECLARE_FEATURE(kFillOnAccountSelect);
diff --git a/components/safe_browsing/content/browser/password_protection/password_protection_service.cc b/components/safe_browsing/content/browser/password_protection/password_protection_service.cc
index 1aa3632..a38f90c 100644
--- a/components/safe_browsing/content/browser/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/content/browser/password_protection/password_protection_service.cc
@@ -108,22 +108,6 @@
   }
 }
 
-PasswordReuseInfo PasswordProtectionService::ConstructPasswordReuseInfo(
-    uint64_t reused_password_hash,
-    const std::string& username,
-    PasswordType password_type,
-    std::vector<std::string> matching_domains) {
-  PasswordReuseInfo pw_reuse_info;
-  pw_reuse_info.matches_signin_password =
-      password_type == PasswordType::PRIMARY_ACCOUNT_PASSWORD;
-  pw_reuse_info.matching_domains = matching_domains;
-  pw_reuse_info.reused_password_account_type =
-      GetPasswordProtectionReusedPasswordAccountType(password_type, username);
-  pw_reuse_info.count = 1;
-  pw_reuse_info.reused_password_hash = reused_password_hash;
-  return pw_reuse_info;
-}
-
 void PasswordProtectionService::StartRequest(
     WebContents* web_contents,
     const GURL& main_frame_url,
diff --git a/components/safe_browsing/content/browser/password_protection/password_protection_service.h b/components/safe_browsing/content/browser/password_protection/password_protection_service.h
index 7f6dfa6..0fce0e40 100644
--- a/components/safe_browsing/content/browser/password_protection/password_protection_service.h
+++ b/components/safe_browsing/content/browser/password_protection/password_protection_service.h
@@ -128,13 +128,6 @@
   MaybeCreateCommitDeferringCondition(
       content::NavigationHandle& navigation_handle);
 
-  // Exports the password reuse event info to a struct.
-  PasswordReuseInfo ConstructPasswordReuseInfo(
-      uint64_t reused_password_hash,
-      const std::string& username,
-      PasswordType reuse_password_type,
-      std::vector<std::string> matching_domains);
-
  protected:
   void RemoveWarningRequestsByWebContents(content::WebContents* web_contents);
 
diff --git a/components/safe_browsing/core/common/proto/csd.proto b/components/safe_browsing/core/common/proto/csd.proto
index e24f4f3..d332f2fe 100644
--- a/components/safe_browsing/core/common/proto/csd.proto
+++ b/components/safe_browsing/core/common/proto/csd.proto
@@ -1923,6 +1923,8 @@
         // pages.
         optional uint32 count = 4;
       }
+      // The remote host(s) contacted within 1 second of at least one (but not
+      // all) of the password reuse event(s) listed below.
       repeated RemoteHostInfo remote_hosts = 1;
       repeated PasswordReuseInfo reused_password_infos = 2;
     }
diff --git a/components/signin/core/browser/signin_header_helper_unittest.cc b/components/signin/core/browser/signin_header_helper_unittest.cc
index 41f1aa4..a6cf3c43 100644
--- a/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -240,6 +240,16 @@
 }
 #endif
 
+// Tests that no Mirror request is returned for youtubekids.com.
+//
+// Regression test for b/247647476
+TEST_F(SigninHeaderHelperTest, TestNoMirrorHeaderForYoutubekids) {
+  account_consistency_ = AccountConsistencyMethod::kMirror;
+  CheckMirrorHeaderRequest(GURL("https://youtubekids.com"), "0123456789",
+                           /*is_child_account=*/Tribool::kUnknown, "");
+  CheckMirrorCookieRequest(GURL("https://youtubekids.com"), "0123456789", "");
+}
+
 // Tests that no Mirror request is returned when the cookies aren't allowed to
 // be set.
 TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestCookieSettingBlocked) {
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index d96142c..aa8ab6f 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -189,12 +189,6 @@
       "//testing/gmock",
     ]
   }
-
-  # TODO(crbug.com/1249845): Remove this dependency once
-  # SyncSettingsCategorization feature flag is removed.
-  if (is_chromeos_ash) {
-    deps += [ "//ash/constants" ]
-  }
 }
 
 source_set("unit_tests") {
diff --git a/components/sync/driver/data_type_controller.h b/components/sync/driver/data_type_controller.h
index e7f531b..a99a520 100644
--- a/components/sync/driver/data_type_controller.h
+++ b/components/sync/driver/data_type_controller.h
@@ -16,7 +16,6 @@
 #include "base/values.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine/shutdown_reason.h"
-#include "components/sync/model/data_type_error_handler.h"
 
 namespace syncer {
 
diff --git a/components/sync/driver/model_load_manager.cc b/components/sync/driver/model_load_manager.cc
index d901f5c..39397c8 100644
--- a/components/sync/driver/model_load_manager.cc
+++ b/components/sync/driver/model_load_manager.cc
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/model/sync_error.h"
 
 namespace syncer {
 
diff --git a/components/sync/driver/model_type_controller.cc b/components/sync/driver/model_type_controller.cc
index fde0c7fa..3fd1bf7 100644
--- a/components/sync/driver/model_type_controller.cc
+++ b/components/sync/driver/model_type_controller.cc
@@ -9,13 +9,12 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/signin/public/identity_manager/account_info.h"
-#include "components/sync/base/data_type_histogram.h"
 #include "components/sync/driver/configure_context.h"
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/model/data_type_activation_request.h"
-#include "components/sync/model/data_type_error_handler_impl.h"
 #include "components/sync/model/type_entities_count.h"
 
 namespace syncer {
diff --git a/components/sync/driver/sync_service_utils_unittest.cc b/components/sync/driver/sync_service_utils_unittest.cc
index 92e024c..2d37f2e 100644
--- a/components/sync/driver/sync_service_utils_unittest.cc
+++ b/components/sync/driver/sync_service_utils_unittest.cc
@@ -151,27 +151,21 @@
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
   // On a transient error, uploading goes back to INITIALIZING.
-  GoogleServiceAuthError transient_error(
-      GoogleServiceAuthError::CONNECTION_FAILED);
-  ASSERT_TRUE(transient_error.IsTransientError());
-  service.SetAuthError(transient_error);
+  service.SetTransientAuthError();
 
   EXPECT_EQ(UploadState::INITIALIZING,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
   // On a persistent error, uploading is not considered active anymore (even
   // though Sync may still be considered active).
-  GoogleServiceAuthError persistent_error(
-      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
-  ASSERT_TRUE(persistent_error.IsPersistentError());
-  service.SetAuthError(persistent_error);
+  service.SetPersistentAuthErrorOtherThanWebSignout();
 
   EXPECT_EQ(UploadState::NOT_ACTIVE,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
   // Once the auth error is resolved (e.g. user re-authenticated), uploading is
   // active again.
-  service.SetAuthError(GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  service.ClearAuthError();
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
 
   EXPECT_EQ(UploadState::ACTIVE,
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc b/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
index 25e4950f4..250cf51 100644
--- a/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
+++ b/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
@@ -45,20 +45,28 @@
   }
 
   void SetInvalidCredentialsAuthError() {
-    GoogleServiceAuthError auth_error(
-        GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+    sync_service_.SetPersistentAuthErrorOtherThanWebSignout();
+
+    const GoogleServiceAuthError auth_error = sync_service_.GetAuthError();
+    CHECK_EQ(auth_error.state(),
+             GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+
     identity_test_env_.UpdatePersistentErrorOfRefreshTokenForAccount(
         identity_test_env_.identity_manager()->GetPrimaryAccountId(
             signin::ConsentLevel::kSync),
         auth_error);
-    sync_service_.SetAuthError(auth_error);
+
+    // TODO(crbug.com/1156584): This below seems off since
+    // GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS doesn't actually lead to
+    // the PAUSED state.
     sync_service_.SetTransportState(SyncService::TransportState::PAUSED);
   }
 
   void ClearAuthError() {
     identity_test_env_.SetRefreshTokenForPrimaryAccount();
-    sync_service_.SetAuthError(GoogleServiceAuthError::AuthErrorNone());
-    sync_service_.SetTransportState(SyncService::TransportState::ACTIVE);
+    sync_service_.ClearAuthError();
+    ASSERT_EQ(sync_service_.GetTransportState(),
+              SyncService::TransportState::ACTIVE);
   }
 
   std::string GetSessionHistogramName(const std::string& histogram_suffix) {
diff --git a/components/sync/model/BUILD.gn b/components/sync/model/BUILD.gn
index e0455d8..3344f3cf 100644
--- a/components/sync/model/BUILD.gn
+++ b/components/sync/model/BUILD.gn
@@ -15,9 +15,6 @@
     "data_batch.h",
     "data_type_activation_request.cc",
     "data_type_activation_request.h",
-    "data_type_error_handler.h",
-    "data_type_error_handler_impl.cc",
-    "data_type_error_handler_impl.h",
     "dummy_metadata_change_list.cc",
     "dummy_metadata_change_list.h",
     "entity_change.cc",
diff --git a/components/sync/model/data_type_error_handler.h b/components/sync/model/data_type_error_handler.h
deleted file mode 100644
index df35607..0000000
--- a/components/sync/model/data_type_error_handler.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2014 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_MODEL_DATA_TYPE_ERROR_HANDLER_H__
-#define COMPONENTS_SYNC_MODEL_DATA_TYPE_ERROR_HANDLER_H__
-
-#include <memory>
-#include <string>
-
-#include "base/location.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/model/sync_error.h"
-
-namespace syncer {
-
-class DataTypeErrorHandler {
- public:
-  virtual ~DataTypeErrorHandler() {}
-
-  // Call this to disable a datatype while it is running. This is usually
-  // called for a runtime failure that is specific to a datatype.
-  virtual void OnUnrecoverableError(const SyncError& error) = 0;
-
-  // This will create a SyncError object. This will also upload a breakpad call
-  // stack to crash server. A sync error usually means that sync has to be
-  // disabled either for that type or completely.
-  virtual SyncError CreateAndUploadError(const base::Location& location,
-                                         const std::string& message,
-                                         ModelType type) = 0;
-
-  // Create a copy of this error handler.
-  virtual std::unique_ptr<DataTypeErrorHandler> Copy() const = 0;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_MODEL_DATA_TYPE_ERROR_HANDLER_H__
diff --git a/components/sync/model/data_type_error_handler_impl.cc b/components/sync/model/data_type_error_handler_impl.cc
deleted file mode 100644
index 889dc68..0000000
--- a/components/sync/model/data_type_error_handler_impl.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 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/model/data_type_error_handler_impl.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-
-namespace syncer {
-
-DataTypeErrorHandlerImpl::DataTypeErrorHandlerImpl(
-    const scoped_refptr<base::SequencedTaskRunner>& ui_thread,
-    const base::RepeatingClosure& dump_stack,
-    const ErrorCallback& sync_callback)
-    : ui_thread_(ui_thread),
-      dump_stack_(dump_stack),
-      sync_callback_(sync_callback) {}
-
-DataTypeErrorHandlerImpl::~DataTypeErrorHandlerImpl() = default;
-
-void DataTypeErrorHandlerImpl::OnUnrecoverableError(const SyncError& error) {
-  if (!dump_stack_.is_null())
-    dump_stack_.Run();
-  UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures2",
-                            ModelTypeHistogramValue(error.model_type()));
-  ui_thread_->PostTask(error.location(), base::BindOnce(sync_callback_, error));
-}
-
-SyncError DataTypeErrorHandlerImpl::CreateAndUploadError(
-    const base::Location& location,
-    const std::string& message,
-    ModelType type) {
-  if (!dump_stack_.is_null())
-    dump_stack_.Run();
-  return SyncError(location, SyncError::DATATYPE_ERROR, message, type);
-}
-
-std::unique_ptr<DataTypeErrorHandler> DataTypeErrorHandlerImpl::Copy() const {
-  return std::make_unique<DataTypeErrorHandlerImpl>(ui_thread_, dump_stack_,
-                                                    sync_callback_);
-}
-
-}  // namespace syncer
diff --git a/components/sync/model/data_type_error_handler_impl.h b/components/sync/model/data_type_error_handler_impl.h
deleted file mode 100644
index a34f90d..0000000
--- a/components/sync/model/data_type_error_handler_impl.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2014 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_MODEL_DATA_TYPE_ERROR_HANDLER_IMPL_H__
-#define COMPONENTS_SYNC_MODEL_DATA_TYPE_ERROR_HANDLER_IMPL_H__
-
-#include <memory>
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/task/sequenced_task_runner.h"
-#include "components/sync/model/data_type_error_handler.h"
-
-namespace syncer {
-
-// The standard implementation of DataTypeErrorHandler.
-class DataTypeErrorHandlerImpl : public DataTypeErrorHandler {
- public:
-  using ErrorCallback = base::RepeatingCallback<void(const SyncError&)>;
-
-  DataTypeErrorHandlerImpl(
-      const scoped_refptr<base::SequencedTaskRunner>& ui_thread,
-      const base::RepeatingClosure& dump_stack,
-      const ErrorCallback& sync_callback);
-
-  DataTypeErrorHandlerImpl(const DataTypeErrorHandlerImpl&) = delete;
-  DataTypeErrorHandlerImpl& operator=(const DataTypeErrorHandlerImpl&) = delete;
-
-  ~DataTypeErrorHandlerImpl() override;
-
-  void OnUnrecoverableError(const SyncError& error) override;
-  SyncError CreateAndUploadError(const base::Location& location,
-                                 const std::string& message,
-                                 ModelType type) override;
-  std::unique_ptr<DataTypeErrorHandler> Copy() const override;
-
- private:
-  // The thread task runner that |sync_callback_| runs on. This is passed in
-  // separately instead of bound inside the callback because we want to be able
-  // to perform the PostTask using the error location.
-  scoped_refptr<base::SequencedTaskRunner> ui_thread_;
-
-  // The callback to dump and upload the stack from the current thread.
-  base::RepeatingClosure dump_stack_;
-
-  // The callback used to inform sync of the error on the |ui_thread_|.
-  ErrorCallback sync_callback_;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_MODEL_DATA_TYPE_ERROR_HANDLER_IMPL_H__
diff --git a/components/sync/protocol/entity_data.cc b/components/sync/protocol/entity_data.cc
index e7d98dbb..024a7d8 100644
--- a/components/sync/protocol/entity_data.cc
+++ b/components/sync/protocol/entity_data.cc
@@ -24,7 +24,7 @@
 
 EntityData& EntityData::operator=(EntityData&& other) = default;
 
-base::Value::Dict EntityData::ToDictionaryValue() {
+base::Value::Dict EntityData::ToDictionaryValue() const {
   // This is used when debugging at sync-internals page. The code in
   // sync_node_browser.js is expecting certain fields names. e.g. CTIME, MTIME,
   // and IS_DIR.
diff --git a/components/sync/protocol/entity_data.h b/components/sync/protocol/entity_data.h
index 768b301..829b472a 100644
--- a/components/sync/protocol/entity_data.h
+++ b/components/sync/protocol/entity_data.h
@@ -91,7 +91,7 @@
   bool is_deleted() const { return specifics.ByteSize() == 0; }
 
   // Dumps all info into a DictionaryValue and returns it.
-  base::Value::Dict ToDictionaryValue();
+  base::Value::Dict ToDictionaryValue() const;
 
   // Returns the estimate of dynamically allocated memory in bytes.
   size_t EstimateMemoryUsage() const;
diff --git a/components/sync/protocol/history_specifics.proto b/components/sync/protocol/history_specifics.proto
index d2e8b64..cc721bd 100644
--- a/components/sync/protocol/history_specifics.proto
+++ b/components/sync/protocol/history_specifics.proto
@@ -46,6 +46,16 @@
   // redirects, this has only one entry.
   repeated RedirectEntry redirect_entries = 3;
 
+  // Whether the redirect chain in this entity is the continuation of a prior
+  // chain, and whether a continuation of this chain exists, in other entities.
+  // These are effectively the inverse of the CHAIN_START/CHAIN_END page
+  // transition qualifiers in Chrome. They are typically both false, since the
+  // whole chain is included in `redirect_entries`, but in some cases (notably,
+  // client redirects) a redirect chain may be split up across multiple
+  // entities.
+  optional bool redirect_chain_start_incomplete = 19;
+  optional bool redirect_chain_end_incomplete = 20;
+
   message PageTransition {
     // The core transition type.
     optional SyncEnums.PageTransition core_transition = 1 [default = LINK];
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 94e3fd5f..8e2bcc7 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -789,6 +789,8 @@
   VISIT(visit_time_windows_epoch_micros);
   VISIT(originator_cache_guid);
   VISIT_REP(redirect_entries);
+  VISIT(redirect_chain_start_incomplete);
+  VISIT(redirect_chain_end_incomplete);
   VISIT(page_transition);
   VISIT(originator_referring_visit_id);
   VISIT(originator_opener_visit_id);
diff --git a/components/sync/test/test_sync_service.cc b/components/sync/test/test_sync_service.cc
index e84acb0..13952be6 100644
--- a/components/sync/test/test_sync_service.cc
+++ b/components/sync/test/test_sync_service.cc
@@ -63,8 +63,32 @@
   has_sync_consent_ = has_sync_consent;
 }
 
-void TestSyncService::SetAuthError(const GoogleServiceAuthError& auth_error) {
-  auth_error_ = auth_error;
+void TestSyncService::SetPersistentAuthErrorOtherThanWebSignout() {
+  auth_error_ = GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+      GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+          CREDENTIALS_REJECTED_BY_SERVER);
+  CHECK(auth_error_.IsPersistentError());
+}
+
+void TestSyncService::SetPersistentAuthErrorWithWebSignout() {
+  transport_state_ = TransportState::PAUSED;
+  auth_error_ = GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+      GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+          CREDENTIALS_REJECTED_BY_CLIENT);
+  CHECK(auth_error_.IsPersistentError());
+}
+
+void TestSyncService::SetTransientAuthError() {
+  auth_error_ =
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED);
+  CHECK(auth_error_.IsTransientError());
+}
+
+void TestSyncService::ClearAuthError() {
+  auth_error_ = GoogleServiceAuthError::AuthErrorNone();
+  if (transport_state_ == TransportState::PAUSED) {
+    transport_state_ = TransportState::ACTIVE;
+  }
 }
 
 void TestSyncService::SetFirstSetupComplete(bool first_setup_complete) {
@@ -187,6 +211,9 @@
 }
 
 ModelTypeSet TestSyncService::GetActiveDataTypes() const {
+  if (transport_state_ != TransportState::ACTIVE) {
+    return ModelTypeSet();
+  }
   return active_data_types_;
 }
 
diff --git a/components/sync/test/test_sync_service.h b/components/sync/test/test_sync_service.h
index fb6ab19..430c8c9 100644
--- a/components/sync/test/test_sync_service.h
+++ b/components/sync/test/test_sync_service.h
@@ -37,7 +37,16 @@
   void SetAccountInfo(const CoreAccountInfo& account_info);
   void SetHasSyncConsent(bool has_consent);
   void SetSetupInProgress(bool in_progress);
-  void SetAuthError(const GoogleServiceAuthError& auth_error);
+
+  // Setters to mimic common auth error scenarios. Note that these functions
+  // may change the transport state, as returned by GetTransportState().
+  // TODO(crbug.com/1156584): Unify the two below once all persistent auth
+  // errors are treated equally.
+  void SetPersistentAuthErrorOtherThanWebSignout();
+  void SetPersistentAuthErrorWithWebSignout();
+  void SetTransientAuthError();
+  void ClearAuthError();
+
   void SetFirstSetupComplete(bool first_setup_complete);
   // TODO(crbug.com/1356216): reconsider SetActiveDataTypes() use in tests. It
   // should set the active types from the user selected types; did not fail.
diff --git a/components/sync/test/test_sync_user_settings.cc b/components/sync/test/test_sync_user_settings.cc
index 7245b998..3644537 100644
--- a/components/sync/test/test_sync_user_settings.cc
+++ b/components/sync/test/test_sync_user_settings.cc
@@ -13,10 +13,6 @@
 #include "components/sync/engine/nigori/nigori.h"
 #include "components/sync/test/test_sync_service.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/constants/ash_features.h"
-#endif
-
 namespace syncer {
 
 ModelTypeSet UserSelectableTypesToModelTypes(
diff --git a/components/sync_sessions/proxy_tabs_data_type_controller.cc b/components/sync_sessions/proxy_tabs_data_type_controller.cc
index 042c36c7..7c62eb6 100644
--- a/components/sync_sessions/proxy_tabs_data_type_controller.cc
+++ b/components/sync_sessions/proxy_tabs_data_type_controller.cc
@@ -11,6 +11,7 @@
 #include "base/values.h"
 #include "components/sync/driver/configure_context.h"
 #include "components/sync/engine/data_type_activation_response.h"
+#include "components/sync/model/sync_error.h"
 #include "components/sync/model/type_entities_count.h"
 
 namespace sync_sessions {
diff --git a/components/translate/ios/browser/BUILD.gn b/components/translate/ios/browser/BUILD.gn
index f61c879..4f3df8e 100644
--- a/components/translate/ios/browser/BUILD.gn
+++ b/components/translate/ios/browser/BUILD.gn
@@ -21,8 +21,6 @@
     "string_clipping_util.h",
     "translate_controller.h",
     "translate_controller.mm",
-    "translate_java_script_feature.h",
-    "translate_java_script_feature.mm",
   ]
 
   deps = [
@@ -51,11 +49,6 @@
   visibility = [ ":browser" ]
   primary_script = "resources/translate_ios.js"
   sources = [ "resources/translate_ios.js" ]
-
-  deps = [
-    "//ios/web/public/js_messaging:gcrweb",
-    "//ios/web/public/js_messaging:util_scripts",
-  ]
 }
 
 source_set("unit_tests") {
diff --git a/components/translate/ios/browser/ios_translate_driver.h b/components/translate/ios/browser/ios_translate_driver.h
index 23cf1c8..4e17775f 100644
--- a/components/translate/ios/browser/ios_translate_driver.h
+++ b/components/translate/ios/browser/ios_translate_driver.h
@@ -46,6 +46,9 @@
     return language_detection_controller_.get();
   }
 
+  TranslateController* translate_controller() {
+    return translate_controller_.get();
+  }
   void OnLanguageModelFileAvailabilityChanged(bool available);
 
   // web::WebStateObserver methods.
@@ -106,6 +109,7 @@
   web::WebState* web_state_ = nullptr;
 
   base::WeakPtr<TranslateManager> translate_manager_;
+  std::unique_ptr<TranslateController> translate_controller_;
   std::unique_ptr<LanguageDetectionController> language_detection_controller_;
 
   LanguageDetectionModelService* language_detection_model_service_ = nullptr;
diff --git a/components/translate/ios/browser/ios_translate_driver.mm b/components/translate/ios/browser/ios_translate_driver.mm
index 8b3a7e5..0799747 100644
--- a/components/translate/ios/browser/ios_translate_driver.mm
+++ b/components/translate/ios/browser/ios_translate_driver.mm
@@ -75,9 +75,10 @@
           web_state, language_detection_model,
           translate_manager_->translate_client()->GetPrefs());
 
-  TranslateController::CreateForWebState(
+  translate_controller_ = std::make_unique<TranslateController>(
       web_state, JSTranslateWebFrameManagerFactory::GetInstance());
-  TranslateController::FromWebState(web_state)->set_observer(this);
+
+  translate_controller_->set_observer(this);
 }
 
 IOSTranslateDriver::~IOSTranslateDriver() {
@@ -162,14 +163,13 @@
   source_language_ = source_lang;
   target_language_ = target_lang;
   pending_page_seq_no_ = page_seq_no;
-  TranslateController::FromWebState(web_state_)
-      ->InjectTranslateScript(translate_script);
+  translate_controller_->InjectTranslateScript(translate_script);
 }
 
 void IOSTranslateDriver::RevertTranslation(int page_seq_no) {
   if (page_seq_no != page_seq_no_)
     return;  // The user navigated away.
-  TranslateController::FromWebState(web_state_)->RevertTranslation();
+  translate_controller_->RevertTranslation();
 }
 
 bool IOSTranslateDriver::IsIncognito() {
@@ -257,8 +257,7 @@
   std::string source = (source_language_ != kUnknownLanguageCode)
                            ? source_language_
                            : kAutoDetectionLanguage;
-  TranslateController::FromWebState(web_state_)
-      ->StartTranslation(source_language_, target_language_);
+  translate_controller_->StartTranslation(source_language_, target_language_);
 }
 
 void IOSTranslateDriver::OnTranslateComplete(TranslateErrors error_type,
diff --git a/components/translate/ios/browser/resources/translate_ios.js b/components/translate/ios/browser/resources/translate_ios.js
index d83f6c2..8b075c8a 100644
--- a/components/translate/ios/browser/resources/translate_ios.js
+++ b/components/translate/ios/browser/resources/translate_ios.js
@@ -6,34 +6,44 @@
  * @fileoverview Translate script for iOS that is needed in addition to the
  * cross platform script translate.js.
  *
+ * TODO(crbug.com/659442): Enable checkTypes, checkVars errors for this file.
+ * @suppress {checkTypes, checkVars}
  */
 
-import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
-import {sendWebKitMessage} from '//ios/web/public/js_messaging/resources/utils.js';
+// Requires functions from base.js
+
+/**
+ * Namespace for this module.
+ */
+__gCrWeb.translate = {};
+
+// Store message namespace object in a global __gCrWeb object referenced by a
+// string, so it does not get renamed by closure compiler during the
+// minification.
+__gCrWeb['translate'] = __gCrWeb.translate;
 
 /**
  * Defines function to install callbacks on cr.googleTranslate.
  * See translate_script.cc for usage.
  */
-function installCallbacks() {
+__gCrWeb.translate['installCallbacks'] = function() {
   /**
    * Sets a callback to inform host of the ready state of the translate element.
    */
   cr.googleTranslate.readyCallback = function() {
-    sendWebKitMessage('TranslateMessage', {
-      'command': 'ready',
-      'errorCode': cr.googleTranslate.errorCode,
-      'loadTime': cr.googleTranslate.loadTime,
-      'readyTime': cr.googleTranslate.readyTime,
-    });
+    __gCrWeb.message.invokeOnHost({
+        'command': 'translate.ready',
+        'errorCode': cr.googleTranslate.errorCode,
+        'loadTime': cr.googleTranslate.loadTime,
+        'readyTime': cr.googleTranslate.readyTime});
   };
 
   /**
    * Sets a callback to inform host of the result of translation.
    */
   cr.googleTranslate.resultCallback = function() {
-    sendWebKitMessage('TranslateMessage', {
-      'command': 'status',
+    __gCrWeb.message.invokeOnHost({
+      'command': 'translate.status',
       'errorCode': cr.googleTranslate.errorCode,
       'pageSourceLanguage': cr.googleTranslate.sourceLang,
       'translationTime': cr.googleTranslate.translationTime,
@@ -44,10 +54,11 @@
    * Sets a callback to inform host to download javascript.
    */
   cr.googleTranslate.loadJavascriptCallback = function(url) {
-    sendWebKitMessage(
-        'TranslateMessage', {'command': 'loadjavascript', 'url': url});
+    __gCrWeb.message.invokeOnHost({
+        'command': 'translate.loadjavascript',
+        'url': url});
   };
-}
+};
 
 /**
  * Redefine XMLHttpRequest's open to capture request configurations.
@@ -67,6 +78,12 @@
 }
 
 /**
+ * Translate XMLHttpRequests still outstanding.
+ * @type {Array<XMLHttpRequest>}
+ */
+__gCrWeb.translate['xhrs'] = [];
+
+/**
  * Redefine XMLHttpRequest's send to call into the browser if it matches the
  * predefined translate security origin.
  * Only redefines once because this script may be injected multiple times.
@@ -78,14 +95,13 @@
     // the browser. Else, pass it through to the original implementation.
     // |securityOrigin| is predefined by translate_script.cc.
     if (this.savedUrl.startsWith(securityOrigin)) {
-      const length = gCrWeb.translate.xhrs.push(this);
-      sendWebKitMessage('TranslateMessage', {
-        'command': 'sendrequest',
-        'method': this.savedMethod,
-        'url': this.savedUrl,
-        'body': body,
-        'requestID': length - 1,
-      });
+      const length = __gCrWeb.translate['xhrs'].push(this);
+      __gCrWeb.message.invokeOnHost({
+          'command': 'translate.sendrequest',
+          'method': this.savedMethod,
+          'url': this.savedUrl,
+          'body': body,
+          'requestID': length - 1});
     } else {
       this.realSend(body);
     }
@@ -102,10 +118,10 @@
  * @param {string} responseURL The url which the response was returned from.
  * @param {string} responseText The text received from the server.
  */
-function handleResponse(
+__gCrWeb.translate['handleResponse'] = function(
     url, requestID, status, statusText, responseURL, responseText) {
-  // Retrieve xhr object that's waiting for the response.
-  xhr = gCrWeb.translate.xhrs[requestID];
+  // Retrive xhr object that's waiting for the response.
+  xhr = __gCrWeb.translate['xhrs'][requestID];
 
   // Configure xhr as it would have been if it was sent.
   Object.defineProperties(xhr, {
@@ -120,19 +136,5 @@
   xhr.onreadystatechange();
 
   // Clean it up
-  delete gCrWeb.translate.xhrs[requestID];
-}
-
-// Mark: Public API
-
-/**
- * Translate XMLHttpRequests still outstanding.
- * @type {Array<XMLHttpRequest>}
- */
-const xhrs = [];
-
-gCrWeb.translate = {
-  installCallbacks,
-  handleResponse,
-  xhrs,
+  delete __gCrWeb.translate['xhrs'][requestID];
 };
diff --git a/components/translate/ios/browser/translate_controller.h b/components/translate/ios/browser/translate_controller.h
index 7793e40..fbb4e401 100644
--- a/components/translate/ios/browser/translate_controller.h
+++ b/components/translate/ios/browser/translate_controller.h
@@ -12,13 +12,12 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
-#include "base/values.h"
 #include "components/translate/core/common/translate_errors.h"
 #import "ios/web/public/web_state.h"
 #include "ios/web/public/web_state_observer.h"
-#import "ios/web/public/web_state_user_data.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 
+class GURL;
 class JSTranslateWebFrameManagerFactory;
 
 namespace web {
@@ -29,8 +28,7 @@
 
 // TranslateController controls the translation of the page, by injecting the
 // translate scripts and monitoring the status.
-class TranslateController : public web::WebStateObserver,
-                            public web::WebStateUserData<TranslateController> {
+class TranslateController : public web::WebStateObserver {
  public:
   // Observer class to monitor the progress of the translation.
   class Observer {
@@ -48,6 +46,9 @@
                                      double translation_time) = 0;
   };
 
+  TranslateController(web::WebState* web_state,
+                      JSTranslateWebFrameManagerFactory* js_manager_factory);
+
   TranslateController(const TranslateController&) = delete;
   TranslateController& operator=(const TranslateController&) = delete;
 
@@ -67,20 +68,12 @@
   void StartTranslation(const std::string& source_language,
                         const std::string& target_language);
 
-  // Called when a JavaScript command is received.
-  void OnJavascriptCommandReceived(const base::Value::Dict& payload);
-
   // Changes the JSTranslateWebFrameManagerFactory used by this
   // TranslateController. Only used for testing.
   void SetJsTranslateWebFrameManagerFactoryForTesting(
       JSTranslateWebFrameManagerFactory* manager);
 
  private:
-  TranslateController(web::WebState* web_state,
-                      JSTranslateWebFrameManagerFactory* js_manager_factory);
-  friend class web::WebStateUserData<TranslateController>;
-  WEB_STATE_USER_DATA_KEY_DECL();
-
   FRIEND_TEST_ALL_PREFIXES(TranslateControllerTest,
                            OnJavascriptCommandReceived);
   FRIEND_TEST_ALL_PREFIXES(TranslateControllerTest,
@@ -99,12 +92,17 @@
   FRIEND_TEST_ALL_PREFIXES(TranslateControllerTest,
                            OnTranslateSendRequestWithBadMethod);
 
+  // Called when a JavaScript command is received.
+  bool OnJavascriptCommandReceived(const base::Value& command,
+                                   const GURL& url,
+                                   bool interacting,
+                                   web::WebFrame* sender_frame);
   // Methods to handle specific JavaScript commands.
-  // The command is ignored if `payload` format is unexpected.
-  void OnTranslateReady(const base::Value::Dict& payload);
-  void OnTranslateComplete(const base::Value::Dict& payload);
-  void OnTranslateLoadJavaScript(const base::Value::Dict& payload);
-  void OnTranslateSendRequest(const base::Value::Dict& payload);
+  // Return false if the command is invalid.
+  bool OnTranslateReady(const base::Value& command);
+  bool OnTranslateComplete(const base::Value& command);
+  bool OnTranslateLoadJavaScript(const base::Value& command);
+  bool OnTranslateSendRequest(const base::Value& command);
 
   // The callback when the script is fetched or a server error occurred.
   void OnScriptFetchComplete(std::unique_ptr<std::string> response_body);
@@ -136,6 +134,9 @@
   // Used to fetch additional scripts needed for translate.
   std::unique_ptr<network::SimpleURLLoader> script_fetcher_;
 
+  // Subscription for JS message.
+  base::CallbackListSubscription subscription_;
+
   Observer* observer_;
   JSTranslateWebFrameManagerFactory* js_manager_factory_;
   base::WeakPtrFactory<TranslateController> weak_method_factory_;
diff --git a/components/translate/ios/browser/translate_controller.mm b/components/translate/ios/browser/translate_controller.mm
index 04a4e3e0..3bb1605 100644
--- a/components/translate/ios/browser/translate_controller.mm
+++ b/components/translate/ios/browser/translate_controller.mm
@@ -36,13 +36,16 @@
 
 namespace {
 
+// Prefix for the translate javascript commands. Must be kept in sync with
+// translate_ios.js.
+const char kCommandPrefix[] = "translate";
+
 // Extracts a TranslateErrors value from `value` for the given `key`. Returns
 // absl::nullopt if the value is missing or not convertible to TranslateErrors.
-absl::optional<TranslateErrors> FindTranslateErrorsKey(
-    const base::Value::Dict& value,
-    base::StringPiece key) {
+absl::optional<TranslateErrors> FindTranslateErrorsKey(const base::Value& value,
+                                                       base::StringPiece key) {
   // Does `value` contains a double value for `key`?
-  const absl::optional<double> found_value = value.FindDouble(key);
+  const absl::optional<double> found_value = value.FindDoubleKey(key);
   if (!found_value.has_value())
     return absl::nullopt;
 
@@ -68,8 +71,6 @@
 
 }  // anonymous namespace
 
-WEB_STATE_USER_DATA_KEY_IMPL(TranslateController)
-
 TranslateController::TranslateController(
     web::WebState* web_state,
     JSTranslateWebFrameManagerFactory* js_manager_factory)
@@ -79,6 +80,11 @@
       weak_method_factory_(this) {
   DCHECK(web_state_);
   web_state_->AddObserver(this);
+  subscription_ = web_state_->AddScriptCommandCallback(
+      base::BindRepeating(
+          base::IgnoreResult(&TranslateController::OnJavascriptCommandReceived),
+          base::Unretained(this)),
+      kCommandPrefix);
 }
 
 TranslateController::~TranslateController() {
@@ -121,59 +127,67 @@
   js_manager_factory_ = manager;
 }
 
-void TranslateController::OnJavascriptCommandReceived(
-    const base::Value::Dict& payload) {
-  const std::string* command = payload.FindString("command");
-  if (!command) {
-    return;
+bool TranslateController::OnJavascriptCommandReceived(
+    const base::Value& command,
+    const GURL& page_url,
+    bool user_is_interacting,
+    web::WebFrame* sender_frame) {
+  if (!sender_frame->IsMainFrame()) {
+    // Translate is only supported on main frame.
+    return false;
+  }
+  const std::string* command_string = command.FindStringKey("command");
+  if (!command_string) {
+    return false;
   }
 
-  if (*command == "ready") {
-    OnTranslateReady(payload);
-  } else if (*command == "status") {
-    OnTranslateComplete(payload);
-  } else if (*command == "loadjavascript") {
-    OnTranslateLoadJavaScript(payload);
-  } else if (*command == "sendrequest") {
-    OnTranslateSendRequest(payload);
-  }
+  if (*command_string == "translate.ready")
+    return OnTranslateReady(command);
+  if (*command_string == "translate.status")
+    return OnTranslateComplete(command);
+  if (*command_string == "translate.loadjavascript")
+    return OnTranslateLoadJavaScript(command);
+  if (*command_string == "translate.sendrequest")
+    return OnTranslateSendRequest(command);
+
+  return false;
 }
 
-void TranslateController::OnTranslateReady(const base::Value::Dict& payload) {
+bool TranslateController::OnTranslateReady(const base::Value& command) {
   absl::optional<TranslateErrors> error_type =
-      FindTranslateErrorsKey(payload, "errorCode");
+      FindTranslateErrorsKey(command, "errorCode");
   if (!error_type.has_value())
-    return;
+    return false;
 
   absl::optional<double> load_time;
   absl::optional<double> ready_time;
   if (*error_type == TranslateErrors::NONE) {
-    load_time = payload.FindDouble("loadTime");
-    ready_time = payload.FindDouble("readyTime");
+    load_time = command.FindDoubleKey("loadTime");
+    ready_time = command.FindDoubleKey("readyTime");
     if (!load_time.has_value() || !ready_time.has_value()) {
-      return;
+      return false;
     }
   }
   if (observer_) {
     observer_->OnTranslateScriptReady(*error_type, load_time.value_or(0.),
                                       ready_time.value_or(0.));
   }
+  return true;
 }
 
-void TranslateController::OnTranslateComplete(
-    const base::Value::Dict& payload) {
+bool TranslateController::OnTranslateComplete(const base::Value& command) {
   absl::optional<TranslateErrors> error_type =
-      FindTranslateErrorsKey(payload, "errorCode");
+      FindTranslateErrorsKey(command, "errorCode");
   if (!error_type.has_value())
-    return;
+    return false;
 
   const std::string* source_language = nullptr;
   absl::optional<double> translation_time;
   if (*error_type == TranslateErrors::NONE) {
-    source_language = payload.FindString("pageSourceLanguage");
-    translation_time = payload.FindDouble("translationTime");
+    source_language = command.FindStringKey("pageSourceLanguage");
+    translation_time = command.FindDoubleKey("translationTime");
     if (!source_language || !translation_time.has_value()) {
-      return;
+      return false;
     }
   }
 
@@ -182,18 +196,19 @@
         *error_type, source_language ? *source_language : std::string(),
         translation_time.value_or(0.));
   }
+  return true;
 }
 
-void TranslateController::OnTranslateLoadJavaScript(
-    const base::Value::Dict& payload) {
-  const std::string* url = payload.FindString("url");
+bool TranslateController::OnTranslateLoadJavaScript(
+    const base::Value& command) {
+  const std::string* url = command.FindStringKey("url");
   if (!url) {
-    return;
+    return false;
   }
 
   GURL security_origin = translate::GetTranslateSecurityOrigin();
   if (url->find(security_origin.spec()) || script_fetcher_) {
-    return;
+    return false;
   }
 
   auto resource_request = std::make_unique<network::ResourceRequest>();
@@ -205,25 +220,31 @@
       web_state_->GetBrowserState()->GetURLLoaderFactory(),
       base::BindOnce(&TranslateController::OnScriptFetchComplete,
                      base::Unretained(this)));
+
+  return true;
 }
 
-void TranslateController::OnTranslateSendRequest(
-    const base::Value::Dict& payload) {
-  const std::string* method = payload.FindString("method");
-  const std::string* url = payload.FindString("url");
-  const std::string* body = payload.FindString("body");
-
-  if (!method || !url || !body) {
-    return;
+bool TranslateController::OnTranslateSendRequest(const base::Value& command) {
+  const std::string* method = command.FindStringKey("method");
+  if (!method) {
+    return false;
   }
-  absl::optional<double> request_id = payload.FindDouble("requestID");
+  const std::string* url = command.FindStringKey("url");
+  if (!url) {
+    return false;
+  }
+  const std::string* body = command.FindStringKey("body");
+  if (!body) {
+    return false;
+  }
+  absl::optional<double> request_id = command.FindDoubleKey("requestID");
   if (!request_id.has_value()) {
-    return;
+    return false;
   }
 
   GURL security_origin = translate::GetTranslateSecurityOrigin();
   if (url->find(security_origin.spec())) {
-    return;
+    return false;
   }
 
   auto request = std::make_unique<network::ResourceRequest>();
@@ -240,6 +261,7 @@
       base::BindOnce(&TranslateController::OnRequestFetchComplete,
                      base::Unretained(this), pair.first, *url,
                      static_cast<int>(*request_id)));
+  return true;
 }
 
 void TranslateController::OnScriptFetchComplete(
diff --git a/components/translate/ios/browser/translate_controller_unittest.mm b/components/translate/ios/browser/translate_controller_unittest.mm
index 7ffbfdb..1618840 100644
--- a/components/translate/ios/browser/translate_controller_unittest.mm
+++ b/components/translate/ios/browser/translate_controller_unittest.mm
@@ -110,12 +110,14 @@
                                 public TranslateController::Observer {
  protected:
   TranslateControllerTest()
-      : task_environment_(web::WebTaskEnvironment::Options::IO_MAINLOOP),
-        fake_web_state_(std::make_unique<web::FakeWebState>()),
+      : fake_web_state_(std::make_unique<web::FakeWebState>()),
         fake_browser_state_(std::make_unique<web::FakeBrowserState>()),
         fake_main_frame_(web::FakeWebFrame::Create(/*frame_id=*/"",
                                                    /*is_main_frame=*/true,
                                                    GURL())),
+        fake_iframe_(web::FakeWebFrame::Create(/*frame_id=*/"",
+                                               /*is_main_frame=*/false,
+                                               GURL())),
         error_type_(TranslateErrors::NONE),
         ready_time_(0),
         load_time_(0),
@@ -123,10 +125,9 @@
         on_script_ready_called_(false),
         on_translate_complete_called_(false) {
     fake_web_state_->SetBrowserState(fake_browser_state_.get());
-    TranslateController::CreateForWebState(fake_web_state_.get(),
-                                           &fake_translate_factory_);
-    TranslateController::FromWebState(fake_web_state_.get())
-        ->set_observer(this);
+    translate_controller_ = std::make_unique<TranslateController>(
+        fake_web_state_.get(), &fake_translate_factory_);
+    translate_controller_->set_observer(this);
   }
 
   // TranslateController::Observer methods.
@@ -148,15 +149,13 @@
     translation_time_ = translation_time;
   }
 
-  TranslateController* translate_controller() {
-    return TranslateController::FromWebState(fake_web_state_.get());
-  }
-
   web::WebTaskEnvironment task_environment_;
   std::unique_ptr<web::FakeWebState> fake_web_state_;
   std::unique_ptr<web::FakeBrowserState> fake_browser_state_;
   std::unique_ptr<web::FakeWebFrame> fake_main_frame_;
+  std::unique_ptr<web::FakeWebFrame> fake_iframe_;
   FakeJSTranslateWebFrameManagerFactory fake_translate_factory_;
+  std::unique_ptr<TranslateController> translate_controller_;
   TranslateErrors error_type_;
   double ready_time_;
   double load_time_;
@@ -166,36 +165,59 @@
   bool on_translate_complete_called_;
 };
 
-// Tests that OnTranslateScriptReady() is called when a timeout message is
-// received from the JS side.
-TEST_F(TranslateControllerTest, OnTranslateScriptReadyTimeoutCalled) {
+// Tests that OnJavascriptCommandReceived() returns false to malformed commands.
+TEST_F(TranslateControllerTest, OnJavascriptCommandReceived) {
+  base::Value::Dict malformed_command;
+  EXPECT_FALSE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(malformed_command)), GURL("http://google.com"),
+      /*interacting*/ false, fake_main_frame_.get()));
+}
+
+// Tests that OnJavascriptCommandReceived() returns false to iframe commands.
+TEST_F(TranslateControllerTest, OnIFrameJavascriptCommandReceived) {
   base::Value::Dict command;
-  command.Set("command", "ready");
+  command.Set("command", "translate.ready");
   command.Set("errorCode",
               static_cast<double>(TranslateErrors::TRANSLATION_TIMEOUT));
   command.Set("loadTime", .0);
   command.Set("readyTime", .0);
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
+  EXPECT_FALSE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting*/ false, fake_iframe_.get()));
+}
+
+// Tests that OnTranslateScriptReady() is called when a timeout message is
+// received from the JS side.
+TEST_F(TranslateControllerTest, OnTranslateScriptReadyTimeoutCalled) {
+  base::Value::Dict command;
+  command.Set("command", "translate.ready");
+  command.Set("errorCode",
+              static_cast<double>(TranslateErrors::TRANSLATION_TIMEOUT));
+  command.Set("loadTime", .0);
+  command.Set("readyTime", .0);
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting*/ false, fake_main_frame_.get()));
   EXPECT_TRUE(on_script_ready_called_);
   EXPECT_FALSE(on_translate_complete_called_);
   EXPECT_FALSE(error_type_ == TranslateErrors::NONE);
 }
 
 // Tests that OnTranslateScriptReady() is called with the right parameters when
-// a `ready` message is received from the JS side.
+// a |translate.ready| message is received from the JS side.
 TEST_F(TranslateControllerTest, OnTranslateScriptReadyCalled) {
   // Arbitrary values.
   double some_load_time = 23.1;
   double some_ready_time = 12.2;
 
   base::Value::Dict command;
-  command.Set("command", "ready");
+  command.Set("command", "translate.ready");
   command.Set("errorCode", static_cast<double>(TranslateErrors::NONE));
   command.Set("loadTime", some_load_time);
   command.Set("readyTime", some_ready_time);
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting*/ false, fake_main_frame_.get()));
   EXPECT_TRUE(on_script_ready_called_);
   EXPECT_FALSE(on_translate_complete_called_);
   EXPECT_TRUE(error_type_ == TranslateErrors::NONE);
@@ -204,19 +226,20 @@
 }
 
 // Tests that OnTranslateComplete() is called with the right parameters when a
-// `status` message is received from the JS side.
+// |translate.status| message is received from the JS side.
 TEST_F(TranslateControllerTest, TranslationSuccess) {
   // Arbitrary values.
   std::string some_source_language("en");
   double some_translation_time = 12.9;
 
   base::Value::Dict command;
-  command.Set("command", "status");
+  command.Set("command", "translate.status");
   command.Set("errorCode", static_cast<double>(TranslateErrors::NONE));
   command.Set("pageSourceLanguage", some_source_language);
   command.Set("translationTime", some_translation_time);
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting*/ false, fake_main_frame_.get()));
   EXPECT_FALSE(on_script_ready_called_);
   EXPECT_TRUE(on_translate_complete_called_);
   EXPECT_TRUE(error_type_ == TranslateErrors::NONE);
@@ -225,64 +248,57 @@
 }
 
 // Tests that OnTranslateComplete() is called with the right parameters when a
-// `status` message is received from the JS side.
+// |translate.status| message is received from the JS side.
 TEST_F(TranslateControllerTest, TranslationFailure) {
   base::Value::Dict command;
-  command.Set("command", "status");
+  command.Set("command", "translate.status");
   command.Set("errorCode",
               static_cast<double>(TranslateErrors::INITIALIZATION_ERROR));
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting*/ false, fake_main_frame_.get()));
   EXPECT_FALSE(on_script_ready_called_);
   EXPECT_TRUE(on_translate_complete_called_);
   EXPECT_FALSE(error_type_ == TranslateErrors::NONE);
 }
 
-// Tests that OnTranslateSendRequest() is called with the right parameters
-// when a `sendrequest` message is received from the JS side.
-TEST_F(TranslateControllerTest, OnTranslateSendRequestWithValidCommand) {
-  fake_web_state_->OnWebFrameDidBecomeAvailable(fake_main_frame_.get());
-
+// Tests that OnTranslateLoadJavaScript() is called with the right parameters
+// when a |translate.loadjavascript| message is received from the JS side.
+TEST_F(TranslateControllerTest, OnTranslateLoadJavascript) {
   base::Value::Dict command;
-  command.Set("command", "sendrequest");
+  command.Set("command", "translate.loadjavascript");
+  command.Set("url", "https://translate.googleapis.com/javascript.js");
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting=*/false, fake_main_frame_.get()));
+}
+
+// Tests that OnTranslateSendRequest() is called with the right parameters
+// when a |translate.sendrequest| message is received from the JS side.
+TEST_F(TranslateControllerTest, OnTranslateSendRequestWithValidCommand) {
+  base::Value::Dict command;
+  command.Set("command", "translate.sendrequest");
   command.Set("method", "POST");
   command.Set("url", "https://translate.googleapis.com/translate?key=abcd");
   command.Set("body", "helloworld");
   command.Set("requestID", .0);
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
-  task_environment_.RunUntilIdle();
-
-  HandleTranslateResponseParams* last_params =
-      fake_translate_factory_.FromWebFrame(fake_main_frame_.get())
-          ->GetLastHandleResponseParams();
-  ASSERT_TRUE(last_params);
-  EXPECT_EQ("https://translate.googleapis.com/translate?key=abcd",
-            last_params->URL);
-  EXPECT_EQ(0, last_params->request_ID);
-  EXPECT_EQ(net::HttpStatusCode::HTTP_BAD_REQUEST, last_params->response_code);
-  EXPECT_EQ("", last_params->status_text);
-  EXPECT_EQ("https://translate.googleapis.com/translate?key=abcd",
-            last_params->response_URL);
-  EXPECT_EQ("", last_params->response_text);
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting=*/false, fake_main_frame_.get()));
 }
 
 // Tests that OnTranslateSendRequest() rejects a bad url contained in the
-// `sendrequest` message received from Javascript.
+// |translate.sendrequest| message received from Javascript.
 TEST_F(TranslateControllerTest, OnTranslateSendRequestWithBadURL) {
   base::Value::Dict command;
-  command.Set("command", "sendrequest");
+  command.Set("command", "translate.sendrequest");
   command.Set("method", "POST");
   command.Set("url", "https://badurl.example.com");
   command.Set("body", "helloworld");
   command.Set("requestID", .0);
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
-  task_environment_.RunUntilIdle();
-  HandleTranslateResponseParams* last_params =
-      fake_translate_factory_.FromWebFrame(fake_main_frame_.get())
-          ->GetLastHandleResponseParams();
-  ASSERT_FALSE(last_params);
+  EXPECT_FALSE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting=*/false, fake_main_frame_.get()));
 }
 
 // Tests that OnTranslateSendRequest() called with a bad method will eventually
@@ -291,7 +307,7 @@
   fake_web_state_->OnWebFrameDidBecomeAvailable(fake_main_frame_.get());
 
   base::Value::Dict command;
-  command.Set("command", "sendrequest");
+  command.Set("command", "translate.sendrequest");
   command.Set("method", "POST\r\nHost: other.example.com");
   command.Set("url", "https://translate.googleapis.com/translate?key=abcd");
   command.Set("body", "helloworld");
@@ -299,8 +315,9 @@
 
   // The command will be accepted, but a bad method should cause the request to
   // fail shortly thereafter.
-  translate_controller()->OnJavascriptCommandReceived(
-      base::Value::Dict(std::move(command)));
+  EXPECT_TRUE(translate_controller_->OnJavascriptCommandReceived(
+      base::Value(std::move(command)), GURL("http://google.com"),
+      /*interacting=*/false, fake_main_frame_.get()));
   task_environment_.RunUntilIdle();
 
   HandleTranslateResponseParams* last_params =
diff --git a/components/translate/ios/browser/translate_java_script_feature.h b/components/translate/ios/browser/translate_java_script_feature.h
deleted file mode 100644
index 8e9d4f8..0000000
--- a/components/translate/ios/browser/translate_java_script_feature.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2022 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_TRANSLATE_IOS_BROWSER_TRANSLATE_JAVA_SCRIPT_FEATURE_H_
-#define COMPONENTS_TRANSLATE_IOS_BROWSER_TRANSLATE_JAVA_SCRIPT_FEATURE_H_
-
-#include "base/no_destructor.h"
-#include "ios/web/public/js_messaging/java_script_feature.h"
-
-namespace web {
-class WebState;
-}  // namespace web
-
-namespace translate {
-
-// Feature which listens for translate messages from the injected scripts.
-class TranslateJavaScriptFeature : public web::JavaScriptFeature {
- public:
-  static TranslateJavaScriptFeature* GetInstance();
-
- private:
-  friend class base::NoDestructor<TranslateJavaScriptFeature>;
-
-  // web::JavaScriptFeature
-  absl::optional<std::string> GetScriptMessageHandlerName() const override;
-  void ScriptMessageReceived(web::WebState* web_state,
-                             const web::ScriptMessage& message) override;
-
-  TranslateJavaScriptFeature();
-  ~TranslateJavaScriptFeature() override;
-
-  TranslateJavaScriptFeature(const TranslateJavaScriptFeature&) = delete;
-  TranslateJavaScriptFeature& operator=(const TranslateJavaScriptFeature&) =
-      delete;
-};
-
-}  // namespace translate
-
-#endif  // COMPONENTS_TRANSLATE_IOS_BROWSER_TRANSLATE_JAVA_SCRIPT_FEATURE_H_
diff --git a/components/translate/ios/browser/translate_java_script_feature.mm b/components/translate/ios/browser/translate_java_script_feature.mm
deleted file mode 100644
index 0317c92..0000000
--- a/components/translate/ios/browser/translate_java_script_feature.mm
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "components/translate/ios/browser/translate_java_script_feature.h"
-
-#import "components/translate/ios/browser/translate_controller.h"
-#import "ios/web/public/js_messaging/script_message.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-constexpr char kScriptMessageName[] = "TranslateMessage";
-}  // namespace
-
-namespace translate {
-
-// static
-TranslateJavaScriptFeature* TranslateJavaScriptFeature::GetInstance() {
-  static base::NoDestructor<TranslateJavaScriptFeature> instance;
-  return instance.get();
-}
-
-TranslateJavaScriptFeature::TranslateJavaScriptFeature()
-    : web::JavaScriptFeature(
-          ContentWorld::kPageContentWorld,
-          {/* The `translate_ios` script is injected on demand */
-           /* by JSTranslateWebFrameManager. */}) {}
-
-TranslateJavaScriptFeature::~TranslateJavaScriptFeature() = default;
-
-absl::optional<std::string>
-TranslateJavaScriptFeature::GetScriptMessageHandlerName() const {
-  return kScriptMessageName;
-}
-
-void TranslateJavaScriptFeature::ScriptMessageReceived(
-    web::WebState* web_state,
-    const web::ScriptMessage& message) {
-  if (!message.is_main_frame() || !message.body() ||
-      !message.body()->is_dict()) {
-    return;
-  }
-
-  TranslateController* translate_controller =
-      TranslateController::FromWebState(web_state);
-  translate_controller->OnJavascriptCommandReceived(message.body()->GetDict());
-}
-
-}  // namespace translate
diff --git a/components/ukm/observers/ukm_consent_state_observer_unittest.cc b/components/ukm/observers/ukm_consent_state_observer_unittest.cc
index ad2cddfa..0bcbe8b 100644
--- a/components/ukm/observers/ukm_consent_state_observer_unittest.cc
+++ b/components/ukm/observers/ukm_consent_state_observer_unittest.cc
@@ -51,11 +51,6 @@
     NotifyObserversOfStateChanged();
   }
 
-  void SetAuthError(GoogleServiceAuthError::State error_state) {
-    syncer::TestSyncService::SetAuthError(GoogleServiceAuthError(error_state));
-    NotifyObserversOfStateChanged();
-  }
-
   void Shutdown() override {
     for (auto& observer : observers_) {
       observer.OnSyncShutdown(this);
diff --git a/components/viz/service/display/dc_layer_overlay.h b/components/viz/service/display/dc_layer_overlay.h
index 95210035..4985575 100644
--- a/components/viz/service/display/dc_layer_overlay.h
+++ b/components/viz/service/display/dc_layer_overlay.h
@@ -71,7 +71,7 @@
 
   gfx::HDRMetadata hdr_metadata;
 
-  bool is_video_fullscreen_letterboxing;
+  bool is_video_fullscreen_letterboxing = false;
 };
 
 typedef std::vector<DCLayerOverlay> DCLayerOverlayList;
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 7450926..6c18485 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1521,6 +1521,8 @@
       return Page::PrerenderFinalStatusEnum::InactivePageRestriction;
     case PrerenderHost::FinalStatus::kStartFailed:
       return Page::PrerenderFinalStatusEnum::StartFailed;
+    case PrerenderHost::FinalStatus::kTimeoutBackgrounded:
+      return Page::PrerenderFinalStatusEnum::TimeoutBackgrounded;
   }
 }
 
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc
index e6ab794..af7fee2 100644
--- a/content/browser/preloading/prerender/prerender_host.cc
+++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -277,10 +277,30 @@
 
 void PrerenderHost::OnVisibilityChanged(Visibility visibility) {
   TRACE_EVENT("navigation", "PrerenderHost::OnVisibilityChanged");
-  // Keep prerenderings alive in the background when their visibility state
-  // changes to HIDDEN if the feature is enabled.
-  if (base::FeatureList::IsEnabled(blink::features::kPrerender2InBackground))
+  if (base::FeatureList::IsEnabled(blink::features::kPrerender2InBackground)) {
+    switch (visibility) {
+      case Visibility::HIDDEN:
+        // Keep a prerendered page alive in the background when its visibility
+        // state changes to HIDDEN if the feature is enabled.
+        DCHECK(!timeout_timer_.IsRunning());
+
+        timeout_timer_.SetTaskRunner(GetTimerTaskRunner());
+        // Cancel PrerenderHost in the background when it exceeds a certain
+        // amount of time defined in `kTimeToLiveInBackground`.
+        timeout_timer_.Start(
+            FROM_HERE, kTimeToLiveInBackground,
+            base::BindOnce(&PrerenderHost::Cancel, base::Unretained(this),
+                           FinalStatus::kTimeoutBackgrounded));
+        break;
+      case Visibility::OCCLUDED:
+        break;
+      case Visibility::VISIBLE:
+        // Stop the timer when a prerendered page gets visible to users.
+        timeout_timer_.Stop();
+        break;
+    }
     return;
+  }
 
   if (visibility == Visibility::HIDDEN) {
     Cancel(FinalStatus::kTriggerBackgrounded);
@@ -753,6 +773,7 @@
     case FinalStatus::kActivatedBeforeStarted:
     case FinalStatus::kInactivePageRestriction:
     case FinalStatus::kStartFailed:
+    case FinalStatus::kTimeoutBackgrounded:
       attempt_->SetFailureReason(ToPreloadingFailureReason(status));
       // We reset the attempt to ensure we don't update once we have reported it
       // as failure or accidentally use it for any other prerender attempts as
@@ -804,4 +825,15 @@
   registry->CancelHost(frame_tree_node_id_, status);
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+PrerenderHost::GetTimerTaskRunner() {
+  return timer_task_runner_for_testing_ ? timer_task_runner_for_testing_
+                                        : base::ThreadTaskRunnerHandle::Get();
+}
+
+void PrerenderHost::SetTaskRunnerForTesting(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  timer_task_runner_for_testing_ = std::move(task_runner);
+}
+
 }  // namespace content
diff --git a/content/browser/preloading/prerender/prerender_host.h b/content/browser/preloading/prerender/prerender_host.h
index 37b5548c..581396f 100644
--- a/content/browser/preloading/prerender/prerender_host.h
+++ b/content/browser/preloading/prerender/prerender_host.h
@@ -56,6 +56,12 @@
 // is owned by PrerenderHostRegistry.
 class CONTENT_EXPORT PrerenderHost : public WebContentsObserver {
  public:
+  // The time to allow prerendering kept alive in the background. PrerenderHost
+  // will be terminated with kTimeoutBackgrounded when the timer exceeds this.
+  // The value was determined to align with the default value of BFCache's
+  // eviction timer.
+  static constexpr base::TimeDelta kTimeToLiveInBackground = base::Seconds(180);
+
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
   enum class FinalStatus {
@@ -108,7 +114,8 @@
     kActivatedBeforeStarted = 40,
     kInactivePageRestriction = 41,
     kStartFailed = 42,
-    kMaxValue = kStartFailed,
+    kTimeoutBackgrounded = 43,
+    kMaxValue = kTimeoutBackgrounded,
   };
 
   // These values are persisted to logs. Entries should not be renumbered and
@@ -244,6 +251,11 @@
       const url::Origin& origin,
       blink::EnabledClientHints* client_hints) const;
 
+  // Only used for tests.
+  base::OneShotTimer* GetTimerForTesting() { return &timeout_timer_; }
+  void SetTaskRunnerForTesting(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
   // Returns absl::nullopt iff prerendering is initiated by the browser (not by
   // a renderer using Speculation Rules API).
   absl::optional<url::Origin> initiator_origin() const {
@@ -304,6 +316,8 @@
   AreCommonNavigationParamsCompatibleWithNavigation(
       const blink::mojom::CommonNavigationParams& potential_activation);
 
+  scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner();
+
   const PrerenderAttributes attributes_;
 
   // Indicates if `page_holder_` is ready for activation.
@@ -336,6 +350,12 @@
   base::flat_map<url::Origin, std::vector<network::mojom::WebClientHintsType>>
       client_hints_type_;
 
+  // Starts running the timer when prerendering gets hidden.
+  base::OneShotTimer timeout_timer_;
+  // Only used for tests. This task runner is used for precise injection in
+  // tests and for timing control.
+  scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_for_testing_;
+
   // Holds the navigation ID for the main frame initial navigation.
   absl::optional<int64_t> initial_navigation_id_;
 };
diff --git a/content/browser/preloading/prerender/prerender_host_unittest.cc b/content/browser/preloading/prerender/prerender_host_unittest.cc
index f926ce88..e44178fc 100644
--- a/content/browser/preloading/prerender/prerender_host_unittest.cc
+++ b/content/browser/preloading/prerender/prerender_host_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/functional/bind.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "build/build_config.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/browser/preloading/preloading.h"
@@ -415,7 +416,7 @@
   ASSERT_NE(prerender_host, nullptr);
   CommitPrerenderNavigation(*prerender_host);
 
-  // Changing the visibility state to VISIBLE will not affect prerendering.
+  // Changing the visibility state to VISIBLE will not stop prerendering.
   web_contents->WasShown();
   web_contents->ActivatePrerenderedPage(kPrerenderingUrl);
   ExpectFinalStatus(PrerenderHost::FinalStatus::kActivated);
@@ -437,7 +438,7 @@
   ASSERT_NE(prerender_host, nullptr);
   CommitPrerenderNavigation(*prerender_host);
 
-  // Changing the visibility state to OCCLUDED will not affect prerendering.
+  // Changing the visibility state to OCCLUDED will not stop prerendering.
   web_contents->WasOccluded();
   web_contents->ActivatePrerenderedPage(kPrerenderingUrl);
   ExpectFinalStatus(PrerenderHost::FinalStatus::kActivated);
@@ -567,11 +568,74 @@
   ASSERT_NE(prerender_host, nullptr);
   CommitPrerenderNavigation(*prerender_host);
 
-  // Changing the visibility state to HIDDEN will not affect prerendering.
+  // Changing the visibility state to HIDDEN will not stop prerendering.
   web_contents->WasHidden();
   web_contents->ActivatePrerenderedPage(kPrerenderingUrl);
   ExpectFinalStatus(PrerenderHost::FinalStatus::kActivated);
 }
 
+TEST_F(PrerenderHostInBackgroundTest, CancelPrerenderWhenTimeout) {
+  std::unique_ptr<TestWebContents> web_contents =
+      CreateWebContents(GURL("https://example.com/"));
+  const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
+  RenderFrameHostImpl* initiator_rfh = web_contents->GetPrimaryMainFrame();
+  PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry();
+  const int prerender_frame_tree_node_id = registry->CreateAndStartHost(
+      GeneratePrerenderAttributes(kPrerenderingUrl, initiator_rfh),
+      *web_contents);
+  PrerenderHost* prerender_host =
+      registry->FindNonReservedHostById(prerender_frame_tree_node_id);
+  ASSERT_NE(prerender_host, nullptr);
+  CommitPrerenderNavigation(*prerender_host);
+
+  // The timer should not start yet when the prerendered page is in the
+  // foreground.
+  ASSERT_FALSE(prerender_host->GetTimerForTesting()->IsRunning());
+
+  // Inject mock time task runner.
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
+      base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  prerender_host->SetTaskRunnerForTesting(task_runner);
+
+  // Changing the visibility state to HIDDEN will not stop prerendering.
+  web_contents->WasHidden();
+  ASSERT_TRUE(prerender_host->GetTimerForTesting()->IsRunning());
+
+  task_runner->FastForwardBy(PrerenderHost::kTimeToLiveInBackground);
+
+  ExpectFinalStatus(PrerenderHost::FinalStatus::kTimeoutBackgrounded);
+}
+
+TEST_F(PrerenderHostInBackgroundTest,
+       TimerResetWhenHiddenPageGoBackToForeground) {
+  std::unique_ptr<TestWebContents> web_contents =
+      CreateWebContents(GURL("https://example.com/"));
+  const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
+  RenderFrameHostImpl* initiator_rfh = web_contents->GetPrimaryMainFrame();
+  PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry();
+  const int prerender_frame_tree_node_id = registry->CreateAndStartHost(
+      GeneratePrerenderAttributes(kPrerenderingUrl, initiator_rfh),
+      *web_contents);
+  PrerenderHost* prerender_host =
+      registry->FindNonReservedHostById(prerender_frame_tree_node_id);
+  ASSERT_NE(prerender_host, nullptr);
+  CommitPrerenderNavigation(*prerender_host);
+
+  // The timer should not start yet when the prerendered page is in the
+  // foreground.
+  ASSERT_FALSE(prerender_host->GetTimerForTesting()->IsRunning());
+
+  // Changing the visibility state to HIDDEN will not stop prerendering.
+  web_contents->WasHidden();
+  ASSERT_TRUE(prerender_host->GetTimerForTesting()->IsRunning());
+
+  // The timer should be reset when the hidden page goes back to the foreground.
+  web_contents->WasShown();
+  ASSERT_FALSE(prerender_host->GetTimerForTesting()->IsRunning());
+
+  web_contents->ActivatePrerenderedPage(kPrerenderingUrl);
+  ExpectFinalStatus(PrerenderHost::FinalStatus::kActivated);
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/preloading/prerender/prerender_internals_handler_impl.cc b/content/browser/preloading/prerender/prerender_internals_handler_impl.cc
index 17a8354..6aaf30d9 100644
--- a/content/browser/preloading/prerender/prerender_internals_handler_impl.cc
+++ b/content/browser/preloading/prerender/prerender_internals_handler_impl.cc
@@ -88,6 +88,8 @@
       return "InactivePageRestriction";
     case PrerenderHost::FinalStatus::kStartFailed:
       return "StartFailed";
+    case PrerenderHost::FinalStatus::kTimeoutBackgrounded:
+      return "TimeoutBackgrounded";
   }
   NOTREACHED();
   return "";
diff --git a/content/web_test/browser/web_test_cookie_manager.cc b/content/web_test/browser/web_test_cookie_manager.cc
index 8b959b2..66f9985 100644
--- a/content/web_test/browser/web_test_cookie_manager.cc
+++ b/content/web_test/browser/web_test_cookie_manager.cc
@@ -5,6 +5,7 @@
 #include "content/web_test/browser/web_test_cookie_manager.h"
 
 #include "content/public/browser/storage_partition.h"
+#include "net/cookies/canonical_cookie.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "url/gurl.h"
 
@@ -35,4 +36,44 @@
           std::move(callback)));
 }
 
+void WebTestCookieManager::GetAllCookies(
+    blink::test::mojom::CookieManagerAutomation::GetAllCookiesCallback
+        callback) {
+  cookie_manager_->GetCookieList(
+      url_, net::CookieOptions::MakeAllInclusive(),
+      net::CookiePartitionKeyCollection(),
+      base::BindOnce(
+          [](blink::test::mojom::CookieManagerAutomation::GetAllCookiesCallback
+                 callback,
+             const net::CookieAccessResultList& cookies,
+             const net::CookieAccessResultList&) {
+            std::move(callback).Run(std::move(cookies));
+          },
+          std::move(callback)));
+}
+
+void WebTestCookieManager::GetNamedCookie(
+    const std::string& name,
+    blink::test::mojom::CookieManagerAutomation::GetNamedCookieCallback
+        callback) {
+  cookie_manager_->GetCookieList(
+      url_, net::CookieOptions::MakeAllInclusive(),
+      net::CookiePartitionKeyCollection(),
+      base::BindOnce(
+          [](const std::string& name,
+             blink::test::mojom::CookieManagerAutomation::GetNamedCookieCallback
+                 callback,
+             const net::CookieAccessResultList& cookies,
+             const net::CookieAccessResultList&) {
+            for (const auto& cookie : cookies) {
+              if (cookie.cookie.Name() == name) {
+                std::move(callback).Run(std::move(cookie));
+                return;
+              }
+            }
+            std::move(callback).Run(absl::nullopt);
+          },
+          name, std::move(callback)));
+}
+
 }  // namespace content
diff --git a/content/web_test/browser/web_test_cookie_manager.h b/content/web_test/browser/web_test_cookie_manager.h
index 3fcf54c..50c58ce 100644
--- a/content/web_test/browser/web_test_cookie_manager.h
+++ b/content/web_test/browser/web_test_cookie_manager.h
@@ -27,6 +27,13 @@
   void DeleteAllCookies(
       blink::test::mojom::CookieManagerAutomation::DeleteAllCookiesCallback)
       override;
+  void GetAllCookies(
+      blink::test::mojom::CookieManagerAutomation::GetAllCookiesCallback)
+      override;
+  void GetNamedCookie(
+      const std::string& name,
+      blink::test::mojom::CookieManagerAutomation::GetNamedCookieCallback)
+      override;
 
  private:
   const raw_ptr<network::mojom::CookieManager> cookie_manager_;
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index da261b760..5083ca0c 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1780,6 +1780,7 @@
   PASSWORDSPRIVATE_SWITCHBIOMETRICAUTHBEFOREFILLINGSTATE = 1717,
   WMDESKSPRIVATE_GETACTIVEDESK = 1718,
   WMDESKSPRIVATE_SWITCHDESK = 1719,
+  OS_TELEMETRY_GETTPMINFO = 1720,
   // Last entry: Add new entries above, then run:
   // tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/fuchsia_web/runners/cast/cast_runner.cc b/fuchsia_web/runners/cast/cast_runner.cc
index c9d8ee5..45fea1f 100644
--- a/fuchsia_web/runners/cast/cast_runner.cc
+++ b/fuchsia_web/runners/cast/cast_runner.cc
@@ -97,9 +97,6 @@
 // Application URL for the pseudo-component providing fuchsia.web.FrameHost.
 constexpr char kFrameHostComponentName[] = "cast:fuchsia.web.FrameHost";
 
-// Application URL for the pseudo-component providing chromium.cast.DataReset.
-constexpr char kDataResetComponentName[] = "cast:chromium.cast.DataReset";
-
 // Subdirectory used to stage persistent directories to be deleted upon next
 // startup.
 const char kStagedForDeletionSubdirectory[] = "staged_for_deletion";
@@ -264,46 +261,6 @@
   base::WeakPtrFactory<const sys::ServiceDirectory> weak_incoming_services_;
 };
 
-// TODO(crbug.com/1120914): Remove this once Component Framework v2 can be
-// used to route chromium.cast.DataReset capabilities cleanly.
-class DataResetComponent final : public fuchsia::sys::ComponentController {
- public:
-  // Creates a DataResetComponent with lifetime managed by |controller_request|.
-  static void Start(chromium::cast::DataReset* data_reset_impl,
-                    std::unique_ptr<base::StartupContext> startup_context,
-                    fidl::InterfaceRequest<fuchsia::sys::ComponentController>
-                        controller_request) {
-    new DataResetComponent(data_reset_impl, std::move(startup_context),
-                           std::move(controller_request));
-  }
-
- private:
-  DataResetComponent(chromium::cast::DataReset* data_reset_impl,
-                     std::unique_ptr<base::StartupContext> startup_context,
-                     fidl::InterfaceRequest<fuchsia::sys::ComponentController>
-                         controller_request)
-      : startup_context_(std::move(startup_context)),
-        data_reset_handler_binding_(startup_context_->outgoing(),
-                                    data_reset_impl) {
-    startup_context_->ServeOutgoingDirectory();
-    controller_binding_.Bind(std::move(controller_request));
-    controller_binding_.set_error_handler([this](zx_status_t) { Kill(); });
-  }
-  ~DataResetComponent() override = default;
-
-  // fuchsia::sys::ComponentController interface.
-  void Kill() override { delete this; }
-  void Detach() override {
-    controller_binding_.Close(ZX_ERR_NOT_SUPPORTED);
-    delete this;
-  }
-
-  std::unique_ptr<base::StartupContext> startup_context_;
-  const base::ScopedServiceBinding<chromium::cast::DataReset>
-      data_reset_handler_binding_;
-  fidl::Binding<fuchsia::sys::ComponentController> controller_binding_{this};
-};
-
 }  // namespace
 
 CastRunner::CastRunner(WebInstanceHost* web_instance_host, bool is_headless)
@@ -757,18 +714,6 @@
     return;
   }
 
-  // TODO(crbug.com/1120914): Remove this once Component Framework v2 can be
-  // used to route chromium.cast.DataReset capabilities cleanly.
-  if (url.spec() == kDataResetComponentName) {
-    // DataResetComponents are self-owned, so may outlive |this|. However,
-    // |this| is only touched when processing DataReset protocol requests.
-    // Since |this| is only deleted during component shutdown, no protocol
-    // requests will be processed after deletion.
-    DataResetComponent::Start(this, std::move(startup_context),
-                              std::move(controller_request));
-    return;
-  }
-
   pending_components_.emplace(std::make_unique<PendingCastComponent>(
       this, std::move(startup_context), std::move(controller_request),
       url.GetContent()));
diff --git a/fuchsia_web/runners/cast/cast_runner_integration_test.cc b/fuchsia_web/runners/cast/cast_runner_integration_test.cc
index 46cae8a..166f8bcb 100644
--- a/fuchsia_web/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia_web/runners/cast/cast_runner_integration_test.cc
@@ -1192,30 +1192,6 @@
   component.ExpectControllerDisconnectWithStatus(ZX_ERR_PEER_CLOSED);
 }
 
-// Verifies that CastRunner offers a chromium.cast.DataReset component, that
-// provides the DataReset service.
-TEST_F(CastRunnerIntegrationTest, DataReset_component) {
-  TestCastComponent component(cast_runner());
-  constexpr char kDataResetComponentName[] = "cast:chromium.cast.DataReset";
-  component.StartCastComponent(kDataResetComponentName);
-
-  base::RunLoop loop;
-  auto data_reset = component.component_services_client()
-                        ->Connect<chromium::cast::DataReset>();
-  data_reset.set_error_handler([quit_loop = loop.QuitClosure()](zx_status_t) {
-    quit_loop.Run();
-    ADD_FAILURE();
-  });
-  bool succeeded = false;
-  data_reset->DeletePersistentData([&succeeded, &loop](bool result) {
-    succeeded = result;
-    loop.Quit();
-  });
-  loop.Run();
-
-  EXPECT_TRUE(succeeded);
-}
-
 class CastRunnerFrameHostIntegrationTest : public CastRunnerIntegrationTest {
  public:
   CastRunnerFrameHostIntegrationTest()
diff --git a/gpu/command_buffer/service/memory_tracking.h b/gpu/command_buffer/service/memory_tracking.h
index 86a6cc90..92d874f 100644
--- a/gpu/command_buffer/service/memory_tracking.h
+++ b/gpu/command_buffer/service/memory_tracking.h
@@ -75,6 +75,8 @@
 
   ~MemoryTypeTracker();
 
+  const MemoryTracker* memory_tracker() const { return memory_tracker_; }
+
   void TrackMemAlloc(size_t bytes);
   void TrackMemFree(size_t bytes);
   size_t GetMemRepresented() const;
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
index 7511eec..1b40d0a 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
@@ -28,11 +28,6 @@
 namespace gpu {
 
 namespace {
-size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
-  size_t estimated_size = 0;
-  viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
-  return estimated_size;
-}
 
 using ScopedRestoreTexture = GLTextureImageBackingHelper::ScopedRestoreTexture;
 
@@ -134,15 +129,16 @@
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
     uint32_t usage)
-    : ClearTrackingSharedImageBacking(mailbox,
-                                      format,
-                                      size,
-                                      color_space,
-                                      surface_origin,
-                                      alpha_type,
-                                      usage,
-                                      EstimatedSize(format, size),
-                                      false /* is_thread_safe */),
+    : ClearTrackingSharedImageBacking(
+          mailbox,
+          format,
+          size,
+          color_space,
+          surface_origin,
+          alpha_type,
+          usage,
+          viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format),
+          false /* is_thread_safe */),
       context_state_(context_state) {}
 
 AngleVulkanImageBacking::~AngleVulkanImageBacking() {
diff --git a/gpu/command_buffer/service/shared_image/gl_image_backing.cc b/gpu/command_buffer/service/shared_image/gl_image_backing.cc
index 3645edc0..90687d0 100644
--- a/gpu/command_buffer/service/shared_image/gl_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/gl_image_backing.cc
@@ -30,12 +30,6 @@
 
 namespace {
 
-size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
-  size_t estimated_size = 0;
-  viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
-  return estimated_size;
-}
-
 using ScopedRestoreTexture = GLTextureImageBackingHelper::ScopedRestoreTexture;
 
 using InitializeGLTextureParams =
@@ -336,15 +330,16 @@
                                uint32_t usage,
                                const InitializeGLTextureParams& params,
                                bool is_passthrough)
-    : SharedImageBacking(mailbox,
-                         format,
-                         size,
-                         color_space,
-                         surface_origin,
-                         alpha_type,
-                         usage,
-                         EstimatedSize(format, size),
-                         false /* is_thread_safe */),
+    : SharedImageBacking(
+          mailbox,
+          format,
+          size,
+          color_space,
+          surface_origin,
+          alpha_type,
+          usage,
+          viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format),
+          false /* is_thread_safe */),
       image_(image),
       gl_params_(params),
       is_passthrough_(is_passthrough),
@@ -382,7 +377,8 @@
   // Set the GLImage to be initially unbound from the GL texture.
   image_bind_or_copy_needed_ = true;
   if (is_passthrough_) {
-    passthrough_texture_->SetEstimatedSize(EstimatedSize(format(), size()));
+    passthrough_texture_->SetEstimatedSize(
+        viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size(), format()));
     passthrough_texture_->SetLevelImage(gl_params_.target, 0, image_.get());
     passthrough_texture_->set_is_bind_pending(true);
   } else {
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
index 2dd9e79..bacf5a2e 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
@@ -69,12 +69,6 @@
 using InitializeGLTextureParams =
     GLTextureImageBackingHelper::InitializeGLTextureParams;
 
-size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
-  size_t estimated_size = 0;
-  viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
-  return estimated_size;
-}
-
 int BytesPerPixel(viz::ResourceFormat format) {
   int bits = viz::BitsPerPixel(format);
   DCHECK_GE(bits, 8);
@@ -133,15 +127,16 @@
                                              SkAlphaType alpha_type,
                                              uint32_t usage,
                                              bool is_passthrough)
-    : ClearTrackingSharedImageBacking(mailbox,
-                                      format,
-                                      size,
-                                      color_space,
-                                      surface_origin,
-                                      alpha_type,
-                                      usage,
-                                      EstimatedSize(format, size),
-                                      false /* is_thread_safe */),
+    : ClearTrackingSharedImageBacking(
+          mailbox,
+          format,
+          size,
+          color_space,
+          surface_origin,
+          alpha_type,
+          usage,
+          viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format),
+          false /* is_thread_safe */),
       is_passthrough_(is_passthrough) {}
 
 GLTextureImageBacking::~GLTextureImageBacking() {
@@ -335,7 +330,8 @@
       IsPassthrough() ? nullptr : &texture_);
   texture_params_ = params;
   if (IsPassthrough()) {
-    passthrough_texture_->SetEstimatedSize(EstimatedSize(format(), size()));
+    passthrough_texture_->SetEstimatedSize(
+        viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size(), format()));
     SetClearedRect(params.is_cleared ? gfx::Rect(size()) : gfx::Rect());
   } else {
     texture_->SetLevelInfo(params.target, 0, params.internal_format,
diff --git a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
index 337e75c..e985fa2e 100644
--- a/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/raw_draw_image_backing.cc
@@ -19,15 +19,6 @@
 #include "ui/gl/trace_util.h"
 
 namespace gpu {
-namespace {
-
-size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
-  size_t estimated_size = 0;
-  viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
-  return estimated_size;
-}
-
-}  // namespace
 
 class RawDrawImageBacking::RasterRawDrawImageRepresentation
     : public RasterImageRepresentation {
@@ -158,7 +149,10 @@
 size_t RawDrawImageBacking::EstimatedSizeForMemTracking() const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   AutoLock auto_lock(this);
-  return backend_texture_.isValid() ? EstimatedSize(format(), size()) : 0u;
+  return backend_texture_.isValid()
+             ? viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size(),
+                                                                format())
+             : 0u;
 }
 
 std::unique_ptr<RasterImageRepresentation> RawDrawImageBacking::ProduceRaster(
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing.cc b/gpu/command_buffer/service/shared_image/shared_image_backing.cc
index 93652e2..5f7d2ad 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_backing.cc
@@ -239,6 +239,14 @@
   }
 }
 
+const MemoryTracker* SharedImageBacking::GetMemoryTracker() const {
+  AutoLock auto_lock(this);
+  if (refs_.empty())
+    return nullptr;
+
+  return refs_[0]->tracker()->memory_tracker();
+}
+
 void SharedImageBacking::RegisterImageFactory(SharedImageFactory* factory) {
   DCHECK_CALLED_ON_VALID_THREAD(factory_thread_checker_);
   DCHECK(!factory_);
diff --git a/gpu/command_buffer/service/shared_image/shared_image_backing.h b/gpu/command_buffer/service/shared_image/shared_image_backing.h
index 784a660..14bdbab 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_backing.h
@@ -51,6 +51,7 @@
 class MemoryImageRepresentation;
 class VaapiImageRepresentation;
 class RasterImageRepresentation;
+class MemoryTracker;
 class MemoryTypeTracker;
 class SharedImageFactory;
 class VaapiDependenciesFactory;
@@ -115,6 +116,9 @@
   void ReleaseRef(SharedImageRepresentation* representation);
   bool HasAnyRefs() const;
 
+  // Returns the memory tracker this backing is registering memory with.
+  const MemoryTracker* GetMemoryTracker() const;
+
   // Notify backing a read access is succeeded
   void OnReadSucceeded();
   // Notify backing a write access is succeeded.
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.cc b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
index e315de9..d0f4b96 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
@@ -509,20 +509,6 @@
 }
 #endif  // BUILDFLAG(IS_FUCHSIA)
 
-// TODO(ericrk): Move this entirely to SharedImageManager.
-bool SharedImageFactory::OnMemoryDump(
-    const base::trace_event::MemoryDumpArgs& args,
-    base::trace_event::ProcessMemoryDump* pmd,
-    const std::string& dump_base_name,
-    uint64_t client_tracing_id) {
-  for (const auto& shared_image : shared_images_) {
-    shared_image_manager_->OnMemoryDump(shared_image->mailbox(), pmd,
-                                        dump_base_name, client_tracing_id);
-  }
-
-  return true;
-}
-
 #if BUILDFLAG(IS_WIN)
 bool SharedImageFactory::CopyToGpuMemoryBuffer(const Mailbox& mailbox) {
   auto it = shared_images_.find(mailbox);
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.h b/gpu/command_buffer/service/shared_image/shared_image_factory.h
index 73d0df0..830a4faa 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.h
@@ -112,10 +112,6 @@
   bool ReleaseSysmemBufferCollection(gfx::SysmemBufferCollectionId id);
 #endif  // BUILDFLAG(IS_FUCHSIA)
 
-  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
-                    base::trace_event::ProcessMemoryDump* pmd,
-                    const std::string& dump_base_name,
-                    uint64_t client_tracing_id);
   bool RegisterBacking(std::unique_ptr<SharedImageBacking> backing);
 
   SharedContextState* GetSharedContextState() const {
diff --git a/gpu/command_buffer/service/shared_image/shared_image_manager.cc b/gpu/command_buffer/service/shared_image/shared_image_manager.cc
index b333146c..63c05ca 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_manager.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_manager.cc
@@ -12,6 +12,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
@@ -84,6 +85,13 @@
   }
 #endif
   CALLED_ON_VALID_THREAD();
+
+  // In tests there might not be a SingleThreadTaskRunner for this thread.
+  if (base::ThreadTaskRunnerHandle::IsSet()) {
+    is_registered_as_memory_dump_provider_ = true;
+    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+        this, "SharedImageManager", base::ThreadTaskRunnerHandle::Get());
+  }
 }
 
 SharedImageManager::~SharedImageManager() {
@@ -92,6 +100,11 @@
   AutoLock auto_lock(this);
 #endif
   DCHECK(images_.empty());
+
+  if (is_registered_as_memory_dump_provider_) {
+    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+        this);
+  }
 }
 
 std::unique_ptr<SharedImageRepresentationFactoryRef>
@@ -371,36 +384,52 @@
   }
 }
 
-void SharedImageManager::OnMemoryDump(const Mailbox& mailbox,
-                                      base::trace_event::ProcessMemoryDump* pmd,
-                                      const std::string& dump_base_name,
-                                      uint64_t client_tracing_id) {
+bool SharedImageManager::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
   CALLED_ON_VALID_THREAD();
-
   AutoLock autolock(this);
-  auto found = images_.find(mailbox);
-  if (found == images_.end()) {
-    LOG(ERROR) << "SharedImageManager::OnMemoryDump: Trying to dump memory for "
-                  "a non existent mailbox.";
-    return;
+
+  const char* base_dump_name = "gpu/shared_images";
+
+  if (args.level_of_detail ==
+      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
+    size_t total_size = 0;
+    for (auto& backing : images_)
+      total_size += backing->EstimatedSizeForMemTracking();
+
+    base::trace_event::MemoryAllocatorDump* dump =
+        pmd->CreateAllocatorDump(base_dump_name);
+    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                    total_size);
+
+    // Early out, no need for more detail in a BACKGROUND dump.
+    return true;
   }
 
-  auto* backing = found->get();
-  size_t estimated_size = backing->EstimatedSizeForMemTracking();
-  if (estimated_size == 0)
-    return;
+  for (auto& backing : images_) {
+    auto* memory_tracker = backing->GetMemoryTracker();
 
-  // Unique name in the process.
-  std::string dump_name = base::StringPrintf(
-      "%s/mailbox_%s", dump_base_name.c_str(), mailbox.ToDebugString().c_str());
+    // All the backings registered here should have a memory tracker.
+    DCHECK(memory_tracker);
 
-  // GUID which expresses shared ownership with the client process. This must
-  // match the client-side GUID for mailbox.
-  auto client_guid = GetSharedImageGUIDForTracing(backing->mailbox());
+    // Unique name in the process.
+    std::string dump_name = base::StringPrintf(
+        "%s/client_0x%" PRIX32 "/mailbox_%s", base_dump_name,
+        memory_tracker->ClientId(), backing->mailbox().ToDebugString().c_str());
 
-  // Backing will produce dump with relevant information along with ownership
-  // edge to `client_guid`.
-  backing->OnMemoryDump(dump_name, client_guid, pmd, client_tracing_id);
+    // GUID which expresses shared ownership with the client process. This must
+    // match the client-side GUID for mailbox.
+    auto client_guid = GetSharedImageGUIDForTracing(backing->mailbox());
+
+    // Backing will produce dump with relevant information along with ownership
+    // edge to `client_guid`.
+    backing->OnMemoryDump(dump_name, client_guid, pmd,
+                          memory_tracker->ClientTracingId());
+  }
+
+  return true;
 }
 
 scoped_refptr<gfx::NativePixmap> SharedImageManager::GetNativePixmap(
diff --git a/gpu/command_buffer/service/shared_image/shared_image_manager.h b/gpu/command_buffer/service/shared_image/shared_image_manager.h
index 97d8367..7869fba 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_manager.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_manager.h
@@ -9,6 +9,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
+#include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
@@ -20,7 +21,8 @@
 class SharedImageRepresentationFactoryRef;
 class VaapiDependenciesFactory;
 
-class GPU_GLES2_EXPORT SharedImageManager {
+class GPU_GLES2_EXPORT SharedImageManager
+    : public base::trace_event::MemoryDumpProvider {
  public:
   // If |thread_safe| is set, the manager itself can be safely accessed from
   // other threads but the backings themselves may not be thread-safe so
@@ -35,7 +37,11 @@
   SharedImageManager(const SharedImageManager&) = delete;
   SharedImageManager& operator=(const SharedImageManager&) = delete;
 
-  ~SharedImageManager();
+  ~SharedImageManager() override;
+
+  // base::trace_event::MemoryDumpProvider implementation:
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
 
   // Registers a SharedImageBacking with the manager and returns a
   // SharedImageRepresentationFactoryRef which holds a ref on the SharedImage.
@@ -95,12 +101,6 @@
   void OnRepresentationDestroyed(const Mailbox& mailbox,
                                  SharedImageRepresentation* representation);
 
-  // Dump memory for the given mailbox.
-  void OnMemoryDump(const Mailbox& mailbox,
-                    base::trace_event::ProcessMemoryDump* pmd,
-                    const std::string& dump_base_name,
-                    uint64_t client_tracing_id);
-
   bool is_thread_safe() const { return !!lock_; }
 
   bool display_context_on_another_thread() const {
@@ -130,6 +130,8 @@
 
   const bool display_context_on_another_thread_;
 
+  bool is_registered_as_memory_dump_provider_ = false;
+
 #if BUILDFLAG(IS_WIN)
   scoped_refptr<DXGISharedHandleManager> dxgi_shared_handle_manager_;
 #endif
diff --git a/gpu/command_buffer/service/shared_image/shared_image_manager_unittest.cc b/gpu/command_buffer/service/shared_image/shared_image_manager_unittest.cc
index 49e3017..a5dca8eb 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_manager_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_manager_unittest.cc
@@ -7,30 +7,33 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
-#include "gpu/command_buffer/service/mailbox_manager_impl.h"
-#include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/test_image_backing.h"
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "gpu/command_buffer/tests/texture_image_factory.h"
-#include "gpu/config/gpu_driver_bug_workarounds.h"
-#include "gpu/config/gpu_feature_info.h"
-#include "gpu/config/gpu_preferences.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/color_space.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/init/gl_factory.h"
 
 namespace gpu {
 namespace {
 
+std::unique_ptr<TestImageBacking> CreateImageBacking(size_t size_in_bytes) {
+  auto mailbox = Mailbox::GenerateForSharedImage();
+  auto format = viz::ResourceFormat::RGBA_8888;
+  gfx::Size size(256, 256);
+  auto color_space = gfx::ColorSpace::CreateSRGB();
+  auto surface_origin = kTopLeft_GrSurfaceOrigin;
+  auto alpha_type = kPremul_SkAlphaType;
+  uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
+
+  return std::make_unique<TestImageBacking>(mailbox, format, size, color_space,
+                                            surface_origin, alpha_type, usage,
+                                            size_in_bytes);
+}
+
 TEST(SharedImageManagerTest, BasicRefCounting) {
   const size_t kSizeBytes = 1024;
   SharedImageManager manager;
@@ -71,6 +74,38 @@
   EXPECT_EQ(0u, tracker->GetMemRepresented());
 }
 
+TEST(SharedImageManagerTest, MemoryDumps) {
+  constexpr size_t kSizeBytes1 = 1000;
+  constexpr size_t kSizeBytes2 = 2000;
+
+  SharedImageManager manager;
+  auto tracker = std::make_unique<MemoryTypeTracker>(nullptr);
+
+  auto factory_ref1 =
+      manager.Register(CreateImageBacking(kSizeBytes1), tracker.get());
+  auto factory_ref2 =
+      manager.Register(CreateImageBacking(kSizeBytes2), tracker.get());
+
+  base::trace_event::MemoryDumpArgs args = {
+      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND};
+  base::trace_event::ProcessMemoryDump pmd(args);
+
+  manager.OnMemoryDump(args, &pmd);
+
+  auto* dump = pmd.GetAllocatorDump("gpu/shared_images");
+  ASSERT_NE(nullptr, dump);
+  ASSERT_EQ(dump->entries().size(), 1u);
+
+  // There should be a single memory dump entry with total size of both
+  // backings.
+  auto& entry = dump->entries()[0];
+  DCHECK_EQ(entry.name, base::trace_event::MemoryAllocatorDump::kNameSize);
+  DCHECK_EQ(entry.units, base::trace_event::MemoryAllocatorDump::kUnitsBytes);
+  DCHECK_EQ(entry.entry_type,
+            base::trace_event::MemoryAllocatorDump::Entry::kUint64);
+  DCHECK_EQ(entry.value_uint64, kSizeBytes1 + kSizeBytes2);
+}
+
 TEST(SharedImageManagerTest, TransferRefSameTracker) {
   const size_t kSizeBytes = 1024;
   SharedImageManager manager;
diff --git a/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc b/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc
index 62f537b..2929a543 100644
--- a/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc
@@ -30,12 +30,6 @@
 
 namespace gpu {
 namespace {
-size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
-  size_t estimated_size = 0;
-  viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
-  return estimated_size;
-}
-
 class MemoryImageRepresentationImpl : public MemoryImageRepresentation {
  public:
   MemoryImageRepresentationImpl(SharedImageManager* manager,
@@ -199,14 +193,15 @@
     SkAlphaType alpha_type,
     uint32_t usage,
     SharedMemoryRegionWrapper wrapper)
-    : SharedImageBacking(mailbox,
-                         format,
-                         size,
-                         color_space,
-                         surface_origin,
-                         alpha_type,
-                         usage,
-                         EstimatedSize(format, size),
-                         false),
+    : SharedImageBacking(
+          mailbox,
+          format,
+          size,
+          color_space,
+          surface_origin,
+          alpha_type,
+          usage,
+          viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format),
+          false),
       shared_memory_wrapper_(std::move(wrapper)) {}
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/test_image_backing.h b/gpu/command_buffer/service/shared_image/test_image_backing.h
index 1901b013..cbfde9b 100644
--- a/gpu/command_buffer/service/shared_image/test_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/test_image_backing.h
@@ -45,10 +45,6 @@
   void Update(std::unique_ptr<gfx::GpuFence> in_fence) override {}
   bool UploadFromMemory(const SkPixmap& pixmap) override;
   bool ReadbackToMemory(SkPixmap& pixmap) override;
-  void OnMemoryDump(const std::string& dump_name,
-                    base::trace_event::MemoryAllocatorDumpGuid client_guid,
-                    base::trace_event::ProcessMemoryDump* pmd,
-                    uint64_t client_tracing_id) override {}
 
   // Helper functions
   GLuint service_id() const { return service_id_; }
diff --git a/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc b/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc
index d07aa93b..5d19188 100644
--- a/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/resources/resource_sizes.h"
@@ -25,6 +26,27 @@
 
 namespace gpu {
 
+namespace {
+
+// If enabled, then nullptr is passed for the GLImage instance when invoking
+// BindStreamTextureImage(). Rolling this change out is the last blocker to
+// eliminating StreamTextureSharedImageInterface being a subclass of GLImage.
+// TODO(crbug.com/1310020): Remove this flag once the change has rolled out
+// safely.
+BASE_FEATURE(kPassNullForGLImageWhenBindingTexture,
+             "kPassNullForGLImageWhenBindingTexture",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// Returns either |nullptr| or |sii|.
+gl::GLImage* GetGLImageToUseWhenBindingTexture(
+    StreamTextureSharedImageInterface* sii) {
+  return (base::FeatureList::IsEnabled(kPassNullForGLImageWhenBindingTexture))
+             ? nullptr
+             : sii;
+}
+
+}  // namespace
+
 VideoSurfaceTextureImageBacking::VideoSurfaceTextureImageBacking(
     const Mailbox& mailbox,
     const gfx::Size& size,
@@ -193,7 +215,7 @@
   // texture_id in abstract texture via BindStreamTextureImage().
   DCHECK(stream_texture_sii_->TextureOwnerBindsTextureOnUpdate());
   texture->BindStreamTextureImage(
-      stream_texture_sii_.get(),
+      GetGLImageToUseWhenBindingTexture(stream_texture_sii_.get()),
       stream_texture_sii_->GetTextureBase()->service_id());
 
   return std::make_unique<GLTextureVideoImageRepresentation>(
@@ -222,7 +244,7 @@
   // texture_id in abstract texture via BindStreamTextureImage().
   DCHECK(stream_texture_sii_->TextureOwnerBindsTextureOnUpdate());
   texture->BindStreamTextureImage(
-      stream_texture_sii_.get(),
+      GetGLImageToUseWhenBindingTexture(stream_texture_sii_.get()),
       stream_texture_sii_->GetTextureBase()->service_id());
 
   return std::make_unique<GLTexturePassthroughVideoImageRepresentation>(
@@ -263,7 +285,7 @@
   // texture_id in abstract texture via BindStreamTextureImage().
   DCHECK(stream_texture_sii_->TextureOwnerBindsTextureOnUpdate());
   texture->BindStreamTextureImage(
-      stream_texture_sii_.get(),
+      GetGLImageToUseWhenBindingTexture(stream_texture_sii_.get()),
       stream_texture_sii_->GetTextureBase()->service_id());
 
   std::unique_ptr<gpu::GLTextureImageRepresentationBase> gl_representation;
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
index 6fcecd9..9feb0cbd 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.cc
@@ -52,12 +52,6 @@
 
 namespace {
 
-size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
-  size_t estimated_size = 0;
-  viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
-  return estimated_size;
-}
-
 class WrappedSkImage : public ClearTrackingSharedImageBacking {
  public:
   WrappedSkImage(base::PassKey<WrappedSkImageBackingFactory>,
@@ -442,7 +436,8 @@
   // That should be fine for now since we do not have/use any locks in backing.
   DCHECK(!is_thread_safe ||
          (context_state_->GrContextIsVulkan() && is_drdc_enabled_));
-  size_t estimated_size = EstimatedSize(format, size);
+  size_t estimated_size =
+      viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format);
   auto texture = std::make_unique<WrappedSkImage>(
       base::PassKey<WrappedSkImageBackingFactory>(), mailbox, format, size,
       color_space, surface_origin, alpha_type, usage, estimated_size,
@@ -464,7 +459,8 @@
     SkAlphaType alpha_type,
     uint32_t usage,
     base::span<const uint8_t> data) {
-  size_t estimated_size = EstimatedSize(format, size);
+  size_t estimated_size =
+      viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format);
   auto texture = std::make_unique<WrappedSkImage>(
       base::PassKey<WrappedSkImageBackingFactory>(), mailbox, format, size,
       color_space, surface_origin, alpha_type, usage, estimated_size,
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 252aa1cf..94f1a37 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -56,7 +56,6 @@
 #include "ipc/ipc_channel.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "ui/gl/gl_context.h"
-#include "ui/gl/gl_image_shared_memory.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_utils.h"
 
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index 66588b1..e5e6cee 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -77,7 +77,6 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_image_stub.h"
 #include "url/gurl.h"
 
 using testing::InSequence;
@@ -226,9 +225,8 @@
     pending_decodes_.pop();
     if (success) {
       // We give out a dummy GpuMemoryBufferHandle as the result: since we mock
-      // the ImageFactory and the gl::GLImage in these tests, the only
-      // requirement is that the NativePixmapHandle has the right number of
-      // planes.
+      // the SharedImage backing in these tests, the only requirement is that
+      // the NativePixmapHandle has the right number of planes.
       auto decode_result = std::make_unique<DecodeResult>();
       decode_result->handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
       for (size_t plane = 0; plane < gfx::NumberOfPlanesForLinearBufferFormat(
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc
index f7e7a26..7ebc2ba 100644
--- a/gpu/ipc/service/shared_image_stub.cc
+++ b/gpu/ipc/service/shared_image_stub.cc
@@ -11,7 +11,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/service/scheduler.h"
@@ -36,10 +35,7 @@
           channel->sync_point_manager()->CreateSyncPointClientState(
               CommandBufferNamespace::GPU_IO,
               command_buffer_id_,
-              sequence_)) {
-  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-      this, "gpu::SharedImageStub", channel_->task_runner());
-}
+              sequence_)) {}
 
 SharedImageStub::~SharedImageStub() {
   channel_->scheduler()->DestroySequence(sequence_);
@@ -48,8 +44,6 @@
     bool have_context = MakeContextCurrent();
     factory_->DestroyAllSharedImages(have_context);
   }
-  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
-      this);
 }
 
 std::unique_ptr<SharedImageStub> SharedImageStub::Create(GpuChannel* channel,
@@ -510,29 +504,6 @@
   return sync_point_client_state_->command_buffer_id().GetUnsafeValue();
 }
 
-bool SharedImageStub::OnMemoryDump(
-    const base::trace_event::MemoryDumpArgs& args,
-    base::trace_event::ProcessMemoryDump* pmd) {
-  if (!factory_)
-    return true;
-
-  std::string dump_name =
-      base::StringPrintf("gpu/shared_images/client_0x%" PRIX32, ClientId());
-
-  if (args.level_of_detail ==
-      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
-    base::trace_event::MemoryAllocatorDump* dump =
-        pmd->CreateAllocatorDump(dump_name);
-    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                    base::trace_event::MemoryAllocatorDump::kUnitsBytes, size_);
-
-    // Early out, no need for more detail in a BACKGROUND dump.
-    return true;
-  }
-
-  return factory_->OnMemoryDump(args, pmd, dump_name, ClientTracingId());
-}
-
 SharedImageStub::SharedImageDestructionCallback
 SharedImageStub::GetSharedImageDestructionCallback(const Mailbox& mailbox) {
   return base::BindOnce(&SharedImageStub::DestroySharedImage,
diff --git a/gpu/ipc/service/shared_image_stub.h b/gpu/ipc/service/shared_image_stub.h
index 393becf..3d6451d 100644
--- a/gpu/ipc/service/shared_image_stub.h
+++ b/gpu/ipc/service/shared_image_stub.h
@@ -7,7 +7,6 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
 #include "components/viz/common/resources/resource_format.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
@@ -23,9 +22,7 @@
 class GpuChannel;
 class SharedImageFactory;
 
-class GPU_IPC_SERVICE_EXPORT SharedImageStub
-    : public MemoryTracker,
-      public base::trace_event::MemoryDumpProvider {
+class GPU_IPC_SERVICE_EXPORT SharedImageStub : public MemoryTracker {
  public:
   ~SharedImageStub() override;
 
@@ -45,10 +42,6 @@
   int ClientId() const override;
   uint64_t ContextGroupTracingId() const override;
 
-  // base::trace_event::MemoryDumpProvider implementation:
-  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
-                    base::trace_event::ProcessMemoryDump* pmd) override;
-
   SequenceId sequence() const { return sequence_; }
   SharedImageFactory* factory() const { return factory_.get(); }
   GpuChannel* channel() const { return channel_; }
diff --git "a/infra/config/generated/builders/ci/Dawn Android arm DEPS Release \050Pixel 4\051/properties.json" "b/infra/config/generated/builders/ci/Dawn Android arm DEPS Release \050Pixel 4\051/properties.json"
index a47379d2..1d6a2db 100644
--- "a/infra/config/generated/builders/ci/Dawn Android arm DEPS Release \050Pixel 4\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Dawn Android arm DEPS Release \050Pixel 4\051/properties.json"
@@ -17,9 +17,6 @@
                 "config": "main_builder_rel_mb"
               },
               "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
                 "config": "android",
                 "target_platform": "android"
               },
diff --git "a/infra/config/generated/builders/ci/Dawn Android arm Release \050Pixel 4\051/properties.json" "b/infra/config/generated/builders/ci/Dawn Android arm Release \050Pixel 4\051/properties.json"
index f134011..6777233 100644
--- "a/infra/config/generated/builders/ci/Dawn Android arm Release \050Pixel 4\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Dawn Android arm Release \050Pixel 4\051/properties.json"
@@ -17,9 +17,6 @@
                 "config": "main_builder_rel_mb"
               },
               "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
                 "config": "android",
                 "target_platform": "android"
               },
diff --git a/infra/config/generated/builders/try/android-dawn-arm-rel/properties.json b/infra/config/generated/builders/try/android-dawn-arm-rel/properties.json
index 95aee61..f919a2d 100644
--- a/infra/config/generated/builders/try/android-dawn-arm-rel/properties.json
+++ b/infra/config/generated/builders/try/android-dawn-arm-rel/properties.json
@@ -17,9 +17,6 @@
                 "config": "main_builder_rel_mb"
               },
               "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
                 "config": "android",
                 "target_platform": "android"
               },
diff --git a/infra/config/generated/builders/try/dawn-android-arm-deps-rel/properties.json b/infra/config/generated/builders/try/dawn-android-arm-deps-rel/properties.json
index 55588c60..fab20b3f 100644
--- a/infra/config/generated/builders/try/dawn-android-arm-deps-rel/properties.json
+++ b/infra/config/generated/builders/try/dawn-android-arm-deps-rel/properties.json
@@ -17,9 +17,6 @@
                 "config": "main_builder_rel_mb"
               },
               "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
                 "config": "android",
                 "target_platform": "android"
               },
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
index e6138e8..df901da 100644
--- a/infra/config/subprojects/chromium/ci/chromium.dawn.star
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -111,9 +111,6 @@
         ),
         chromium_config = builder_config.chromium_config(
             config = "android",
-            apply_configs = [
-                "mb",
-            ],
             target_platform = builder_config.target_platform.ANDROID,
         ),
         android_config = builder_config.android_config(
@@ -198,9 +195,6 @@
         ),
         chromium_config = builder_config.chromium_config(
             config = "android",
-            apply_configs = [
-                "mb",
-            ],
             target_platform = builder_config.target_platform.ANDROID,
         ),
         android_config = builder_config.android_config(
diff --git a/ios/build/tools/update_deps.py b/ios/build/tools/update_deps.py
new file mode 100755
index 0000000..3f896ed
--- /dev/null
+++ b/ios/build/tools/update_deps.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Updates BUILD.gn files to add missing direct dependencies when the
+transitive dependency already exists.
+The script is intentionally conservative and will not work in many
+scenarios (e.g it does not handle target names with variables). These
+cases will require manual roll (as today).
+"""
+
+import argparse
+from collections import defaultdict
+import os
+import re
+import subprocess
+import sys
+
+# The output of gn check when some direct dependencies are missing is expected
+# to contain these lines.
+TRANSITIVE_PATTERNS = [
+    (3, "The target:"), (5, "is including a file from the target:"),
+    (8, "It's usually best to depend directly on the destination target."),
+    (9, "In some cases, the destination target is considered a subcomponent"),
+    (10, "of an intermediate target. In this case, the intermediate target"),
+    (11, "should depend publicly on the destination to forward the ability"),
+    (12, "to include headers."),
+    (14, "Dependency chain (there may also be others):")
+]
+# The line containing the target in the error message.
+TRANSITIVE_DEPENDENT_LINE_NUMBER = 4
+# The line containing the dependence in the error message.
+TRANSITIVE_DEPENDEE_LINE_NUMBER = 6
+
+MISSING_PATTERNS = [
+    (3, "It is not in any dependency of"),
+    (5, "The include file is in the target(s):"),
+]
+MISSING_DEPENDENT_LINE_NUMBER = 4
+MISSING_DEPENDEE_LINE_NUMBER = 6
+
+# Character to set colors in terminal.
+TERMINAL_ERROR_COLOR = "\033[91m"
+TERMINAL_WARNING_COLOR = "\033[93m"
+TERMINAL_RESET_COLOR = "\033[0m"
+
+# Separator between GN errors.
+GN_ERROR_SEPARATOR = "___________________\n"
+
+# The error message to be used when the include file is in multiple GN targets.
+MULTIPLE_DEPENDEES_ERROR = "Cannot handle includes in multiple targets:"
+# The error message to be used when several "deps" are present for the target.
+MULTIPLE_DEPS_ERROR = "Multiple deps variables in:"
+# Warning message when automatically removing a target ending with
+# "_strings_grit".
+GRIT_TARGET_MESSAGE = "Grit target found!"
+GRIT_TARGET_MESSAGE_DETAILS = "Automatically replacing:\n  %s\nby:\n  %s\n"
+
+# Array to handle special cases for canonical public target. This avoids having
+# dependencies on internal target when there is a canonical target with public
+# deps on those internal targets.
+CANONICAL_PUBLIC_TARGETS = {
+    "//ios/chrome/app/strings:ios_strings_grit":
+    "//ios/chrome/app/strings:strings",
+    "//ios/chrome/app/strings:ios_google_chrome_strings_grit":
+    "//ios/chrome/app/strings:strings",
+    "//ios/chrome/app/strings:ios_chromium_strings_grit":
+    "//ios/chrome/app/strings:strings",
+    "//components/strings:components_strings_grit":
+    "//components/strings:strings",
+}
+
+
+def gn_format(gn_files):
+    """Format the file |gn_files|."""
+    subprocess.check_call(["gn", "format"] + gn_files)
+
+
+def remove_redundant_target_name(dependant, target_name):
+    """Canonicalize target_name to be used as a dep of dependant."""
+    if target_name in CANONICAL_PUBLIC_TARGETS:
+        target_name = CANONICAL_PUBLIC_TARGETS[target_name]
+    (dependant_folder, dependant_target) = dependant.split(":")
+    (folder, target) = target_name.split(":")
+    if dependant_folder == folder:
+        return ":%s" % target
+    last_folder = os.path.basename(folder)
+    if last_folder == target:
+        return folder
+    if target.endswith("_strings_grit"):
+        warning_message = GRIT_TARGET_MESSAGE_DETAILS % (target_name, folder)
+        print_warning(GRIT_TARGET_MESSAGE, warning_message)
+        return folder
+    return target_name
+
+
+def extract_missing_dependency(error, prefix, patterns, dependant_line,
+                             dependee_line):
+    """Parse gn error message for missing direct dependency."""
+    lines = error.splitlines()
+    if len(lines) <= patterns[-1][0]:
+        return False, None
+    for line_number, pattern in patterns:
+        if lines[line_number] != pattern:
+            return False, None
+    dependant = lines[dependant_line].strip()
+    if prefix and not dependant.startswith(prefix):
+        return False, None
+
+    dependees = []
+    index = dependee_line
+    while lines[dependee_line].strip().startswith("//"):
+        dependees.append(remove_redundant_target_name(
+                             dependant, lines[dependee_line].strip()))
+        dependee_line += 1
+
+    return True, (dependant, dependees)
+
+
+def get_missing_deps(builddir, prefix):
+    """Extracts missing direct dependencies from gn."""
+    missing_deps = defaultdict(set)
+    process = subprocess.Popen(["gn", "check", builddir],
+                               stdout=subprocess.PIPE)
+    lines, errs = process.communicate()
+    errors = lines.decode("ascii").split(GN_ERROR_SEPARATOR)
+    for error in errors:
+        has_missing_dep, info = extract_missing_dependency(
+            error, prefix, TRANSITIVE_PATTERNS,
+            TRANSITIVE_DEPENDENT_LINE_NUMBER, TRANSITIVE_DEPENDEE_LINE_NUMBER)
+        if not has_missing_dep:
+            has_missing_dep, info = extract_missing_dependency(
+                error, prefix, MISSING_PATTERNS, MISSING_DEPENDENT_LINE_NUMBER,
+                MISSING_DEPENDEE_LINE_NUMBER)
+        if has_missing_dep:
+            dependant, dependees = info
+            if len(dependees) == 1:
+                missing_deps[dependant].add(dependees[0])
+            else:
+                print_error(MULTIPLE_DEPENDEES_ERROR, error)
+    return missing_deps
+
+
+def add_missing_deps(srcdir, target, deps):
+    """Adds the missing deps to the BUILD.gn file and run gn format on it."""
+    (dir, target_name) = target.split(":")
+    build_gn_file = os.path.join(srcdir, dir[2:], "BUILD.gn")
+    content = []
+    in_target = False
+    changed = False
+    first_deps_variable_line_index = -1
+    target_name = target_name.replace("+", "\\+")
+    target_rule = re.compile("\s*[a-z_]*\(\"%s\"\) {" % target_name)
+    with open(build_gn_file, "r") as build_gn:
+        all_lines = build_gn.readlines()
+        for line_index, line in enumerate(all_lines):
+            content += [line]
+            if target_rule.search(line):
+                indent = len(line) - len(line.lstrip(" "))
+                in_target = True
+            if in_target and line == (" " * indent + "}\n"):
+                in_target = False
+            if (in_target and first_deps_variable_line_index != -1 and
+                     line.strip().startswith("deps ")):
+                error_detail = (f"At lines:\n"
+                  f"* {build_gn_file}:{first_deps_variable_line_index}:\n"
+                  f"  {all_lines[first_deps_variable_line_index]}\n"
+                  f"* {build_gn_file}:{line_index}:\n"
+                  f"  {line}\n")
+                error_detail = f"{target}\n{error_detail}"
+                print_error(MULTIPLE_DEPS_ERROR, error_detail)
+                # Multiple deps, abort
+                return False, None
+            if (in_target and first_deps_variable_line_index == -1 and
+                    line.strip().startswith("deps ")):
+                first_deps_variable_line_index = line_index
+                onelinedeps = False
+                if "[" in line and line[-2] == "]":
+                    onelinedeps = True
+                    content[-1] = line.replace("]", ",")
+                for dep in deps:
+                    content += ["\"%s\",\n" % dep]
+                    changed = True
+                if onelinedeps:
+                    content += ["]\n"]
+    if changed:
+        with open(build_gn_file, "w") as build_gn:
+            build_gn.write("".join(content))
+        return True, build_gn_file
+    return False, None
+
+
+def main(args):
+    parser = argparse.ArgumentParser(
+        description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
+    parser.add_argument("--prefix",
+                        help="Only fix subtargets of prefix",
+                        default="")
+    parser.add_argument("builddir", help="The build dir")
+    parser.add_argument("srcdir", help="The src dir")
+    args = parser.parse_args()
+    deps = get_missing_deps(args.builddir, args.prefix)
+    changed_build_gn_files = []
+    for target in deps:
+        changed, build_gn_file = add_missing_deps(args.srcdir, target,
+                                                deps[target])
+        if changed:
+            changed_build_gn_files.append(build_gn_file)
+    if changed_build_gn_files:
+        gn_format(changed_build_gn_files)
+
+
+def print_error(error_message, error_info):
+    """ Print the `error_message` with additional `error_info` """
+    color_start, color_end = adapted_color_for_output(TERMINAL_ERROR_COLOR,
+                                                   TERMINAL_RESET_COLOR)
+
+    error_message = color_start + "ERROR: " + error_message + color_end
+    if len(error_info) > 0:
+        error_message = error_message + "\n" + error_info
+    print(error_message + "\n" + GN_ERROR_SEPARATOR)
+
+
+def print_warning(warning_message, warning_info):
+    """ Print the `warning_message` with additional `warning_info` """
+    color_start, color_end = adapted_color_for_output(TERMINAL_WARNING_COLOR,
+                                                   TERMINAL_RESET_COLOR)
+
+    warning_message = color_start + "WARNING: " + warning_message + color_end
+    if len(warning_info) > 0:
+        warning_message = warning_message + "\n" + warning_info
+    print(warning_message + "\n" + GN_ERROR_SEPARATOR)
+
+
+def adapted_color_for_output(color_start, color_end):
+    """ Returns a the `color_start`, `color_end` tuple if the output is a
+    terminal, or empty strings otherwise """
+    if not sys.stdout.isatty():
+        return "", ""
+    return color_start, color_end
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv[1:]))
diff --git a/ios/chrome/browser/translate/translate_app_interface.mm b/ios/chrome/browser/translate/translate_app_interface.mm
index 0253f27..b7abcaf9 100644
--- a/ios/chrome/browser/translate/translate_app_interface.mm
+++ b/ios/chrome/browser/translate/translate_app_interface.mm
@@ -109,27 +109,25 @@
     // used by this fake object. Instead just invoke host with 'translate.ready'
     // followed by 'translate.status'.
     base::Value translate_ready_dict(base::Value::Type::DICTIONARY);
-    translate_ready_dict.SetKey("command", base::Value("ready"));
+    translate_ready_dict.SetKey("command", base::Value("translate.ready"));
     translate_ready_dict.SetKey("errorCode", base::Value(0));
     translate_ready_dict.SetKey("loadTime", base::Value(0));
     translate_ready_dict.SetKey("readyTime", base::Value(0));
 
     std::vector<base::Value> translate_ready_params;
-    translate_ready_params.push_back(base::Value("TranslateMessage"));
     translate_ready_params.push_back(std::move(translate_ready_dict));
-    web_frame_->CallJavaScriptFunction("common.sendWebKitMessage",
+    web_frame_->CallJavaScriptFunction("message.invokeOnHost",
                                        translate_ready_params);
 
     base::Value translate_status_dict(base::Value::Type::DICTIONARY);
-    translate_status_dict.SetKey("command", base::Value("status"));
+    translate_status_dict.SetKey("command", base::Value("translate.status"));
     translate_status_dict.SetKey("errorCode", base::Value(0));
     translate_status_dict.SetKey("pageSourceLanguage", base::Value("fr"));
     translate_status_dict.SetKey("translationTime", base::Value(0));
 
     std::vector<base::Value> translate_status_params;
-    translate_status_params.push_back(base::Value("TranslateMessage"));
     translate_status_params.push_back(std::move(translate_status_dict));
-    web_frame_->CallJavaScriptFunction("common.sendWebKitMessage",
+    web_frame_->CallJavaScriptFunction("message.invokeOnHost",
                                        translate_status_params);
   }
 
@@ -150,6 +148,15 @@
         u"myButton = document.getElementById('translated-button');"
         u"myButton.remove();");
   }
+
+  void HandleTranslateResponse(const std::string& url,
+                               int request_id,
+                               int response_code,
+                               const std::string status_text,
+                               const std::string& response_url,
+                               const std::string& response_text) override {
+    // no-op
+  }
 };
 
 class FakeJSTranslateWebFrameManagerFactory
@@ -260,11 +267,13 @@
 }
 
 + (void)setUpFakeJSTranslateManagerInCurrentTab {
-  translate::TranslateController* translate_controller =
-      translate::TranslateController::FromWebState(
-          chrome_test_util::GetCurrentWebState());
-  translate_controller->SetJsTranslateWebFrameManagerFactoryForTesting(
-      FakeJSTranslateWebFrameManagerFactory::GetInstance());
+  ChromeIOSTranslateClient* client = ChromeIOSTranslateClient::FromWebState(
+      chrome_test_util::GetCurrentWebState());
+  translate::IOSTranslateDriver* driver =
+      static_cast<translate::IOSTranslateDriver*>(client->GetTranslateDriver());
+  driver->translate_controller()
+      ->SetJsTranslateWebFrameManagerFactoryForTesting(
+          FakeJSTranslateWebFrameManagerFactory::GetInstance());
 }
 
 + (BOOL)shouldAutoTranslateFromLanguage:(NSString*)source
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index d5451e24..e0d66c2 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -33,8 +33,15 @@
     ":popup_ui_protocols",
     "//base",
     "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/app/theme",
     "//ios/chrome/browser/net:crurl",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/omnibox/popup:popup_accessibility_identifier_constants",
+    "//ios/chrome/browser/ui/toolbar/buttons",
+    "//ios/chrome/browser/ui/toolbar/public:constants",
+    "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/ui/colors:swift",
+    "//ui/base",
   ]
 
   frameworks = [
diff --git a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
index 54bdfea..a290d4b 100644
--- a/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/overflow_menu/BUILD.gn
@@ -133,8 +133,11 @@
   deps = [
     "//base",
     "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/ui/popup_menu:constants",
     "//ios/chrome/browser/ui/popup_menu:metrics_protocols",
+    "//ios/chrome/common:timing",
     "//ios/chrome/common/ui/colors:swift",
+    "//ui/base",
   ]
 
   frameworks = [
diff --git a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
index 46b70138..b639f20 100644
--- a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
@@ -32,7 +32,12 @@
 swift_source_set("safety_check_ui_swift") {
   bridge_header = "safety_check_bridge.h"
   sources = [ "safety_check_table_view_controller.swift" ]
-  deps = [ ":safety_check_ui" ]
+  deps = [
+    ":safety_check_ui",
+    "//ios/chrome/browser/ui/settings:settings_root",
+    "//ios/chrome/browser/ui/settings/cells",
+    "//ui/base",
+  ]
 }
 
 source_set("safety_check") {
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 43962db..3af5ff3 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -242,7 +242,6 @@
     "//components/signin/public/identity_manager",
     "//components/strings",
     "//components/translate/core/browser",
-    "//components/translate/ios/browser",
     "//components/variations",
     "//components/variations/field_trial_config",
     "//components/variations/service",
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 214ad237..3c72813 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -22,7 +22,6 @@
 #import "components/password_manager/core/common/password_manager_features.h"
 #import "components/password_manager/ios/password_manager_java_script_feature.h"
 #import "components/strings/grit/components_strings.h"
-#import "components/translate/ios/browser/translate_java_script_feature.h"
 #import "components/version_info/version_info.h"
 #import "ios/chrome/browser/application_context/application_context.h"
 #import "ios/chrome/browser/browser_about_rewriter.h"
@@ -320,7 +319,6 @@
   SearchEngineJavaScriptFeature::GetInstance()->SetDelegate(
       SearchEngineTabHelperFactory::GetInstance());
   features.push_back(SearchEngineJavaScriptFeature::GetInstance());
-  features.push_back(translate::TranslateJavaScriptFeature::GetInstance());
   features.push_back(WebPerformanceMetricsJavaScriptFeature::GetInstance());
   features.push_back(FollowJavaScriptFeature::GetInstance());
   return features;
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
index 88a9cbe..4ee3fee 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
@@ -218,7 +218,7 @@
                [error.userInfo[CWVSyncErrorIsTransientKey] boolValue];
       }]]);
   OCMExpect([delegate syncControllerDidUpdateState:sync_controller]);
-  sync_service_.SetAuthError(GoogleServiceAuthError::FromConnectionError(0));
+  sync_service_.SetTransientAuthError();
   sync_service_.FireStateChanged();
 
   [delegate verify];
diff --git a/ios/web_view/internal/web_view_web_client.mm b/ios/web_view/internal/web_view_web_client.mm
index fbbf967..ca75a6b 100644
--- a/ios/web_view/internal/web_view_web_client.mm
+++ b/ios/web_view/internal/web_view_web_client.mm
@@ -18,7 +18,6 @@
 #import "components/security_interstitials/core/unsafe_resource.h"
 #include "components/ssl_errors/error_info.h"
 #include "components/strings/grit/components_strings.h"
-#import "components/translate/ios/browser/translate_java_script_feature.h"
 #import "ios/components/security_interstitials/lookalikes/lookalike_url_container.h"
 #import "ios/components/security_interstitials/lookalikes/lookalike_url_error.h"
 #import "ios/components/security_interstitials/safe_browsing/safe_browsing_error.h"
@@ -117,7 +116,6 @@
           autofill::FormHandlersJavaScriptFeature::GetInstance(),
           autofill::SuggestionControllerJavaScriptFeature::GetInstance(),
           password_manager::PasswordManagerJavaScriptFeature::GetInstance(),
-          translate::TranslateJavaScriptFeature::GetInstance(),
           WebViewMessageHandlerJavaScriptFeature::GetInstance()};
 }
 
diff --git a/media/audio/cras/audio_manager_chromeos_unittest.cc b/media/audio/cras/audio_manager_chromeos_unittest.cc
index 4a27ac8..9a100bb 100644
--- a/media/audio/cras/audio_manager_chromeos_unittest.cc
+++ b/media/audio/cras/audio_manager_chromeos_unittest.cc
@@ -274,8 +274,8 @@
 
   AudioManagerChromeOS::SystemAudioProcessingInfo system_apm_info_;
   size_t user_buffer_size_ = kDefaultInputBufferSize;
-  std::vector<base::Feature> enabled_features_;
-  std::vector<base::Feature> disabled_features_;
+  std::vector<base::test::FeatureRef> enabled_features_;
+  std::vector<base::test::FeatureRef> disabled_features_;
   bool aec_on_dsp_allowed_;
   bool ns_on_dsp_allowed_;
   bool agc_on_dsp_allowed_;
diff --git a/media/gpu/test/video_encoder/video_encoder_test_environment.cc b/media/gpu/test/video_encoder/video_encoder_test_environment.cc
index 3898319..ff81bfc9 100644
--- a/media/gpu/test/video_encoder/video_encoder_test_environment.cc
+++ b/media/gpu/test/video_encoder/video_encoder_test_environment.cc
@@ -100,8 +100,8 @@
     Bitrate::Mode bitrate_mode,
     bool reverse,
     const FrameOutputConfig& frame_output_config,
-    const std::vector<base::Feature>& enabled_features,
-    const std::vector<base::Feature>& disabled_features) {
+    const std::vector<base::test::FeatureRef>& enabled_features,
+    const std::vector<base::test::FeatureRef>& disabled_features) {
   if (video_path.empty()) {
     LOG(ERROR) << "No video specified";
     return nullptr;
@@ -147,8 +147,10 @@
 
   // TODO(b/182008564) Add checks to make sure no features are duplicated, and
   // there is no intersection between the enabled and disabled set.
-  std::vector<base::Feature> combined_enabled_features(enabled_features);
-  std::vector<base::Feature> combined_disabled_features(disabled_features);
+  std::vector<base::test::FeatureRef> combined_enabled_features(
+      enabled_features);
+  std::vector<base::test::FeatureRef> combined_disabled_features(
+      disabled_features);
   combined_disabled_features.push_back(media::kFFmpegDecodeOpaqueVP8);
 #if BUILDFLAG(USE_VAAPI)
   // TODO(crbug.com/828482): remove once enabled by default.
@@ -208,8 +210,8 @@
     bool save_output_bitstream,
     bool reverse,
     const FrameOutputConfig& frame_output_config,
-    const std::vector<base::Feature>& enabled_features,
-    const std::vector<base::Feature>& disabled_features)
+    const std::vector<base::test::FeatureRef>& enabled_features,
+    const std::vector<base::test::FeatureRef>& disabled_features)
     : VideoTestEnvironment(enabled_features, disabled_features),
       video_(std::move(video)),
       enable_bitstream_validator_(enable_bitstream_validator),
diff --git a/media/gpu/test/video_encoder/video_encoder_test_environment.h b/media/gpu/test/video_encoder/video_encoder_test_environment.h
index 6e91344..6c5f896 100644
--- a/media/gpu/test/video_encoder/video_encoder_test_environment.h
+++ b/media/gpu/test/video_encoder/video_encoder_test_environment.h
@@ -51,8 +51,8 @@
       Bitrate::Mode bitrate_mode,
       bool reverse,
       const FrameOutputConfig& frame_output_config = FrameOutputConfig(),
-      const std::vector<base::Feature>& enabled_features = {},
-      const std::vector<base::Feature>& disabled_features = {});
+      const std::vector<base::test::FeatureRef>& enabled_features = {},
+      const std::vector<base::test::FeatureRef>& disabled_features = {});
 
   ~VideoEncoderTestEnvironment() override;
 
@@ -103,8 +103,8 @@
       bool save_output_bitstream,
       bool reverse,
       const FrameOutputConfig& frame_output_config,
-      const std::vector<base::Feature>& enabled_features,
-      const std::vector<base::Feature>& disabled_features);
+      const std::vector<base::test::FeatureRef>& enabled_features,
+      const std::vector<base::test::FeatureRef>& disabled_features);
 
   // Video file to be used for testing.
   const std::unique_ptr<media::test::Video> video_;
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index 9734e885..c5c9976 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -32,8 +32,8 @@
     bool linear_output,
     const base::FilePath& output_folder,
     const FrameOutputConfig& frame_output_config,
-    const std::vector<base::Feature>& enabled_features,
-    const std::vector<base::Feature>& disabled_features) {
+    const std::vector<base::test::FeatureRef>& enabled_features,
+    const std::vector<base::test::FeatureRef>& disabled_features) {
   auto video = std::make_unique<media::test::Video>(
       video_path.empty() ? base::FilePath(kDefaultTestVideoPath) : video_path,
       video_metadata_path);
@@ -44,9 +44,11 @@
 
   // TODO(b/182008564) Add checks to make sure no features are duplicated, and
   // there is no intersection between the enabled and disabled set.
-  std::vector<base::Feature> combined_enabled_features(enabled_features);
+  std::vector<base::test::FeatureRef> combined_enabled_features(
+      enabled_features);
   combined_enabled_features.push_back(media::kVp9kSVCHWDecoding);
-  std::vector<base::Feature> combined_disabled_features(disabled_features);
+  std::vector<base::test::FeatureRef> combined_disabled_features(
+      disabled_features);
 #if BUILDFLAG(USE_VAAPI)
   // TODO(b/172217032): remove once enabled by default.
   combined_enabled_features.push_back(media::kVaapiAV1Decoder);
@@ -71,8 +73,8 @@
     bool linear_output,
     const base::FilePath& output_folder,
     const FrameOutputConfig& frame_output_config,
-    const std::vector<base::Feature>& enabled_features,
-    const std::vector<base::Feature>& disabled_features)
+    const std::vector<base::test::FeatureRef>& enabled_features,
+    const std::vector<base::test::FeatureRef>& disabled_features)
     : VideoTestEnvironment(enabled_features, disabled_features),
       video_(std::move(video)),
       validator_type_(validator_type),
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
index d37527ee..1151422 100644
--- a/media/gpu/test/video_player/video_player_test_environment.h
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -36,8 +36,8 @@
       bool linear_output,
       const base::FilePath& output_folder = base::FilePath(),
       const FrameOutputConfig& frame_output_config = FrameOutputConfig(),
-      const std::vector<base::Feature>& enabled_features = {},
-      const std::vector<base::Feature>& disabled_features = {});
+      const std::vector<base::test::FeatureRef>& enabled_features = {},
+      const std::vector<base::test::FeatureRef>& disabled_features = {});
   ~VideoPlayerTestEnvironment() override;
 
   // Get the video the tests will be ran on.
@@ -68,8 +68,8 @@
       bool linear_output,
       const base::FilePath& output_folder,
       const FrameOutputConfig& frame_output_config,
-      const std::vector<base::Feature>& enabled_features,
-      const std::vector<base::Feature>& disabled_features);
+      const std::vector<base::test::FeatureRef>& enabled_features,
+      const std::vector<base::test::FeatureRef>& disabled_features);
 
   const std::unique_ptr<media::test::Video> video_;
   const ValidatorType validator_type_;
diff --git a/media/gpu/test/video_test_environment.cc b/media/gpu/test/video_test_environment.cc
index 8dfe711..2ed50f2 100644
--- a/media/gpu/test/video_test_environment.cc
+++ b/media/gpu/test/video_test_environment.cc
@@ -26,8 +26,8 @@
 VideoTestEnvironment::VideoTestEnvironment() : VideoTestEnvironment({}, {}) {}
 
 VideoTestEnvironment::VideoTestEnvironment(
-    const std::vector<base::Feature>& enabled_features,
-    const std::vector<base::Feature>& disabled_features) {
+    const std::vector<base::test::FeatureRef>& enabled_features,
+    const std::vector<base::test::FeatureRef>& disabled_features) {
   // Using shared memory requires mojo to be initialized (crbug.com/849207).
   mojo::core::Init();
 
diff --git a/media/gpu/test/video_test_environment.h b/media/gpu/test/video_test_environment.h
index 95670290..b8cba91a 100644
--- a/media/gpu/test/video_test_environment.h
+++ b/media/gpu/test/video_test_environment.h
@@ -50,8 +50,9 @@
  public:
   VideoTestEnvironment();
   // Features are overridden by given features in this environment.
-  VideoTestEnvironment(const std::vector<base::Feature>& enabled_features,
-                       const std::vector<base::Feature>& disabled_features);
+  VideoTestEnvironment(
+      const std::vector<base::test::FeatureRef>& enabled_features,
+      const std::vector<base::test::FeatureRef>& disabled_features);
   virtual ~VideoTestEnvironment();
 
   // ::testing::Environment implementation.
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index c707c63..d0fcbd9 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -472,8 +472,8 @@
   bool use_legacy = false;
   bool use_vd_vda = false;
   bool linear_output = false;
-  std::vector<base::Feature> disabled_features;
-  std::vector<base::Feature> enabled_features;
+  std::vector<base::test::FeatureRef> disabled_features;
+  std::vector<base::test::FeatureRef> enabled_features;
 
 #if defined(ARCH_CPU_ARM_FAMILY)
   enabled_features.push_back(media::kPreferLibYuvImageProcessor);
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 7671be5..712b8230 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -544,8 +544,8 @@
   bool use_legacy = false;
   bool use_vd_vda = false;
   bool linear_output = false;
-  std::vector<base::Feature> disabled_features;
-  std::vector<base::Feature> enabled_features;
+  std::vector<base::test::FeatureRef> disabled_features;
+  std::vector<base::test::FeatureRef> enabled_features;
 
 #if defined(ARCH_CPU_ARM_FAMILY)
   enabled_features.push_back(media::kPreferLibYuvImageProcessor);
diff --git a/media/gpu/video_encode_accelerator_perf_tests.cc b/media/gpu/video_encode_accelerator_perf_tests.cc
index 7fcd3bc..2ed2028c 100644
--- a/media/gpu/video_encode_accelerator_perf_tests.cc
+++ b/media/gpu/video_encode_accelerator_perf_tests.cc
@@ -791,7 +791,7 @@
   media::Bitrate::Mode bitrate_mode = media::Bitrate::Mode::kConstant;
   bool reverse = false;
   absl::optional<uint32_t> encode_bitrate;
-  std::vector<base::Feature> disabled_features;
+  std::vector<base::test::FeatureRef> disabled_features;
 
   // Parse command line arguments.
   base::FilePath::StringType output_folder = media::test::kDefaultOutputFolder;
diff --git a/media/gpu/video_encode_accelerator_tests.cc b/media/gpu/video_encode_accelerator_tests.cc
index 4e3a9de..2b48c65 100644
--- a/media/gpu/video_encode_accelerator_tests.cc
+++ b/media/gpu/video_encode_accelerator_tests.cc
@@ -871,7 +871,7 @@
   media::test::FrameOutputConfig frame_output_config;
   base::FilePath output_folder =
       base::FilePath(base::FilePath::kCurrentDirectory);
-  std::vector<base::Feature> disabled_features;
+  std::vector<base::test::FeatureRef> disabled_features;
 
   // Parse command line arguments.
   bool enable_bitstream_validator = true;
diff --git a/media/gpu/windows/d3d11_h264_accelerator.cc b/media/gpu/windows/d3d11_h264_accelerator.cc
index 3b346d8..4f3f9014 100644
--- a/media/gpu/windows/d3d11_h264_accelerator.cc
+++ b/media/gpu/windows/d3d11_h264_accelerator.cc
@@ -21,7 +21,6 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
-#include "ui/gl/gl_image_dxgi.h"
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/scoped_binders.h"
 
diff --git a/media/gpu/windows/d3d11_h265_accelerator.cc b/media/gpu/windows/d3d11_h265_accelerator.cc
index 2235519..ad15bf52 100644
--- a/media/gpu/windows/d3d11_h265_accelerator.cc
+++ b/media/gpu/windows/d3d11_h265_accelerator.cc
@@ -23,7 +23,6 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
-#include "ui/gl/gl_image_dxgi.h"
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/scoped_binders.h"
 
diff --git a/media/gpu/windows/d3d11_texture_wrapper.h b/media/gpu/windows/d3d11_texture_wrapper.h
index bd82b6aa..1e0fde475 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.h
+++ b/media/gpu/windows/d3d11_texture_wrapper.h
@@ -24,7 +24,6 @@
 #include "ui/gfx/hdr_metadata.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
-#include "ui/gl/gl_image_dxgi.h"
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/scoped_binders.h"
 
diff --git a/net/base/mock_network_change_notifier.cc b/net/base/mock_network_change_notifier.cc
index f5e1a0d..c3aae7b 100644
--- a/net/base/mock_network_change_notifier.cc
+++ b/net/base/mock_network_change_notifier.cc
@@ -84,6 +84,10 @@
   base::RunLoop().RunUntilIdle();
 }
 
+bool MockNetworkChangeNotifier::IsDefaultNetworkActiveInternal() {
+  return is_default_network_active_;
+}
+
 void MockNetworkChangeNotifier::SetConnectionTypeAndNotifyObservers(
     ConnectionType connection_type) {
   SetConnectionType(connection_type);
diff --git a/net/base/mock_network_change_notifier.h b/net/base/mock_network_change_notifier.h
index e3c50b0..f16fc8ee 100644
--- a/net/base/mock_network_change_notifier.h
+++ b/net/base/mock_network_change_notifier.h
@@ -61,6 +61,12 @@
     connection_cost_ = connection_cost;
   }
 
+  bool IsDefaultNetworkActiveInternal() override;
+
+  void SetIsDefaultNetworkActiveInternalForTesting(bool is_active) {
+    is_default_network_active_ = is_active;
+  }
+
   // Tells this class to ignore its cached connection cost value and instead
   // call the base class's implementation. This is intended to allow tests to
   // mock the product code's fallback to the default implementation in certain
@@ -84,6 +90,7 @@
       std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier);
 
   bool force_network_handles_supported_ = false;
+  bool is_default_network_active_ = true;
   ConnectionType connection_type_ = CONNECTION_UNKNOWN;
   ConnectionCost connection_cost_ = CONNECTION_COST_UNKNOWN;
   bool use_default_connection_cost_implementation_ = false;
diff --git a/services/audio/input_sync_writer.cc b/services/audio/input_sync_writer.cc
index 497031d..fcd5c00 100644
--- a/services/audio/input_sync_writer.cc
+++ b/services/audio/input_sync_writer.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/check.h"
 #include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
@@ -91,28 +92,32 @@
   // - Percentage of data written to fifo (and not to shared memory).
   // - Percentage of data dropped (fifo reached max size or socket buffer full).
   // - Glitch yes/no (at least 1 drop).
-  //
+
+  // The amount of data that has either been dropped or successfully written.
+  // This number does not include data that is in the fifo, which we do not know
+  // if it would have been dropped or successfully written.
+  size_t total_processed_data_count =
+      dropped_data_count_ + successful_write_count_;
+
+  DCHECK_LE(trailing_shared_memory_full_count_, shared_memory_full_count_);
+  DCHECK_LE(trailing_shared_memory_full_count_, write_count_);
+  DCHECK_LE(trailing_dropped_data_count_, dropped_data_count_);
+  DCHECK_LE(trailing_dropped_data_count_, total_processed_data_count);
+
   // Subtract 'trailing' counts that will happen if the renderer process was
   // killed or e.g. the page refreshed while the input device was open etc.
   // This trims off the end of both the error and write counts so that we
-  // preserve the proportion of counts before the teardown period. We pick
-  // the largest trailing count as the time we consider that the trailing errors
-  // begun, and subract that from the total write count.
-  DCHECK_LE(trailing_write_to_fifo_count_, write_to_fifo_count_);
-  DCHECK_LE(trailing_write_to_fifo_count_, write_count_);
-  DCHECK_LE(trailing_write_error_count_, write_error_count_);
-  DCHECK_LE(trailing_write_error_count_, write_count_);
+  // preserve the proportion of counts before the teardown period.
+  shared_memory_full_count_ -= trailing_shared_memory_full_count_;
+  write_count_ -= trailing_shared_memory_full_count_;
+  dropped_data_count_ -= trailing_dropped_data_count_;
+  total_processed_data_count -= trailing_dropped_data_count_;
 
-  write_to_fifo_count_ -= trailing_write_to_fifo_count_;
-  write_error_count_ -= trailing_write_error_count_;
-  write_count_ -=
-      std::max(trailing_write_to_fifo_count_, trailing_write_error_count_);
-
-  if (write_count_ == 0)
+  if (write_count_ == 0 || total_processed_data_count == 0)
     return;
 
   base::UmaHistogramEnumeration("Media.AudioCapturerAudioGlitches",
-                                write_error_count_ == 0
+                                dropped_data_count_ == 0
                                     ? AudioGlitchResult::kNoGlitches
                                     : AudioGlitchResult::kGlitches);
 
@@ -128,16 +133,16 @@
       write_count_ < kShortStreamMaxCallbackCount ? "Short" : "Long";
 
   int missed_deadline =
-      std::ceil(kPermilleScaling * static_cast<double>(write_to_fifo_count_) /
-                write_count_);
+      std::ceil(kPermilleScaling *
+                static_cast<double>(shared_memory_full_count_) / write_count_);
 
   base::UmaHistogramCustomCounts(
       "Media.AudioCapturerMissedReadDeadline2." + suffix,
       std::min(missed_deadline, kHistogramRange), 0, kHistogramRange + 1, 100);
 
   int dropped_data =
-      std::ceil(kPermilleScaling * static_cast<double>(write_error_count_) /
-                write_count_);
+      std::ceil(kPermilleScaling * static_cast<double>(dropped_data_count_) /
+                total_processed_data_count);
 
   base::UmaHistogramCustomCounts("Media.AudioCapturerDroppedData2." + suffix,
                                  std::min(dropped_data, kHistogramRange), 0,
@@ -145,7 +150,7 @@
 
   std::string log_string = base::StringPrintf(
       "AISW: number of detected audio glitches: %" PRIuS " out of %" PRIuS,
-      write_error_count_, write_count_);
+      dropped_data_count_, total_processed_data_count);
   log_callback_.Run(log_string);
 }
 
@@ -213,37 +218,61 @@
     }
   }
 
-  bool write_error = !WriteDataFromFifoToSharedMemory();
+  // If there is data in the fifo, write as much of it to shared memory as
+  // possible.
+  if (!overflow_data_.empty()) {
+    const size_t segment_count = audio_buses_.size();
+    auto data_it = overflow_data_.begin();
+
+    while (data_it != overflow_data_.end() &&
+           number_of_filled_segments_ < segment_count) {
+      // Write parameters to shared memory.
+      if (WriteDataToCurrentSegment(*data_it->audio_bus_, data_it->volume_,
+                                    data_it->key_pressed_,
+                                    data_it->capture_time_)) {
+        ++successful_write_count_;
+        trailing_shared_memory_full_count_ = 0;
+        trailing_dropped_data_count_ = 0;
+      } else {
+        // This happens only if writing to the socket buffer fails, which should
+        // be rare.
+        ++dropped_data_count_;
+        ++trailing_dropped_data_count_;
+      }
+      ++data_it;
+    }
+
+    // Erase all copied data from fifo.
+    overflow_data_.erase(overflow_data_.begin(), data_it);
+
+    if (overflow_data_.empty()) {
+      static const char* message = "AISW: Fifo emptied.";
+      log_callback_.Run(message);
+    }
+  }
 
   // Write the current data to the shared memory if there is room, otherwise
   // put it in the fifo.
   if (number_of_filled_segments_ < audio_buses_.size()) {
-    WriteParametersToCurrentSegment(volume, key_pressed, capture_time);
-
-    // Copy data into shared memory using pre-allocated audio buses.
-    data->CopyTo(audio_buses_[current_segment_id_].get());
-
-    if (!SignalDataWrittenAndUpdateCounters())
-      write_error = true;
-
-    trailing_write_to_fifo_count_ = 0;
+    DCHECK(overflow_data_.empty());
+    if (WriteDataToCurrentSegment(*data, volume, key_pressed, capture_time)) {
+      ++successful_write_count_;
+      trailing_shared_memory_full_count_ = 0;
+      trailing_dropped_data_count_ = 0;
+    } else {
+      // This happens only if writing to the socket buffer fails, which should
+      // be rare.
+      ++dropped_data_count_;
+      ++trailing_dropped_data_count_;
+    }
   } else {
-    if (!PushDataToFifo(data, volume, key_pressed, capture_time))
-      write_error = true;
+    if (!PushDataToFifo(*data, volume, key_pressed, capture_time)) {
+      ++dropped_data_count_;
+      ++trailing_dropped_data_count_;
+    }
 
-    ++write_to_fifo_count_;
-    ++trailing_write_to_fifo_count_;
-  }
-
-  // Increase write error counts if error, or reset the trailing error counter
-  // if all write operations went well (no data dropped).
-  if (write_error) {
-    ++write_error_count_;
-    ++trailing_write_error_count_;
-    TRACE_EVENT_INSTANT0("audio", "InputSyncWriter write error",
-                         TRACE_EVENT_SCOPE_THREAD);
-  } else {
-    trailing_write_error_count_ = 0;
+    ++shared_memory_full_count_;
+    ++trailing_shared_memory_full_count_;
   }
 }
 
@@ -279,23 +308,24 @@
 #endif
 }
 
-bool InputSyncWriter::PushDataToFifo(const media::AudioBus* data,
+bool InputSyncWriter::PushDataToFifo(const media::AudioBus& data,
                                      double volume,
                                      bool key_pressed,
                                      base::TimeTicks capture_time) {
   TRACE_EVENT1("audio", "InputSyncWriter::PushDataToFifo", "capture time (ms)",
                (capture_time - base::TimeTicks()).InMillisecondsF());
   if (overflow_data_.size() == kMaxOverflowBusesSize) {
-    // We use |write_error_count_| for capping number of log messages.
-    // |write_error_count_| also includes socket Send() errors, but those should
-    // be rare.
-    TRACE_EVENT_INSTANT0("audio", "InputSyncWriter::PushDataToFifo - overflow",
-                         TRACE_EVENT_SCOPE_THREAD);
-    if (write_error_count_ <= 50 && write_error_count_ % 10 == 0) {
+    // We use |dropped_data_count_| for capping number of log messages.
+    // |dropped_data_count_| also includes socket Send() errors, but those
+    // should be rare.
+    TRACE_EVENT_INSTANT0(
+        "audio", "InputSyncWriter::PushDataToFifo - overflow - dropped data",
+        TRACE_EVENT_SCOPE_THREAD);
+    if (dropped_data_count_ <= 50 && dropped_data_count_ % 10 == 0) {
       static const char* error_message = "AISW: No room in fifo.";
       LOG(WARNING) << error_message;
       log_callback_.Run(error_message);
-      if (write_error_count_ == 50) {
+      if (dropped_data_count_ == 50) {
         static const char* cap_error_message =
             "AISW: Log cap reached, suppressing further fifo overflow logs.";
         LOG(WARNING) << cap_error_message;
@@ -312,56 +342,20 @@
 
   // Push data to fifo.
   std::unique_ptr<media::AudioBus> audio_bus =
-      media::AudioBus::Create(data->channels(), data->frames());
-  data->CopyTo(audio_bus.get());
+      media::AudioBus::Create(data.channels(), data.frames());
+  data.CopyTo(audio_bus.get());
   overflow_data_.emplace_back(volume, key_pressed, capture_time,
                               std::move(audio_bus));
   DCHECK_LE(overflow_data_.size(), static_cast<size_t>(kMaxOverflowBusesSize));
-
   return true;
 }
 
-bool InputSyncWriter::WriteDataFromFifoToSharedMemory() {
-  TRACE_EVENT0("audio", "InputSyncWriter::WriteDataFromFifoToSharedMemory");
-  if (overflow_data_.empty())
-    return true;
-
-  const size_t segment_count = audio_buses_.size();
-  bool write_error = false;
-  auto data_it = overflow_data_.begin();
-
-  while (data_it != overflow_data_.end() &&
-         number_of_filled_segments_ < segment_count) {
-    // Write parameters to shared memory.
-    WriteParametersToCurrentSegment(data_it->volume_, data_it->key_pressed_,
-                                    data_it->capture_time_);
-
-    // Copy data from the fifo into shared memory using pre-allocated audio
-    // buses.
-    data_it->audio_bus_->CopyTo(audio_buses_[current_segment_id_].get());
-
-    if (!SignalDataWrittenAndUpdateCounters())
-      write_error = true;
-
-    ++data_it;
-  }
-
-  // Erase all copied data from fifo.
-  overflow_data_.erase(overflow_data_.begin(), data_it);
-
-  if (overflow_data_.empty()) {
-    static const char* message = "AISW: Fifo emptied.";
-    log_callback_.Run(message);
-  }
-
-  return !write_error;
-}
-
-void InputSyncWriter::WriteParametersToCurrentSegment(
-    double volume,
-    bool key_pressed,
-    base::TimeTicks capture_time) {
-  TRACE_EVENT1("audio", "WriteParametersToCurrentSegment", "capture time (ms)",
+bool InputSyncWriter::WriteDataToCurrentSegment(const media::AudioBus& data,
+                                                double volume,
+                                                bool key_pressed,
+                                                base::TimeTicks capture_time) {
+  CHECK(number_of_filled_segments_ < audio_buses_.size());
+  TRACE_EVENT1("audio", "WriteDataToCurrentSegment", "capture time (ms)",
                (capture_time - base::TimeTicks()).InMillisecondsF());
   uint8_t* ptr = static_cast<uint8_t*>(shared_memory_mapping_.memory());
   CHECK_LT(current_segment_id_, audio_buses_.size());
@@ -373,6 +367,11 @@
   buffer->params.capture_time_us =
       (capture_time - base::TimeTicks()).InMicroseconds();
   buffer->params.id = next_buffer_id_;
+
+  // Copy data into shared memory using pre-allocated audio buses.
+  data.CopyTo(audio_buses_[current_segment_id_].get());
+
+  return SignalDataWrittenAndUpdateCounters();
 }
 
 bool InputSyncWriter::SignalDataWrittenAndUpdateCounters() {
@@ -385,13 +384,13 @@
       static const char* error_message = "AISW: No room in socket buffer.";
       PLOG(WARNING) << error_message;
       log_callback_.Run(error_message);
-      TRACE_EVENT_INSTANT0("audio", "InputSyncWriter: No room in socket buffer",
-                           TRACE_EVENT_SCOPE_THREAD);
+      TRACE_EVENT_INSTANT0(
+          "audio", "InputSyncWriter: No room in socket buffer - dropped data",
+          TRACE_EVENT_SCOPE_THREAD);
     }
     return false;
-  } else {
-    had_socket_error_ = false;
   }
+  had_socket_error_ = false;
 
   if (++current_segment_id_ >= audio_buses_.size())
     current_segment_id_ = 0;
diff --git a/services/audio/input_sync_writer.h b/services/audio/input_sync_writer.h
index 4b4f061a..7758f2d 100644
--- a/services/audio/input_sync_writer.h
+++ b/services/audio/input_sync_writer.h
@@ -79,20 +79,18 @@
   // Push |data| and metadata to |audio_buffer_fifo_|. Returns true if
   // successful. Logs error and returns false if the fifo already reached the
   // maximum size.
-  bool PushDataToFifo(const media::AudioBus* data,
+  bool PushDataToFifo(const media::AudioBus& data,
                       double volume,
                       bool key_pressed,
                       base::TimeTicks capture_time);
 
-  // Writes as much data as possible from the fifo (|overflow_buses_|) to the
-  // shared memory ring buffer. Returns true if all operations were successful,
-  // otherwise false.
-  bool WriteDataFromFifoToSharedMemory();
-
-  // Write audio parameters to current segment in shared memory.
-  void WriteParametersToCurrentSegment(double volume,
-                                       bool key_pressed,
-                                       base::TimeTicks capture_time);
+  // Write data and audio parameters to current segment in shared memory.
+  // Returns true if the data was successfully written, returns false if it was
+  // dropped.
+  bool WriteDataToCurrentSegment(const media::AudioBus& data,
+                                 double volume,
+                                 bool key_pressed,
+                                 base::TimeTicks capture_time);
 
   // Signals over the socket that data has been written to the current segment.
   // Updates counters and returns true if successful. Logs error and returns
@@ -136,25 +134,30 @@
   // ensure the we don't overwrite data that hasn't been read yet.
   size_t number_of_filled_segments_ = 0;
 
-  // Counts the total number of calls to Write().
+  // Counts the total number of calls to InputSyncWriter::Write().
   size_t write_count_ = 0;
 
-  // Counts the number of writes to the fifo instead of to the shared memory.
-  size_t write_to_fifo_count_ = 0;
+  // Counts the number of calls to InputSyncWriter::Write() when the shared
+  // memory is full and we instead have to attempt to write to the fifo.
+  size_t shared_memory_full_count_ = 0;
 
-  // Counts the number of errors that causes data to be dropped, due to either
-  // the fifo or the socket buffer being full.
-  size_t write_error_count_ = 0;
+  // Counts the number of times that data is dropped, due to either the fifo or
+  // the socket buffer being full.
+  size_t dropped_data_count_ = 0;
+
+  // Counts the number of times we have written data to the shared memory and
+  // successfully communicated the write over the socket.
+  size_t successful_write_count_ = 0;
 
   // Denotes that the most recent socket error has been logged. Used to avoid
   // log spam.
   bool had_socket_error_ = false;
 
-  // Counts the fifo writes and errors we get during renderer process teardown
-  // so that we can account for that (subtract) when we calculate the overall
-  // counts.
-  size_t trailing_write_to_fifo_count_ = 0;
-  size_t trailing_write_error_count_ = 0;
+  // Counts the missed deadlines and drops we get during renderer process
+  // teardown so that we can account for that (subtract) when we calculate the
+  // overall counts.
+  size_t trailing_shared_memory_full_count_ = 0;
+  size_t trailing_dropped_data_count_ = 0;
 
   // Vector of audio buses allocated during construction and deleted in the
   // destructor.
diff --git a/services/network/public/cpp/simple_url_loader_throttle.cc b/services/network/public/cpp/simple_url_loader_throttle.cc
index 093b6e6..5528eb7 100644
--- a/services/network/public/cpp/simple_url_loader_throttle.cc
+++ b/services/network/public/cpp/simple_url_loader_throttle.cc
@@ -43,7 +43,7 @@
       return false;
 
     if (base::PowerMonitor::IsInitialized() &&
-        base::PowerMonitor::IsOnBatteryPower()) {
+        !base::PowerMonitor::IsOnBatteryPower()) {
       return false;
     }
 
diff --git a/services/network/public/cpp/simple_url_loader_throttle.h b/services/network/public/cpp/simple_url_loader_throttle.h
index 55f6b08..acae642 100644
--- a/services/network/public/cpp/simple_url_loader_throttle.h
+++ b/services/network/public/cpp/simple_url_loader_throttle.h
@@ -65,6 +65,7 @@
 
   void SetDelegateForTesting(std::unique_ptr<Delegate> delegate);
   void SetTimeoutForTesting(base::TimeDelta timeout);
+  Delegate& GetDelegateForTesting() { return *delegate_; }
 
  private:
   void OnTimeout();
diff --git a/services/network/public/cpp/simple_url_loader_unittest.cc b/services/network/public/cpp/simple_url_loader_unittest.cc
index 27e5a06..d9885ba5 100644
--- a/services/network/public/cpp/simple_url_loader_unittest.cc
+++ b/services/network/public/cpp/simple_url_loader_unittest.cc
@@ -29,6 +29,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/bind.h"
+#include "base/test/power_monitor_test.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
@@ -40,6 +41,8 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/mock_network_change_notifier.h"
+#include "net/base/network_change_notifier.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
@@ -66,6 +69,10 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/radio_utils.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
 namespace network {
 namespace {
 
@@ -3845,6 +3852,49 @@
   }
 }
 
+TEST(SimpleURLLoaderThrottleTest, ShouldThrottle) {
+  base::test::TaskEnvironment task_environment(
+      base::test::TaskEnvironment::MainThreadType::IO);
+  base::test::ScopedPowerMonitorTestSource power_monitor_source;
+  auto notifier = net::test::MockNetworkChangeNotifier::Create();
+  SimpleURLLoaderThrottle throttle = SimpleURLLoaderThrottle();
+
+  // Device NOT on battery power
+  power_monitor_source.SetOnBatteryPower(false);
+
+  // Device's DefaultNetwork NOT Active
+  notifier.get()->ForceNetworkHandlesSupported();
+  notifier.get()->SetIsDefaultNetworkActiveInternalForTesting(false);
+
+// Device's RadioConnectionType is kCell
+#if BUILDFLAG(IS_ANDROID)
+  base::android::RadioUtils::OverrideForTesting radio_utils_test;
+  radio_utils_test.SetConnectionTypeForTesting(
+      base::android::RadioConnectionType::kCell);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  EXPECT_FALSE(throttle.GetDelegateForTesting().ShouldThrottle());
+  power_monitor_source.SetOnBatteryPower(true);
+
+  // Device's DefaultNetwork is active
+  notifier.get()->SetIsDefaultNetworkActiveInternalForTesting(true);
+
+  EXPECT_FALSE(throttle.GetDelegateForTesting().ShouldThrottle());
+  notifier.get()->SetIsDefaultNetworkActiveInternalForTesting(false);
+
+// Device's connection type is != kCell
+#if BUILDFLAG(IS_ANDROID)
+  radio_utils_test.SetConnectionTypeForTesting(
+      base::android::RadioConnectionType::kWifi);
+
+  EXPECT_FALSE(throttle.GetDelegateForTesting().ShouldThrottle());
+  radio_utils_test.SetConnectionTypeForTesting(
+      base::android::RadioConnectionType::kCell);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  EXPECT_TRUE(throttle.GetDelegateForTesting().ShouldThrottle());
+}
+
 TEST(SimpleURLLoaderThrottleTest, BatchingDisabled_FeatureDisabled) {
   SimpleURLLoaderThrottle::ResetConfigForTesting();
   net::NetworkTrafficAnnotationTag traffic_annotation =
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 02a1921..2e5f636 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1189,25 +1189,6 @@
             ]
         }
     ],
-    "AutofillIgnoreAutocompleteForImport": [
-        {
-            "platforms": [
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillIgnoreAutocompleteForImport"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillKeyboardAccessory": [
         {
             "platforms": [
@@ -6440,7 +6421,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_20220926",
+                    "name": "Enabled_20221013",
                     "params": {
                         "minPhotosVersionForImage": "6.8",
                         "minPhotosVersionForVideo": "6.8"
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index fe3b316..87d9381 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -8495,6 +8495,7 @@
       ActivatedBeforeStarted
       InactivePageRestriction
       StartFailed
+      TimeoutBackgrounded
 
   # Fired when a prerender attempt is completed.
   experimental event prerenderAttemptCompleted
diff --git a/third_party/blink/public/mojom/cookie_manager/cookie_manager_automation.mojom b/third_party/blink/public/mojom/cookie_manager/cookie_manager_automation.mojom
index d9ded7b..20856a8 100644
--- a/third_party/blink/public/mojom/cookie_manager/cookie_manager_automation.mojom
+++ b/third_party/blink/public/mojom/cookie_manager/cookie_manager_automation.mojom
@@ -4,9 +4,17 @@
 
 module blink.test.mojom;
 
-// Provides a way for tests to modify all cookies where needed.
+import "services/network/public/mojom/cookie_manager.mojom";
+
+// Provides a way for tests to modify cookies for the calling frame within
+// the content_shell test environment. This should be used/expanded in cases
+// where the RestrictedCookieManager provides insufficient power or detail.
+// This interface is bound per RenderFrameHost.
 interface CookieManagerAutomation {
-  // Delete all cookies for the url of the calling frame.
   // See https://w3c.github.io/webdriver/#delete-all-cookies
   DeleteAllCookies() => ();
+  // See https://w3c.github.io/webdriver/#get-all-cookies
+  GetAllCookies() => (array<network.mojom.CookieWithAccessResult> cookies);
+  // See https://w3c.github.io/webdriver/#get-named-cookie
+  GetNamedCookie(string name) => (network.mojom.CookieWithAccessResult? cookie);
 };
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index f6a194d..548516f 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -1774,6 +1774,10 @@
 ]
 
 generated_dictionary_sources_for_testing_in_core = [
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_cookie.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_cookie.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_cookie_same_site.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_cookie_same_site.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_dictionary.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_dictionary.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_internal_dictionary_derived.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_core.gni b/third_party/blink/renderer/bindings/idl_in_core.gni
index 87307d91..d1dcb60 100644
--- a/third_party/blink/renderer/bindings/idl_in_core.gni
+++ b/third_party/blink/renderer/bindings/idl_in_core.gni
@@ -762,7 +762,10 @@
           "//third_party/blink/renderer/core/testing/internal_dictionary_derived_derived.idl",
           "//third_party/blink/renderer/core/testing/internal_settings.idl",
           "//third_party/blink/renderer/core/testing/internals.idl",
+          "//third_party/blink/renderer/core/testing/internals_cookie.idl",
           "//third_party/blink/renderer/core/testing/internals_delete_all_cookies.idl",
+          "//third_party/blink/renderer/core/testing/internals_get_all_cookies.idl",
+          "//third_party/blink/renderer/core/testing/internals_get_named_cookie.idl",
           "//third_party/blink/renderer/core/testing/attribution_reporting_automation.idl",
           "//third_party/blink/renderer/core/testing/origin_trials_test.idl",
           "//third_party/blink/renderer/core/testing/origin_trials_test_dictionary.idl",
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 8e91527..c886cff 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -467,8 +467,14 @@
     "testing/internal_settings.h",
     "testing/internals.cc",
     "testing/internals.h",
+    "testing/internals_cookies.cc",
+    "testing/internals_cookies.h",
     "testing/internals_delete_all_cookies.cc",
     "testing/internals_delete_all_cookies.h",
+    "testing/internals_get_all_cookies.cc",
+    "testing/internals_get_all_cookies.h",
+    "testing/internals_get_named_cookie.cc",
+    "testing/internals_get_named_cookie.h",
     "testing/intersection_observer_test_helper.h",
     "testing/mock_clipboard_host.cc",
     "testing/mock_clipboard_host.h",
@@ -1681,7 +1687,8 @@
     "//third_party/blink/renderer/platform:blink_fuzzer_test_support",
     "//third_party/libprotobuf-mutator",
   ]
-  seed_corpus = "//testing/libfuzzer/fuzzers/attribution_source_registration_corpus"
+  seed_corpus =
+      "//testing/libfuzzer/fuzzers/attribution_source_registration_corpus"
 }
 
 fuzzer_test("attribution_trigger_registration_fuzzer") {
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_test.cc b/third_party/blink/renderer/core/content_capture/content_capture_test.cc
index f37e51ed..c2aa985 100644
--- a/third_party/blink/renderer/core/content_capture/content_capture_test.cc
+++ b/third_party/blink/renderer/core/content_capture/content_capture_test.cc
@@ -152,14 +152,15 @@
   WebContentCaptureClient& client_;
 };
 
-class ContentCaptureTest
-    : public PageTestBase,
-      public ::testing::WithParamInterface<std::vector<base::Feature>> {
+class ContentCaptureTest : public PageTestBase,
+                           public ::testing::WithParamInterface<
+                               std::vector<base::test::FeatureRef>> {
  public:
   ContentCaptureTest() {
     EnablePlatform();
     feature_list_.InitWithFeatures(
-        GetParam(), /*disabled_features=*/std::vector<base::Feature>());
+        GetParam(),
+        /*disabled_features=*/std::vector<base::test::FeatureRef>());
   }
 
   void SetUp() override {
@@ -366,8 +367,8 @@
 INSTANTIATE_TEST_SUITE_P(
     ,
     ContentCaptureTest,
-    testing::Values(std::vector<base::Feature>{},
-                    std::vector<base::Feature>{
+    testing::Values(std::vector<base::test::FeatureRef>{},
+                    std::vector<base::test::FeatureRef>{
                         features::kContentCaptureConstantStreaming}));
 
 TEST_P(ContentCaptureTest, Basic) {
diff --git a/third_party/blink/renderer/core/css/perftest_data/.gitignore b/third_party/blink/renderer/core/css/perftest_data/.gitignore
new file mode 100644
index 0000000..a6c57f5
--- /dev/null
+++ b/third_party/blink/renderer/core/css/perftest_data/.gitignore
@@ -0,0 +1 @@
+*.json
diff --git a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
index 093089b..b093dd9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_anchor_query.cc
@@ -89,6 +89,13 @@
     return anchor_query;
   }
 
+  enum class Conflict {
+    // The first entry wins. The calls must be in the tree order.
+    kFirstInCallOrder,
+    // Overwrite existing entry if the new one is before the existing one.
+    kOverwriteIfBefore,
+  };
+
   void AddChild(const NGPhysicalFragment& fragment,
                 const PhysicalOffset& offset_from_fragmentainer,
                 const FragmentainerContext& fragmentainer) {
@@ -99,14 +106,15 @@
       DCHECK(it.value->fragment);
       AddAnchorReference(it.key, *it.value->fragment,
                          it.value->rect + offset_from_fragmentainer,
-                         fragmentainer);
+                         fragmentainer, Conflict::kFirstInCallOrder);
     }
   }
 
   void AddAnchorReference(const AtomicString& anchor_name,
                           const NGPhysicalFragment& fragment,
                           const PhysicalRect& physical_rect_in_fragmentainer,
-                          const FragmentainerContext& fragmentainer) {
+                          const FragmentainerContext& fragmentainer,
+                          Conflict conflict) {
     const LogicalRect rect_in_fragmentainer =
         fragmentainer.converter.ToLogical(physical_rect_in_fragmentainer);
     auto* new_value = MakeGarbageCollected<NGStitchedAnchorReference>(
@@ -115,16 +123,30 @@
     if (result.is_new_entry)
       return;
 
-    // If this is the same anchor-name on a different box, ignore it. The
-    // first one in the pre-order wins.
+    // If this is a fragment of the existing box, unite it with other fragments.
     NGStitchedAnchorReference* existing = result.stored_value->value;
-    if (existing->fragment->GetLayoutObject() !=
-        new_value->fragment->GetLayoutObject()) {
+    const LayoutObject* existing_object = existing->fragment->GetLayoutObject();
+    DCHECK(existing_object);
+    const LayoutObject* new_object = new_value->fragment->GetLayoutObject();
+    DCHECK(new_object);
+    if (existing_object == new_object) {
+      existing->Unite(rect_in_fragmentainer, fragmentainer.offset);
       return;
     }
 
-    // If this is a fragment of the same box, unite it.
-    existing->Unite(rect_in_fragmentainer, fragmentainer.offset);
+    // If this is the same anchor-name on a different box, the first one in the
+    // pre-order wins. Normally, the call order is in the layout-order, which is
+    // pre-order of the box tree. But OOFs may be laid out later, check the tree
+    // order in such case.
+    switch (conflict) {
+      case Conflict::kFirstInCallOrder:
+        DCHECK(existing_object->IsBeforeInPreOrder(*new_object));
+        break;
+      case Conflict::kOverwriteIfBefore:
+        if (new_object->IsBeforeInPreOrder(*existing_object))
+          *existing = *new_value;
+        break;
+    }
   }
 
   void Trace(Visitor* visitor) const { visitor->Trace(references); }
@@ -269,18 +291,24 @@
     DCHECK(containing_block);
     DCHECK_NE(containing_block, &root_);
     DCHECK(!skip_info.AncestorSkipped());
-    do {
+    // Skip the first containing block, because the spec defines "If el has the
+    // same containing block as query el, el is not absolutely positioned." That
+    // said, for absolutely positioned anchors should be invalid for the first
+    // containing block.
+    // https://tabatkins.github.io/specs/css-anchor-position/#determining
+    containing_block = containing_block->Container(&skip_info);
+    while (containing_block && containing_block != root_ &&
+           !skip_info.AncestorSkipped()) {
       NGStitchedAnchorQuery& query =
           EnsureStitchedAnchorQuery(*containing_block);
       if (!anchor_name.IsNull()) {
-        query.AddAnchorReference(anchor_name, fragment,
-                                 {offset_from_fragmentainer, fragment.Size()},
-                                 fragmentainer);
+        query.AddAnchorReference(
+            anchor_name, fragment, {offset_from_fragmentainer, fragment.Size()},
+            fragmentainer, NGStitchedAnchorQuery::Conflict::kOverwriteIfBefore);
       }
       query.AddChild(fragment, offset_from_fragmentainer, fragmentainer);
       containing_block = containing_block->Container(&skip_info);
-    } while (containing_block && containing_block != root_ &&
-             !skip_info.AncestorSkipped());
+    }
   }
 
   NGStitchedAnchorQuery& EnsureStitchedAnchorQuery(
@@ -407,27 +435,28 @@
   if (result.is_new_entry)
     return;
 
+  // If this is a fragment of the existing |LayoutObject|, unite the rect.
   DCHECK(result.stored_value->value);
   NGLogicalAnchorReference& existing = *result.stored_value->value;
   const LayoutObject* existing_object = existing.fragment->GetLayoutObject();
   DCHECK(existing_object);
   const LayoutObject* new_object = reference->fragment->GetLayoutObject();
   DCHECK(new_object);
-  if (existing_object != new_object) {
-    if (!reference->is_invalid && !existing.is_invalid) {
-      // If both new and existing values are valid, ignore the new value. This
-      // logic assumes the callers call this function in the correct order.
-      DCHECK(existing_object->IsBeforeInPreOrder(*new_object));
-      return;
-    }
-    // When out-of-flow objects are involved, callers can't guarantee the call
-    // order. Insert into the list in the tree order.
-    reference->InsertInPreOrderInto(&result.stored_value->value);
+  if (existing_object == new_object) {
+    existing.rect.Unite(reference->rect);
     return;
   }
 
-  // If this is a fragment from the same |LayoutObject|, unite the rect.
-  existing.rect.Unite(reference->rect);
+  // Ignore the new value if both new and existing values are valid. This logic
+  // assumes the callers call this function in the correct order.
+  if (!reference->is_invalid && !existing.is_invalid) {
+    DCHECK(existing_object->IsBeforeInPreOrder(*new_object));
+    return;
+  }
+
+  // When out-of-flow objects are involved, callers can't guarantee the call
+  // order. Insert into the list in the tree order.
+  reference->InsertInPreOrderInto(&result.stored_value->value);
 }
 
 void NGPhysicalAnchorQuery::SetFromLogical(
diff --git a/third_party/blink/renderer/core/loader/link_loader_test.cc b/third_party/blink/renderer/core/loader/link_loader_test.cc
index 25c8a505..438aa2a 100644
--- a/third_party/blink/renderer/core/loader/link_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/link_loader_test.cc
@@ -545,8 +545,8 @@
   LinkLoaderTestPrefetchPrivacyChanges()
       : privacy_changes_enabled_(GetParam()) {}
   void SetUp() override {
-    std::vector<base::Feature> enable_features;
-    std::vector<base::Feature> disabled_features;
+    std::vector<base::test::FeatureRef> enable_features;
+    std::vector<base::test::FeatureRef> disabled_features;
     if (GetParam()) {
       enable_features.push_back(features::kPrefetchPrivacyChanges);
     } else {
diff --git a/third_party/blink/renderer/core/testing/internals_cookie.idl b/third_party/blink/renderer/core/testing/internals_cookie.idl
new file mode 100644
index 0000000..7a24972b
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_cookie.idl
@@ -0,0 +1,17 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+enum InternalCookieSameSite { "None", "Lax", "Strict" };
+
+// This matches the spec in https://w3c.github.io/webdriver/#cookies.
+dictionary InternalCookie {
+    DOMString name;
+    DOMString value;
+    DOMString path;
+    DOMString domain;
+    boolean secure;
+    boolean httpOnly;
+    long long expiry;
+    InternalCookieSameSite sameSite;
+};
diff --git a/third_party/blink/renderer/core/testing/internals_cookies.cc b/third_party/blink/renderer/core/testing/internals_cookies.cc
new file mode 100644
index 0000000..b43fc4b
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_cookies.cc
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/testing/internals_cookies.h"
+#include "base/time/time.h"
+
+namespace blink {
+
+InternalCookie* CookieMojomToInternalCookie(
+    const network::mojom::blink::CookieWithAccessResultPtr& cookie,
+    v8::Isolate* isolate) {
+  InternalCookie* result = InternalCookie::Create(isolate);
+  result->setName(cookie->cookie.Name().c_str());
+  result->setValue(cookie->cookie.Value().c_str());
+  result->setPath(cookie->cookie.Path().c_str());
+  result->setDomain(cookie->cookie.Domain().c_str());
+  result->setSecure(cookie->cookie.IsSecure());
+  result->setHttpOnly(cookie->cookie.IsHttpOnly());
+  if (!cookie->cookie.ExpiryDate().is_null()) {
+    // Expiry is omitted if unspecified.
+    result->setExpiry(
+        (cookie->cookie.ExpiryDate() - base::Time::UnixEpoch()).InSeconds());
+  }
+  switch (cookie->cookie.SameSite()) {
+    case net::CookieSameSite::NO_RESTRICTION:
+      result->setSameSite(V8InternalCookieSameSite::Enum::kNone);
+      break;
+    case net::CookieSameSite::LAX_MODE:
+      result->setSameSite(V8InternalCookieSameSite::Enum::kLax);
+      break;
+    case net::CookieSameSite::STRICT_MODE:
+      result->setSameSite(V8InternalCookieSameSite::Enum::kStrict);
+      break;
+    case net::CookieSameSite::UNSPECIFIED:
+      // SameSite is omitted if unspecified.
+      break;
+  }
+  return result;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/internals_cookies.h b/third_party/blink/renderer/core/testing/internals_cookies.h
new file mode 100644
index 0000000..2077ee9
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_cookies.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_COOKIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_COOKIES_H_
+
+#include "services/network/public/mojom/cookie_manager.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_internal_cookie.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_internal_cookie_same_site.h"
+#include "v8/include/v8-isolate.h"
+
+namespace v8 {
+class Isolate;
+}  // namespace v8
+
+namespace blink {
+
+InternalCookie* CookieMojomToInternalCookie(
+    const network::mojom::blink::CookieWithAccessResultPtr& cookie,
+    v8::Isolate* isolate);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_COOKIES_H_
diff --git a/third_party/blink/renderer/core/testing/internals_get_all_cookies.cc b/third_party/blink/renderer/core/testing/internals_get_all_cookies.cc
new file mode 100644
index 0000000..8d7020fbe3
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_get_all_cookies.cc
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/testing/internals_get_all_cookies.h"
+
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/mojom/cookie_manager.mojom-blink.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/cookie_manager/cookie_manager_automation.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_internal_cookie.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_internal_cookie_same_site.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/testing/internals_cookies.h"
+
+namespace blink {
+
+// static
+ScriptPromise InternalsGetAllCookies::getAllCookies(ScriptState* script_state,
+                                                    Internals&) {
+  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+  mojo::Remote<test::mojom::blink::CookieManagerAutomation> cookie_manager;
+  window->GetBrowserInterfaceBroker().GetInterface(
+      cookie_manager.BindNewPipeAndPassReceiver());
+
+  ScriptPromiseResolver* resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+  // Get the interface so `cookie_manager` can be moved below.
+  test::mojom::blink::CookieManagerAutomation* raw_cookie_manager =
+      cookie_manager.get();
+  raw_cookie_manager->GetAllCookies(WTF::BindOnce(
+      [](ScriptPromiseResolver* resolver, ScriptState* script_state,
+         mojo::Remote<test::mojom::blink::CookieManagerAutomation>,
+         WTF::Vector<network::mojom::blink::CookieWithAccessResultPtr>
+             cookies) {
+        HeapVector<Member<InternalCookie>> cookie_results;
+        for (const auto& cookie : cookies) {
+          cookie_results.push_back(
+              CookieMojomToInternalCookie(cookie, script_state->GetIsolate()));
+        }
+        resolver->Resolve(cookie_results);
+      },
+      WrapPersistent(resolver), WrapPersistent(script_state),
+      // Keep `cookie_manager` alive to wait for callback.
+      std::move(cookie_manager)));
+  return promise;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/internals_get_all_cookies.h b/third_party/blink/renderer/core/testing/internals_get_all_cookies.h
new file mode 100644
index 0000000..8d8d034d
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_get_all_cookies.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_GET_ALL_COOKIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_GET_ALL_COOKIES_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class Internals;
+class ScriptPromise;
+class ScriptState;
+
+class InternalsGetAllCookies {
+  STATIC_ONLY(InternalsGetAllCookies);
+
+ public:
+  static ScriptPromise getAllCookies(ScriptState* script_state,
+                                     Internals& internals);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_GET_ALL_COOKIES_H_
diff --git a/third_party/blink/renderer/core/testing/internals_get_all_cookies.idl b/third_party/blink/renderer/core/testing/internals_get_all_cookies.idl
new file mode 100644
index 0000000..912e0b22
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_get_all_cookies.idl
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+    ImplementedAs=InternalsGetAllCookies
+] partial interface Internals {
+    // Get details for all cookies in the current context.
+    // See https://w3c.github.io/webdriver/#get-all-cookies
+    [CallWith=ScriptState] Promise<sequence<InternalCookie>> getAllCookies();
+};
diff --git a/third_party/blink/renderer/core/testing/internals_get_named_cookie.cc b/third_party/blink/renderer/core/testing/internals_get_named_cookie.cc
new file mode 100644
index 0000000..f4aeb40
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_get_named_cookie.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/testing/internals_get_named_cookie.h"
+
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/mojom/cookie_manager.mojom-blink.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/cookie_manager/cookie_manager_automation.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_internal_cookie.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_internal_cookie_same_site.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/testing/internals_cookies.h"
+
+namespace blink {
+
+// static
+ScriptPromise InternalsGetNamedCookie::getNamedCookie(ScriptState* script_state,
+                                                      Internals&,
+                                                      const String& name) {
+  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
+  mojo::Remote<test::mojom::blink::CookieManagerAutomation> cookie_manager;
+  window->GetBrowserInterfaceBroker().GetInterface(
+      cookie_manager.BindNewPipeAndPassReceiver());
+
+  ScriptPromiseResolver* resolver =
+      MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+  // Get the interface so `cookie_manager` can be moved below.
+  test::mojom::blink::CookieManagerAutomation* raw_cookie_manager =
+      cookie_manager.get();
+  raw_cookie_manager->GetNamedCookie(
+      name, WTF::BindOnce(
+                [](ScriptPromiseResolver* resolver, ScriptState* script_state,
+                   mojo::Remote<test::mojom::blink::CookieManagerAutomation>,
+                   network::mojom::blink::CookieWithAccessResultPtr cookie) {
+                  InternalCookie* result = nullptr;
+                  if (cookie) {
+                    result = CookieMojomToInternalCookie(
+                        cookie, script_state->GetIsolate());
+                  }
+                  resolver->Resolve(result);
+                },
+                WrapPersistent(resolver), WrapPersistent(script_state),
+                // Keep `cookie_manager` alive to wait for callback.
+                std::move(cookie_manager)));
+  return promise;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/internals_get_named_cookie.h b/third_party/blink/renderer/core/testing/internals_get_named_cookie.h
new file mode 100644
index 0000000..3928a8d
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_get_named_cookie.h
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_GET_NAMED_COOKIE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_GET_NAMED_COOKIE_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class Internals;
+class ScriptPromise;
+class ScriptState;
+
+class InternalsGetNamedCookie {
+  STATIC_ONLY(InternalsGetNamedCookie);
+
+ public:
+  static ScriptPromise getNamedCookie(ScriptState* script_state,
+                                      Internals& internals,
+                                      const String& name);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_INTERNALS_GET_NAMED_COOKIE_H_
diff --git a/third_party/blink/renderer/core/testing/internals_get_named_cookie.idl b/third_party/blink/renderer/core/testing/internals_get_named_cookie.idl
new file mode 100644
index 0000000..5fa0ec0
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/internals_get_named_cookie.idl
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+    ImplementedAs=InternalsGetNamedCookie
+] partial interface Internals {
+    // Get details for a cookie in the current context by name if it exists.
+    // See https://w3c.github.io/webdriver/#get-named-cookie
+    [CallWith=ScriptState] Promise<InternalCookie?> getNamedCookie(DOMString name);
+};
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc b/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
index e008672d..6c0bbe08 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/modules/clipboard/clipboard.h"
 #include "third_party/blink/renderer/modules/clipboard/clipboard_promise.h"
 #include "third_party/blink/renderer/modules/clipboard/clipboard_writer.h"
+#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
 #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
@@ -83,15 +84,15 @@
     }
 
     worker_pool::PostTask(
-        FROM_HERE, CrossThreadBindOnce(
-                       &ClipboardTextReader::EncodeOnBackgroundThread,
-                       std::move(plain_text), WrapCrossThreadPersistent(this),
-                       std::move(clipboard_task_runner_)));
+        FROM_HERE,
+        CrossThreadBindOnce(&ClipboardTextReader::EncodeOnBackgroundThread,
+                            std::move(plain_text), MakeCrossThreadHandle(this),
+                            std::move(clipboard_task_runner_)));
   }
 
   static void EncodeOnBackgroundThread(
       String plain_text,
-      ClipboardTextReader* reader,
+      CrossThreadHandle<ClipboardTextReader> reader,
       scoped_refptr<base::SingleThreadTaskRunner> clipboard_task_runner) {
     DCHECK(!IsMainThread());
 
@@ -101,10 +102,13 @@
     utf8_bytes.ReserveInitialCapacity(utf8_text.size());
     utf8_bytes.Append(utf8_text.data(), utf8_text.size());
 
-    PostCrossThreadTask(*clipboard_task_runner, FROM_HERE,
-                        CrossThreadBindOnce(&ClipboardTextReader::NextRead,
-                                            WrapCrossThreadPersistent(reader),
-                                            std::move(utf8_bytes)));
+    PostCrossThreadTask(
+        *clipboard_task_runner, FROM_HERE,
+        CrossThreadBindOnce(
+            &ClipboardTextReader::NextRead,
+            MakeUnwrappingCrossThreadHandle<ClipboardTextReader>(
+                std::move(reader)),
+            std::move(utf8_bytes)));
   }
 
   void NextRead(Vector<uint8_t> utf8_bytes) override {
@@ -160,16 +164,15 @@
       return;
     }
     worker_pool::PostTask(
-        FROM_HERE,
-        CrossThreadBindOnce(&ClipboardHtmlReader::EncodeOnBackgroundThread,
-                            std::move(sanitized_html),
-                            WrapCrossThreadPersistent(this),
-                            std::move(clipboard_task_runner_)));
+        FROM_HERE, CrossThreadBindOnce(
+                       &ClipboardHtmlReader::EncodeOnBackgroundThread,
+                       std::move(sanitized_html), MakeCrossThreadHandle(this),
+                       std::move(clipboard_task_runner_)));
   }
 
   static void EncodeOnBackgroundThread(
       String plain_text,
-      ClipboardHtmlReader* reader,
+      CrossThreadHandle<ClipboardHtmlReader> reader,
       scoped_refptr<base::SingleThreadTaskRunner> clipboard_task_runner) {
     DCHECK(!IsMainThread());
 
@@ -179,10 +182,11 @@
     utf8_bytes.ReserveInitialCapacity(utf8_text.size());
     utf8_bytes.Append(utf8_text.data(), utf8_text.size());
 
-    PostCrossThreadTask(*clipboard_task_runner, FROM_HERE,
-                        CrossThreadBindOnce(&ClipboardHtmlReader::NextRead,
-                                            WrapCrossThreadPersistent(reader),
-                                            std::move(utf8_bytes)));
+    PostCrossThreadTask(
+        *clipboard_task_runner, FROM_HERE,
+        CrossThreadBindOnce(&ClipboardHtmlReader::NextRead,
+                            MakeUnwrappingCrossThreadHandle(std::move(reader)),
+                            std::move(utf8_bytes)));
   }
 
   void NextRead(Vector<uint8_t> utf8_bytes) override {
@@ -234,16 +238,15 @@
       return;
     }
     worker_pool::PostTask(
-        FROM_HERE,
-        CrossThreadBindOnce(&ClipboardSvgReader::EncodeOnBackgroundThread,
-                            std::move(sanitized_svg),
-                            WrapCrossThreadPersistent(this),
-                            std::move(clipboard_task_runner_)));
+        FROM_HERE, CrossThreadBindOnce(
+                       &ClipboardSvgReader::EncodeOnBackgroundThread,
+                       std::move(sanitized_svg), MakeCrossThreadHandle(this),
+                       std::move(clipboard_task_runner_)));
   }
 
   static void EncodeOnBackgroundThread(
       String plain_text,
-      ClipboardSvgReader* reader,
+      CrossThreadHandle<ClipboardSvgReader> reader,
       scoped_refptr<base::SingleThreadTaskRunner> clipboard_task_runner) {
     DCHECK(!IsMainThread());
 
@@ -253,10 +256,11 @@
     utf8_bytes.ReserveInitialCapacity(utf8_text.size());
     utf8_bytes.Append(utf8_text.data(), utf8_text.size());
 
-    PostCrossThreadTask(*clipboard_task_runner, FROM_HERE,
-                        CrossThreadBindOnce(&ClipboardSvgReader::NextRead,
-                                            WrapCrossThreadPersistent(reader),
-                                            std::move(utf8_bytes)));
+    PostCrossThreadTask(
+        *clipboard_task_runner, FROM_HERE,
+        CrossThreadBindOnce(&ClipboardSvgReader::NextRead,
+                            MakeUnwrappingCrossThreadHandle(std::move(reader)),
+                            std::move(utf8_bytes)));
   }
 
   void NextRead(Vector<uint8_t> utf8_bytes) override {
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
index 0b6abf2..1a94aa2 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
@@ -15,7 +15,9 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
+#include "third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h"
 #include "third_party/blink/renderer/modules/clipboard/clipboard.h"
+#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
@@ -44,20 +46,23 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+    // ArrayBufferContents is a thread-safe smart pointer around the backing
+    // store.
+    ArrayBufferContents contents = *raw_data->Content();
     worker_pool::PostTask(
         FROM_HERE,
         CrossThreadBindOnce(&ClipboardImageWriter::DecodeOnBackgroundThread,
-                            WrapCrossThreadPersistent(raw_data),
-                            WrapCrossThreadPersistent(this), task_runner));
+                            std::move(contents), MakeCrossThreadHandle(this),
+                            task_runner));
   }
   static void DecodeOnBackgroundThread(
-      DOMArrayBuffer* png_data,
-      ClipboardImageWriter* writer,
+      ArrayBufferContents png_data,
+      CrossThreadHandle<ClipboardImageWriter> writer,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
     DCHECK(!IsMainThread());
     std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
         SegmentReader::CreateFromSkData(
-            SkData::MakeWithoutCopy(png_data->Data(), png_data->ByteLength())),
+            SkData::MakeWithoutCopy(png_data.Data(), png_data.DataLength())),
         true, ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
         ColorBehavior::Tag());
     sk_sp<SkImage> image = nullptr;
@@ -65,10 +70,11 @@
     if (decoder)
       image = ImageBitmap::GetSkImageFromDecoder(std::move(decoder));
 
-    PostCrossThreadTask(*task_runner, FROM_HERE,
-                        CrossThreadBindOnce(&ClipboardImageWriter::Write,
-                                            WrapCrossThreadPersistent(writer),
-                                            std::move(image)));
+    PostCrossThreadTask(
+        *task_runner, FROM_HERE,
+        CrossThreadBindOnce(&ClipboardImageWriter::Write,
+                            MakeUnwrappingCrossThreadHandle(std::move(writer)),
+                            std::move(image)));
   }
   void Write(sk_sp<SkImage> image) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -99,25 +105,28 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+    // ArrayBufferContents is a thread-safe smart pointer around the backing
+    // store.
+    ArrayBufferContents contents = *raw_data->Content();
     worker_pool::PostTask(
         FROM_HERE,
         CrossThreadBindOnce(&ClipboardTextWriter::DecodeOnBackgroundThread,
-                            WrapCrossThreadPersistent(raw_data),
-                            WrapCrossThreadPersistent(this), task_runner));
+                            std::move(contents), MakeCrossThreadHandle(this),
+                            task_runner));
   }
   static void DecodeOnBackgroundThread(
-      DOMArrayBuffer* raw_data,
-      ClipboardTextWriter* writer,
+      ArrayBufferContents raw_data,
+      CrossThreadHandle<ClipboardTextWriter> writer,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
     DCHECK(!IsMainThread());
 
-    String wtf_string =
-        String::FromUTF8(reinterpret_cast<const LChar*>(raw_data->Data()),
-                         raw_data->ByteLength());
-    PostCrossThreadTask(*task_runner, FROM_HERE,
-                        CrossThreadBindOnce(&ClipboardTextWriter::Write,
-                                            WrapCrossThreadPersistent(writer),
-                                            std::move(wtf_string)));
+    String wtf_string = String::FromUTF8(
+        reinterpret_cast<const LChar*>(raw_data.Data()), raw_data.DataLength());
+    PostCrossThreadTask(
+        *task_runner, FROM_HERE,
+        CrossThreadBindOnce(&ClipboardTextWriter::Write,
+                            MakeUnwrappingCrossThreadHandle(std::move(writer)),
+                            std::move(wtf_string)));
   }
   void Write(const String& text) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
index 47a2d37..35fc84fe 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
@@ -336,8 +336,8 @@
       public testing::WithParamInterface<ThreadPriorityTestParam> {
  public:
   void InitWithRealtimePrioritySettings(bool is_enabled_by_finch) {
-    std::vector<base::Feature> enabled;
-    std::vector<base::Feature> disabled;
+    std::vector<base::test::FeatureRef> enabled;
+    std::vector<base::test::FeatureRef> disabled;
     if (is_enabled_by_finch) {
       enabled.push_back(features::kAudioWorkletThreadRealtimePriority);
     } else {
diff --git a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc
index c19b899..cb34139 100644
--- a/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/reclaimable_codec_test.cc
@@ -89,9 +89,9 @@
 class ReclaimBackgroundOnlyTest : public BaseReclaimableCodecTest {
  public:
   ReclaimBackgroundOnlyTest() {
-    std::vector<base::Feature> enabled_features{
+    std::vector<base::test::FeatureRef> enabled_features{
         kReclaimInactiveWebCodecs, kOnlyReclaimBackgroundWebCodecs};
-    std::vector<base::Feature> disabled_features{};
+    std::vector<base::test::FeatureRef> disabled_features{};
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
 
@@ -104,8 +104,9 @@
 class ReclaimForegroundSameAsBackgroundTest : public BaseReclaimableCodecTest {
  public:
   ReclaimForegroundSameAsBackgroundTest() {
-    std::vector<base::Feature> enabled_features{kReclaimInactiveWebCodecs};
-    std::vector<base::Feature> disabled_features{
+    std::vector<base::test::FeatureRef> enabled_features{
+        kReclaimInactiveWebCodecs};
+    std::vector<base::test::FeatureRef> disabled_features{
         kOnlyReclaimBackgroundWebCodecs};
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
@@ -118,8 +119,8 @@
 class ReclaimDisabledTest : public BaseReclaimableCodecTest {
  public:
   ReclaimDisabledTest() {
-    std::vector<base::Feature> enabled_features{};
-    std::vector<base::Feature> disabled_features{
+    std::vector<base::test::FeatureRef> enabled_features{};
+    std::vector<base::test::FeatureRef> disabled_features{
         kReclaimInactiveWebCodecs, kOnlyReclaimBackgroundWebCodecs};
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc
index 58e9419..ac933c8 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder_broker_test.cc
@@ -179,12 +179,12 @@
  public:
   VideoDecoderBrokerTest() {
     // Make sure we have the option of creating HW or SW decoders.
-    std::vector<base::Feature> disabled_features{
+    std::vector<base::test::FeatureRef> disabled_features{
         media::kForceHardwareVideoDecoders};
 
     // Make it easier to switch between HW and SW decoders, by initializing with
     // configs with a small height.
-    std::vector<base::Feature> enabled_features{
+    std::vector<base::test::FeatureRef> enabled_features{
         media::kResolutionBasedDecoderPriority};
 
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 9636aa1..0117953 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1863,6 +1863,7 @@
             size, buffer_format, buffer_usage, gpu::kNullSurfaceHandle,
             nullptr);
         if (gpu_memory_buffer) {
+          gpu_memory_buffer->SetColorSpace(color_space_);
           back_buffer_mailbox = sii->CreateSharedImage(
               gpu_memory_buffer.get(), gpu_memory_buffer_manager, color_space_,
               origin, kPremul_SkAlphaType, usage | additional_usage_flags);
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index cb0902c..46d2565e 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -31,6 +31,8 @@
     "collection_support/heap_linked_stack.h",
     "collection_support/heap_vector.h",
     "collection_support/heap_vector_backing.h",
+    "cross_thread_handle.h",
+    "cross_thread_handle_internal.h",
     "custom_spaces.cc",
     "custom_spaces.h",
     "disallow_new_wrapper.h",
@@ -140,6 +142,7 @@
     "../testing/run_all_tests.cc",
     "test/blink_gc_memory_dump_provider_test.cc",
     "test/concurrent_marking_test.cc",
+    "test/cross_thread_handle_test.cc",
     "test/heap_compact_test.cc",
     "test/heap_linked_stack_test.cc",
     "test/heap_test.cc",
diff --git a/third_party/blink/renderer/platform/heap/cross_thread_handle.h b/third_party/blink/renderer/platform/heap/cross_thread_handle.h
new file mode 100644
index 0000000..d3f9280
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/cross_thread_handle.h
@@ -0,0 +1,128 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CROSS_THREAD_HANDLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CROSS_THREAD_HANDLE_H_
+
+#include "third_party/blink/renderer/platform/heap/cross_thread_handle_internal.h"
+
+namespace blink {
+
+// A handle object that may be used to hold a garbage collection object from a
+// different thread than the object was created on. The handle supports
+// thread-safe copy/move/destruction. The underlying object may only be used
+// from the creation thread.
+//
+// Example posting to a static method that forwards back to an instance method
+// on the main thread:
+// ```
+// class PingPong final : public GarbageCollected<PingPong> {
+//  public:
+//   void Ping() {
+//    DCHECK(IsMainThread());
+//    worker_pool::PostTask(FROM_HERE,
+//                          CrossThreadBindOnce(&PingPong::PongOnBackground,
+//                                              MakeCrossThreadHandle(this),
+//                                              std::move(task_runner_)));
+//   }
+//
+//  private:
+//   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_ =
+//       GetTaskRunner();
+//
+//   void DoneOnMainThread() { DCHECK(IsMainThread()); }
+//
+//   static void PongOnBackground(
+//      CrossThreadHandle<PingPong> ping_pong,
+//      scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+//    DCHECK(!IsMainThread());
+//    PostCrossThreadTask(
+//        *task_runner, FROM_HERE,
+//        CrossThreadBindOnce(&PingPong::DoneOnMainThread,
+//                            MakeUnwrappingCrossThreadWeakHandle(
+//                                std::move(ping_pong))));
+//   }
+// };
+// ```
+template <typename T>
+using CrossThreadHandle = internal::
+    BasicCrossThreadHandle<T, internal::StrongCrossThreadHandleWeaknessPolicy>;
+
+// Utility function creating a `CrossThreadHandle` tracking the current source
+// location position in debugging configurations.
+template <typename T>
+CrossThreadHandle<T> MakeCrossThreadHandle(
+    T* value,
+    const CrossThreadHandleLocation& loc =
+        CROSS_THREAD_HANDLE_LOCATION_FROM_HERE) {
+  return CrossThreadHandle<T>(value);
+}
+
+// A weak handle object with similar restrictions as `CrossThreadHandle`.
+// The object is only held alive weakly, meaning that the object may be
+// reclaimed by the garbage collector. As a consequence, any value retrieved
+// from this handle must be checked against nullptr.
+template <typename T>
+using CrossThreadWeakHandle = internal::
+    BasicCrossThreadHandle<T, internal::WeakCrossThreadHandleWeaknessPolicy>;
+
+// Utility function creating a `CrossThreadWeakHandle` tracking the current
+// source location position in debugging configurations.
+template <typename T>
+CrossThreadWeakHandle<T> MakeCrossThreadWeakHandle(
+    T* value,
+    const CrossThreadHandleLocation& loc =
+        CROSS_THREAD_HANDLE_LOCATION_FROM_HERE) {
+  return CrossThreadWeakHandle<T>(value);
+}
+
+// A version of `CrossThreadHandle` that automatically unwraps into `T*` on
+// invocation of a bound function. This is useful for binding against regular
+// instance methods of a type.
+template <typename T>
+using UnwrappingCrossThreadHandle = internal::BasicUnwrappingCrossThreadHandle<
+    T,
+    internal::StrongCrossThreadHandleWeaknessPolicy>;
+
+// Utility function creating a `UnwrappingCrossThreadHandle`.
+template <typename T>
+UnwrappingCrossThreadHandle<T> MakeUnwrappingCrossThreadHandle(
+    const CrossThreadHandle<T>& handle) {
+  return UnwrappingCrossThreadHandle<T>(handle);
+}
+
+// Utility function creating a `UnwrappingCrossThreadHandle`.
+template <typename T>
+UnwrappingCrossThreadHandle<T> MakeUnwrappingCrossThreadHandle(
+    CrossThreadHandle<T>&& handle) {
+  return UnwrappingCrossThreadHandle<T>(std::move(handle));
+}
+
+// A version of `CrossThreadWeakHandle` that automatically unwraps into `T*`
+// on invocation of a bound function. This is useful for binding against regular
+// instance methods of a type.
+template <typename T>
+using UnwrappingCrossThreadWeakHandle =
+    internal::BasicUnwrappingCrossThreadHandle<
+        T,
+        internal::WeakCrossThreadHandleWeaknessPolicy>;
+
+// Utility function creating a `UnwrappingCrossThreadHandle`.
+template <typename T>
+UnwrappingCrossThreadWeakHandle<T> MakeUnwrappingCrossThreadWeakHandle(
+    const CrossThreadWeakHandle<T>& handle) {
+  return UnwrappingCrossThreadWeakHandle<T>(handle);
+}
+
+// Utility function creating a `UnwrappingCrossThreadHandle`.
+
+template <typename T>
+UnwrappingCrossThreadWeakHandle<T> MakeUnwrappingCrossThreadWeakHandle(
+    CrossThreadWeakHandle<T>&& handle) {
+  return UnwrappingCrossThreadWeakHandle<T>(std::move(handle));
+}
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CROSS_THREAD_HANDLE_H_
diff --git a/third_party/blink/renderer/platform/heap/cross_thread_handle_internal.h b/third_party/blink/renderer/platform/heap/cross_thread_handle_internal.h
new file mode 100644
index 0000000..a88ee222
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/cross_thread_handle_internal.h
@@ -0,0 +1,178 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CROSS_THREAD_HANDLE_INTERNAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CROSS_THREAD_HANDLE_INTERNAL_H_
+
+#include "base/bind.h"
+#include "base/threading/platform_thread.h"
+#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
+#include "v8/include/cppgc/cross-thread-persistent.h"
+#include "v8/include/cppgc/source-location.h"
+
+// Required to optimize away locations for builds that do not need them to avoid
+// binary size blowup.
+// Same as in `platform/heap/persistent.h`. Avoiding the include to avoid
+// exposing other types that are harder to use in concurrent contexts.
+#if BUILDFLAG(VERBOSE_PERSISTENT)
+#define CROSS_THREAD_HANDLE_LOCATION_FROM_HERE \
+  blink::CrossThreadHandleLocation::Current()
+#else  // !BUILDFLAG(VERBOSE_PERSISTENT)
+#define CROSS_THREAD_HANDLE_LOCATION_FROM_HERE \
+  blink::CrossThreadHandleLocation()
+#endif  // !BUILDFLAG(VERBOSE_PERSISTENT)
+
+namespace blink {
+
+template <typename T, typename WeaknessPolicy>
+class BasicUnwrappingCrossThreadHandle;
+
+// A source location that is used for tracking creation of
+// `CrossThreadHandle` and `CrossThreadWeakHandle` in debugging
+// configurations.
+using CrossThreadHandleLocation = cppgc::SourceLocation;
+
+namespace internal {
+
+struct StrongCrossThreadHandleWeaknessPolicy {
+  template <typename T>
+  using InternalRefType = cppgc::subtle::CrossThreadPersistent<T>;
+};
+
+struct WeakCrossThreadHandleWeaknessPolicy {
+  template <typename T>
+  using InternalRefType = cppgc::subtle::WeakCrossThreadPersistent<T>;
+};
+
+template <typename T, typename WeaknessPolicy>
+class BasicCrossThreadHandle {
+ public:
+  explicit BasicCrossThreadHandle(T* raw,
+                                  const CrossThreadHandleLocation& loc =
+                                      CROSS_THREAD_HANDLE_LOCATION_FROM_HERE)
+      : ref_(raw, loc) {}
+
+  ~BasicCrossThreadHandle() = default;
+  BasicCrossThreadHandle(BasicCrossThreadHandle&&) = default;
+  // Required by CrossThreadCopier.
+  BasicCrossThreadHandle(const BasicCrossThreadHandle&) = default;
+
+  BasicCrossThreadHandle& operator=(BasicCrossThreadHandle&&) = delete;
+  BasicCrossThreadHandle* operator=(const BasicCrossThreadHandle&) = delete;
+
+ protected:
+  // Returns a pointer to the garbage collected object. Must only be used from
+  // the creation thread and will crash if invoked on any other thread.
+  T* GetOnCreationThread() const {
+    CHECK_EQ(creation_thread_id_, base::PlatformThread::CurrentId());
+    return ref_.Get();
+  }
+
+ private:
+  template <typename U, typename V>
+  friend class blink::BasicUnwrappingCrossThreadHandle;
+
+  typename WeaknessPolicy::template InternalRefType<T> ref_;
+  const base::PlatformThreadId creation_thread_id_ =
+      base::PlatformThread::CurrentId();
+};
+
+template <typename T, typename WeaknessPolicy>
+class BasicUnwrappingCrossThreadHandle final
+    : public BasicCrossThreadHandle<T, WeaknessPolicy> {
+  using Base = BasicCrossThreadHandle<T, WeaknessPolicy>;
+
+ public:
+  explicit BasicUnwrappingCrossThreadHandle(
+      BasicCrossThreadHandle<T, WeaknessPolicy>&& handle)
+      : Base(std::move(handle)) {}
+  explicit BasicUnwrappingCrossThreadHandle(
+      const BasicCrossThreadHandle<T, WeaknessPolicy>& handle)
+      : Base(handle) {}
+
+  ~BasicUnwrappingCrossThreadHandle() = default;
+  BasicUnwrappingCrossThreadHandle(BasicUnwrappingCrossThreadHandle&&) =
+      default;
+  // Required by CrossThreadCopier.
+  BasicUnwrappingCrossThreadHandle(const BasicUnwrappingCrossThreadHandle&) =
+      default;
+
+  // Re-expose the actual getter for the underlying object.
+  using Base::GetOnCreationThread;
+
+  // Returns whether a value is set.  May only be accessed on the thread the
+  // original CrossThreadHandle object was created.
+  //
+  // Exposed so the callback implementation can test if weak handles are still
+  // live before trying to run the callback.
+  explicit operator bool() const { return Base::GetOnCreationThread(); }
+
+  BasicUnwrappingCrossThreadHandle& operator=(
+      BasicUnwrappingCrossThreadHandle&&) = delete;
+  BasicUnwrappingCrossThreadHandle* operator=(
+      const BasicUnwrappingCrossThreadHandle&) = delete;
+};
+
+}  // namespace internal
+}  // namespace blink
+
+namespace WTF {
+
+template <typename T, typename WeaknessPolicy>
+struct CrossThreadCopier<
+    blink::internal::BasicCrossThreadHandle<T, WeaknessPolicy>>
+    : public CrossThreadCopierPassThrough<
+          blink::internal::BasicCrossThreadHandle<T, WeaknessPolicy>> {};
+
+template <typename T, typename WeaknessPolicy>
+struct CrossThreadCopier<
+    blink::internal::BasicUnwrappingCrossThreadHandle<T, WeaknessPolicy>>
+    : public CrossThreadCopierPassThrough<
+          blink::internal::BasicUnwrappingCrossThreadHandle<T,
+                                                            WeaknessPolicy>> {};
+
+}  // namespace WTF
+
+namespace base {
+
+template <typename T>
+struct IsWeakReceiver<blink::internal::BasicUnwrappingCrossThreadHandle<
+    T,
+    blink::internal::WeakCrossThreadHandleWeaknessPolicy>> : std::true_type {};
+
+template <typename T, typename WeaknessPolicy>
+struct BindUnwrapTraits<
+    blink::internal::BasicUnwrappingCrossThreadHandle<T, WeaknessPolicy>> {
+  static T* Unwrap(
+      const blink::internal::BasicUnwrappingCrossThreadHandle<T,
+                                                              WeaknessPolicy>&
+          wrapped) {
+    return wrapped.GetOnCreationThread();
+  }
+};
+
+template <typename T, typename WeaknessPolicy>
+struct MaybeValidTraits<
+    blink::internal::BasicCrossThreadHandle<T, WeaknessPolicy>> {
+  static bool MaybeValid(
+      const blink::internal::BasicCrossThreadHandle<T, WeaknessPolicy>& p) {
+    return true;
+  }
+};
+
+template <typename T, typename WeaknessPolicy>
+struct MaybeValidTraits<
+    blink::internal::BasicUnwrappingCrossThreadHandle<T, WeaknessPolicy>> {
+  static bool MaybeValid(
+      const blink::internal::BasicUnwrappingCrossThreadHandle<T,
+                                                              WeaknessPolicy>&
+          p) {
+    return true;
+  }
+};
+
+}  // namespace base
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CROSS_THREAD_HANDLE_INTERNAL_H_
diff --git a/third_party/blink/renderer/platform/heap/persistent.h b/third_party/blink/renderer/platform/heap/persistent.h
index de77a8a..1992c0f8 100644
--- a/third_party/blink/renderer/platform/heap/persistent.h
+++ b/third_party/blink/renderer/platform/heap/persistent.h
@@ -35,6 +35,8 @@
 // CrossThreadPersistent allows retaining objects from threads other than the
 // thread that owns the heap of the corresponding object.
 //
+// Strongly prefer using `CrossThreadHandle` if the use case allows.
+//
 // Caveats:
 // - Does not protect the heap owning an object from terminating. E.g., posting
 //   a task with a CrossThreadPersistent for `this` will result in a
@@ -48,6 +50,8 @@
 // CrossThreadWeakPersistent allows weakly retaining objects from threads other
 // than the thread that owns the heap of the corresponding object.
 //
+// Strongly prefer using `CrossThreadWeakHandle` if the use case allows.
+//
 // Caveats:
 // - Does not protect the heap owning an object from termination, as the
 //   reference is weak.
diff --git a/third_party/blink/renderer/platform/heap/test/cross_thread_handle_test.cc b/third_party/blink/renderer/platform/heap/test/cross_thread_handle_test.cc
new file mode 100644
index 0000000..0ae6909
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/test/cross_thread_handle_test.cc
@@ -0,0 +1,253 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/thread_pool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace WTF {
+
+template <>
+struct CrossThreadCopier<base::internal::UnretainedWrapper<void>>
+    : public CrossThreadCopierPassThrough<
+          base::internal::UnretainedWrapper<void>> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+}  // namespace WTF
+
+namespace blink {
+namespace {
+
+class CrossThreadHandleTest : public TestSupportingGC {};
+
+class PingPongBase;
+class GCed final : public GarbageCollected<GCed> {
+ public:
+  void Trace(Visitor*) const {}
+
+  void SetReceivedPong(scoped_refptr<PingPongBase>);
+};
+
+TEST_F(CrossThreadHandleTest, GetOnCreationThread) {
+  auto* gced = MakeGarbageCollected<GCed>();
+  auto handle = MakeCrossThreadHandle(gced);
+  PreciselyCollectGarbage();
+  EXPECT_EQ(
+      gced,
+      MakeUnwrappingCrossThreadHandle(std::move(handle)).GetOnCreationThread());
+}
+
+TEST_F(CrossThreadHandleTest, UnwrapperGetOnCreationThread) {
+  auto* gced = MakeGarbageCollected<GCed>();
+  auto handle = MakeCrossThreadHandle(gced);
+  PreciselyCollectGarbage();
+  auto unwrapping_handle = MakeUnwrappingCrossThreadHandle(std::move(handle));
+  PreciselyCollectGarbage();
+  EXPECT_EQ(gced, unwrapping_handle.GetOnCreationThread());
+}
+
+class PingPongBase : public WTF::ThreadSafeRefCounted<PingPongBase> {
+ public:
+  PingPongBase(scoped_refptr<base::SingleThreadTaskRunner> main_runner,
+               scoped_refptr<base::SequencedTaskRunner> thread_runner)
+      : main_runner_(std::move(main_runner)),
+        thread_runner_(std::move(thread_runner)),
+        needle_(MakeGarbageCollected<GCed>()) {}
+
+  bool ReceivedPong() const { return received_pong_; }
+
+  void SetReceivedPong() { received_pong_ = true; }
+
+ protected:
+  scoped_refptr<base::SingleThreadTaskRunner> main_runner_;
+  scoped_refptr<base::SequencedTaskRunner> thread_runner_;
+  WeakPersistent<GCed> needle_;
+  bool received_pong_ = false;
+};
+
+void GCed::SetReceivedPong(scoped_refptr<PingPongBase> ping_pong) {
+  ping_pong->SetReceivedPong();
+}
+
+class PassThroughPingPong final : public PingPongBase {
+ public:
+  PassThroughPingPong(scoped_refptr<base::SingleThreadTaskRunner> main_runner,
+                      scoped_refptr<base::SequencedTaskRunner> thread_runner)
+      : PingPongBase(std::move(main_runner), std::move(thread_runner)) {}
+
+  void Ping() {
+    PostCrossThreadTask(
+        *thread_runner_, FROM_HERE,
+        WTF::CrossThreadBindOnce(&PassThroughPingPong::PingOnOtherThread,
+                                 scoped_refptr(this),
+                                 MakeCrossThreadHandle(needle_.Get())));
+    TestSupportingGC::PreciselyCollectGarbage();
+  }
+
+ private:
+  static void PingOnOtherThread(scoped_refptr<PassThroughPingPong> ping_pong,
+                                CrossThreadHandle<GCed> handle) {
+    auto main_runner = ping_pong->main_runner_;
+    PostCrossThreadTask(
+        *main_runner, FROM_HERE,
+        WTF::CrossThreadBindOnce(&PassThroughPingPong::PongOnMainThread,
+                                 std::move(ping_pong), std::move(handle)));
+  }
+
+  static void PongOnMainThread(scoped_refptr<PassThroughPingPong> ping_pong,
+                               CrossThreadHandle<GCed> handle) {
+    TestSupportingGC::PreciselyCollectGarbage();
+    EXPECT_EQ(ping_pong->needle_.Get(),
+              MakeUnwrappingCrossThreadHandle(std::move(handle))
+                  .GetOnCreationThread());
+    ping_pong->SetReceivedPong();
+  }
+};
+
+TEST_F(CrossThreadHandleTest, PassThroughPingPong) {
+  auto thread_runner = base::ThreadPool::CreateSequencedTaskRunner({});
+  auto main_runner = task_environment_.GetMainThreadTaskRunner();
+  auto ping_pong =
+      base::MakeRefCounted<PassThroughPingPong>(main_runner, thread_runner);
+  ping_pong->Ping();
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(ping_pong->ReceivedPong());
+}
+
+class UnwrappingPingPong final : public PingPongBase {
+ public:
+  UnwrappingPingPong(scoped_refptr<base::SingleThreadTaskRunner> main_runner,
+                     scoped_refptr<base::SequencedTaskRunner> thread_runner)
+      : PingPongBase(std::move(main_runner), std::move(thread_runner)) {}
+
+  void Ping() {
+    PostCrossThreadTask(
+        *thread_runner_, FROM_HERE,
+        WTF::CrossThreadBindOnce(&UnwrappingPingPong::PingOnOtherThread,
+                                 scoped_refptr(this),
+                                 MakeCrossThreadHandle(needle_.Get())));
+    TestSupportingGC::PreciselyCollectGarbage();
+  }
+
+ private:
+  static void PingOnOtherThread(scoped_refptr<UnwrappingPingPong> ping_pong,
+                                CrossThreadHandle<GCed> handle) {
+    auto main_runner = ping_pong->main_runner_;
+    PostCrossThreadTask(
+        *main_runner, FROM_HERE,
+        WTF::CrossThreadBindOnce(
+            &UnwrappingPingPong::PongOnMainThread, std::move(ping_pong),
+            MakeUnwrappingCrossThreadHandle(std::move(handle))));
+  }
+
+  static void PongOnMainThread(scoped_refptr<UnwrappingPingPong> ping_pong,
+                               GCed* gced) {
+    // Unwrapping keeps the handle in scope during the call, so even a GC
+    // without stack cannot reclaim the object here.
+    TestSupportingGC::PreciselyCollectGarbage();
+    EXPECT_EQ(ping_pong->needle_.Get(), gced);
+    ping_pong->SetReceivedPong();
+  }
+};
+
+TEST_F(CrossThreadHandleTest, UnwrappingPingPong) {
+  auto thread_runner = base::ThreadPool::CreateSequencedTaskRunner({});
+  auto main_runner = task_environment_.GetMainThreadTaskRunner();
+  auto ping_pong =
+      base::MakeRefCounted<UnwrappingPingPong>(main_runner, thread_runner);
+  ping_pong->Ping();
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(ping_pong->ReceivedPong());
+}
+
+class BindToMethodPingPong final : public PingPongBase {
+ public:
+  BindToMethodPingPong(scoped_refptr<base::SingleThreadTaskRunner> main_runner,
+                       scoped_refptr<base::SequencedTaskRunner> thread_runner)
+      : PingPongBase(std::move(main_runner), std::move(thread_runner)) {}
+
+  void Ping() {
+    PostCrossThreadTask(
+        *thread_runner_, FROM_HERE,
+        WTF::CrossThreadBindOnce(&BindToMethodPingPong::PingOnOtherThread,
+                                 scoped_refptr(this),
+                                 MakeCrossThreadHandle(needle_.Get())));
+    TestSupportingGC::PreciselyCollectGarbage();
+    ASSERT_TRUE(needle_);
+  }
+
+ private:
+  static void PingOnOtherThread(scoped_refptr<BindToMethodPingPong> ping_pong,
+                                CrossThreadHandle<GCed> handle) {
+    auto main_runner = ping_pong->main_runner_;
+    PostCrossThreadTask(*main_runner, FROM_HERE,
+                        WTF::CrossThreadBindOnce(
+                            &GCed::SetReceivedPong,
+                            MakeUnwrappingCrossThreadHandle(std::move(handle)),
+                            std::move(ping_pong)));
+  }
+};
+
+TEST_F(CrossThreadHandleTest, BindToMethodPingPong) {
+  auto thread_runner = base::ThreadPool::CreateSequencedTaskRunner({});
+  auto main_runner = task_environment_.GetMainThreadTaskRunner();
+  auto ping_pong =
+      base::MakeRefCounted<BindToMethodPingPong>(main_runner, thread_runner);
+  ping_pong->Ping();
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(ping_pong->ReceivedPong());
+}
+
+class BindToMethodDiscardingPingPong final : public PingPongBase {
+ public:
+  BindToMethodDiscardingPingPong(
+      scoped_refptr<base::SingleThreadTaskRunner> main_runner,
+      scoped_refptr<base::SequencedTaskRunner> thread_runner)
+      : PingPongBase(std::move(main_runner), std::move(thread_runner)) {}
+
+  void Ping() {
+    PostCrossThreadTask(
+        *thread_runner_, FROM_HERE,
+        WTF::CrossThreadBindOnce(
+            &BindToMethodDiscardingPingPong::PingOnOtherThread,
+            scoped_refptr(this), MakeCrossThreadWeakHandle(needle_.Get())));
+    TestSupportingGC::PreciselyCollectGarbage();
+    ASSERT_FALSE(needle_);
+  }
+
+ private:
+  static void PingOnOtherThread(
+      scoped_refptr<BindToMethodDiscardingPingPong> ping_pong,
+      CrossThreadWeakHandle<GCed> handle) {
+    auto main_runner = ping_pong->main_runner_;
+    PostCrossThreadTask(
+        *main_runner, FROM_HERE,
+        WTF::CrossThreadBindOnce(
+            &GCed::SetReceivedPong,
+            MakeUnwrappingCrossThreadWeakHandle(std::move(handle)),
+            std::move(ping_pong)));
+  }
+};
+
+TEST_F(CrossThreadHandleTest, BindToMethodDiscardingPingPong) {
+  auto thread_runner = base::ThreadPool::CreateSequencedTaskRunner({});
+  auto main_runner = task_environment_.GetMainThreadTaskRunner();
+  auto ping_pong = base::MakeRefCounted<BindToMethodDiscardingPingPong>(
+      main_runner, thread_runner);
+  ping_pong->Ping();
+  task_environment_.RunUntilIdle();
+  EXPECT_FALSE(ping_pong->ReceivedPong());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter_test.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter_test.cc
index 91ae8663..2fe9684 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter_test.cc
@@ -200,8 +200,8 @@
         sdp_format_(webrtc::SdpVideoFormat(
             webrtc::CodecTypeToPayloadString(webrtc::kVideoCodecVP9))),
         spatial_index_(0) {
-    std::vector<base::Feature> enabled_features;
-    std::vector<base::Feature> disabled_features;
+    std::vector<base::test::FeatureRef> enabled_features;
+    std::vector<base::test::FeatureRef> disabled_features;
 #if BUILDFLAG(IS_WIN)
     enabled_features.push_back(::media::kD3D11Vp9kSVCHWDecoding);
 #endif
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index e6b940b3..8d6b04d 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -198,17 +198,19 @@
 
   // Constructs a FrameSchedulerImplTest with a list of features to enable and a
   // list of features to disable.
-  FrameSchedulerImplTest(std::vector<base::Feature> features_to_enable,
-                         std::vector<base::Feature> features_to_disable)
+  FrameSchedulerImplTest(
+      std::vector<base::test::FeatureRef> features_to_enable,
+      std::vector<base::test::FeatureRef> features_to_disable)
       : FrameSchedulerImplTest() {
     feature_list_.InitWithFeatures(features_to_enable, features_to_disable);
   }
 
   // Constructs a FrameSchedulerImplTest with a feature to enable, associated
   // params, and a list of features to disable.
-  FrameSchedulerImplTest(const base::Feature& feature_to_enable,
-                         const base::FieldTrialParams& feature_to_enable_params,
-                         const std::vector<base::Feature>& features_to_disable)
+  FrameSchedulerImplTest(
+      const base::Feature& feature_to_enable,
+      const base::FieldTrialParams& feature_to_enable_params,
+      const std::vector<base::test::FeatureRef>& features_to_disable)
       : FrameSchedulerImplTest() {
     feature_list_.InitWithFeaturesAndParameters(
         {{feature_to_enable, feature_to_enable_params}}, features_to_disable);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 49a1318..b2c549d 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -395,8 +395,9 @@
 
 class MainThreadSchedulerImplTest : public testing::Test {
  public:
-  MainThreadSchedulerImplTest(std::vector<Feature> features_to_enable,
-                              std::vector<Feature> features_to_disable) {
+  MainThreadSchedulerImplTest(
+      const std::vector<base::test::FeatureRef>& features_to_enable,
+      const std::vector<base::test::FeatureRef>& features_to_disable) {
     feature_list_.InitWithFeatures(features_to_enable, features_to_disable);
   }
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index 74b0d39f..b73ecaf5 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -94,8 +94,8 @@
     feature_list_.InitAndEnableFeature(blink::features::kStopInBackground);
   }
 
-  PageSchedulerImplTest(std::vector<base::Feature> enabled_features,
-                        std::vector<base::Feature> disabled_features) {
+  PageSchedulerImplTest(std::vector<base::test::FeatureRef> enabled_features,
+                        std::vector<base::test::FeatureRef> disabled_features) {
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-multicol-003.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-multicol-003.tentative.html
new file mode 100644
index 0000000..21425112
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-multicol-003.tentative.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>Anchor name resolution of OOF anchors in multicol</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#propdef-anchor-name">
+<link rel="help" href="https://tabatkins.github.io/specs/css-anchor-position/#anchor-size">
+<link rel="author" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<style>
+.relpos {
+  position: relative;
+}
+.abspos {
+  position: absolute;
+}
+.columns {
+  column-count: 6;
+  column-fill: auto;
+  column-gap: 10px;
+  column-width: 20px;
+  width: 170px;
+  height: 50px;
+}
+.spacer {
+  height: 10px;
+  background: pink;
+}
+.anchor {
+  anchor-name: --a1;
+  margin-left: 5px;
+  width: 10px;
+  background: orange;
+}
+.target {
+  position: absolute;
+  background: lime;
+  opacity: .3;
+  left: anchor(--a1 left);
+  top: anchor(--a1 top);
+  width: anchor-size(--a1 width);
+  height: anchor-size(--a1 height);
+}
+</style>
+<body onload="checkLayout('.target')">
+  <div class="spacer"></div>
+  <div class="columns">
+    <div class="relpos">
+      <div class="relpos">
+        <div class="spacer"></div>
+        <div class="anchor abspos" style="top: 120px; height: 100px"></div>
+        <div class="anchor" style="height: 60px"></div>
+        <div class="target"
+             data-expected-width=40 data-expected-height=50></div>
+      </div>
+      <div class="target"
+           data-expected-width=70 data-expected-height=50></div>
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
index 8c694fac..cc280084 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
@@ -50,6 +50,8 @@
 ### Cookies ###
 ```eval_rst
 .. js:autofunction:: test_driver.delete_all_cookies
+.. js:autofunction:: test_driver.get_all_cookies
+.. js:autofunction:: test_driver.get_named_cookie
 ```
 
 ### Permissions ###
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/aria-element-reflection.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/dom/nodes/aria-element-reflection.tentative-expected.txt
deleted file mode 100644
index a4b34447..0000000
--- a/third_party/blink/web_tests/external/wpt/dom/nodes/aria-element-reflection.tentative-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-This is a testharness.js-based test.
-PASS aria-activedescendant element reflection
-PASS If the content attribute is set directly, the IDL attribute getter always returns the first element whose ID matches the content attribute.
-PASS Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string
-PASS Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.
-PASS aria-errormessage
-PASS aria-details
-PASS Deleting a reflected element should return null for the IDL attribute and cause the content attribute to become stale.
-PASS Changing the ID of an element causes the content attribute to become out of sync.
-PASS Reparenting an element into a descendant shadow scope hides the element reference.
-PASS Reparenting referenced element cannot cause retargeting of reference.
-PASS Element reference set in invalid scope remains intact throughout move to valid scope.
-FAIL aria-labelledby. assert_equals: check idl attribute caching after parsing expected [Element node <div id="billingElement">Billing</div>, Element node <div id="nameElement">Name</div>] but got [Element node <div id="billingElement">Billing</div>, Element node <div id="nameElement">Name</div>]
-PASS aria-controls.
-PASS aria-describedby.
-PASS aria-flowto.
-PASS aria-owns.
-PASS shadow DOM behaviour for FrozenArray element reflection.
-PASS Moving explicitly set elements across shadow DOM boundaries.
-PASS Moving explicitly set elements around within the same scope, and removing from the DOM.
-PASS Reparenting.
-PASS Attaching element reference before it's inserted into the DOM.
-PASS Cross-document references and moves.
-PASS Adopting element keeps references.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/aria-element-reflection.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/aria-element-reflection.tentative-expected.txt
deleted file mode 100644
index e5b83c9..0000000
--- a/third_party/blink/web_tests/external/wpt/html/dom/aria-element-reflection.tentative-expected.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-This is a testharness.js-based test.
-PASS aria-activedescendant element reflection
-PASS If the content attribute is set directly, the IDL attribute getter always returns the first element whose ID matches the content attribute.
-PASS Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string
-PASS Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.
-PASS aria-errormessage
-PASS aria-details
-PASS Deleting a reflected element should return null for the IDL attribute and cause the content attribute to become stale.
-PASS Changing the ID of an element causes the content attribute to become out of sync.
-PASS Reparenting an element into a descendant shadow scope hides the element reference.
-PASS Reparenting referenced element cannot cause retargeting of reference.
-PASS Element reference set in invalid scope remains intact throughout move to valid scope.
-FAIL aria-labelledby. assert_equals: check idl attribute caching after parsing expected [Element node <div id="billingElement">Billing</div>, Element node <div id="nameElement">Name</div>] but got [Element node <div id="billingElement">Billing</div>, Element node <div id="nameElement">Name</div>]
-PASS aria-controls.
-PASS aria-describedby.
-PASS aria-flowto.
-PASS aria-owns.
-PASS shadow DOM behaviour for FrozenArray element reflection.
-PASS Moving explicitly set elements across shadow DOM boundaries.
-PASS Moving explicitly set elements around within the same scope, and removing from the DOM.
-PASS Reparenting.
-PASS Attaching element reference before it's inserted into the DOM.
-PASS Cross-document references and moves.
-PASS Adopting element keeps references.
-FAIL Caching invariant different attributes. assert_equals: Caching invariant for ariaControlsElements expected [Element node <div id="cachingInvariantElement1"></div>, Element node <div id="cachingInvariantElement2"></div>] but got [Element node <div id="cachingInvariantElement1"></div>, Element node <div id="cachingInvariantElement2"></div>]
-FAIL Caching invariant different elements. assert_equals: Caching invariant for ariaDescribedByElements in one elemnt expected [Element node <div id="cachingInvariantElement1"></div>, Element node <div id="cachingInvariantElement2"></div>] but got [Element node <div id="cachingInvariantElement1"></div>, Element node <div id="cachingInvariantElement2"></div>]
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.html
new file mode 100644
index 0000000..ab9ac07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>TestDriver get_all_cookies method in HTTP</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+promise_test(async t => {
+  const kTenDaysFromNow = new Date(Date.now() + 10 * 24 * 60 * 60 * 1000);
+  document.cookie = "test0=0";
+  document.cookie = `test1=1; Expires=${kTenDaysFromNow.toUTCString()}`;
+  document.cookie = "test2=2; Path=/";
+  // document.cookie = "test3=3; HttpOnly"; This is set in the headers file.
+  document.cookie = "test4=4; Secure";
+  document.cookie = "test5=5; SameSite=Strict";
+  document.cookie = "test6=6; SameSite=None; Secure";
+  document.cookie = "test7=7; SameSite=Lax";
+  const cookies = await test_driver.get_all_cookies();
+  assert_equals(cookies.length, 6);
+  let cookieMap = new Map();
+  for (const cookie of cookies) {
+    cookieMap.set(cookie["name"], cookie);
+  }
+
+  // test0
+  assert_equals(cookieMap.get("test0")["name"], "test0");
+  assert_equals(cookieMap.get("test0")["value"], "0");
+  assert_equals(cookieMap.get("test0")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test0")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test0")["secure"], false);
+  assert_equals(cookieMap.get("test0")["httpOnly"], false);
+  assert_equals(cookieMap.get("test0")["expiry"], undefined);
+  assert_equals(cookieMap.get("test0")["sameSite"], undefined);
+
+  // test1 [Expires in 10 days]
+  assert_equals(cookieMap.get("test1")["name"], "test1");
+  assert_equals(cookieMap.get("test1")["value"], "1");
+  assert_equals(cookieMap.get("test1")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test1")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test1")["secure"], false);
+  assert_equals(cookieMap.get("test1")["httpOnly"], false);
+  assert_equals(cookieMap.get("test1")["expiry"], Math.floor(kTenDaysFromNow.getTime()/1000));
+  assert_equals(cookieMap.get("test1")["sameSite"], undefined);
+
+  // test2 [Path /]
+  assert_equals(cookieMap.get("test2")["name"], "test2");
+  assert_equals(cookieMap.get("test2")["value"], "2");
+  assert_equals(cookieMap.get("test2")["path"], "/");
+  assert_equals(cookieMap.get("test2")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test2")["secure"], false);
+  assert_equals(cookieMap.get("test2")["httpOnly"], false);
+  assert_equals(cookieMap.get("test2")["expiry"], undefined);
+  assert_equals(cookieMap.get("test2")["sameSite"], undefined);
+
+  // test3 [HttpOnly]
+  assert_equals(cookieMap.get("test3")["name"], "test3");
+  assert_equals(cookieMap.get("test3")["value"], "3");
+  assert_equals(cookieMap.get("test3")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test3")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test3")["secure"], false);
+  assert_equals(cookieMap.get("test3")["httpOnly"], true);
+  assert_equals(cookieMap.get("test3")["expiry"], undefined);
+  assert_equals(cookieMap.get("test3")["sameSite"], undefined);
+
+  // test4 [Secure] Omitted
+
+  // test5 [SameSite Strict]
+  assert_equals(cookieMap.get("test5")["name"], "test5");
+  assert_equals(cookieMap.get("test5")["value"], "5");
+  assert_equals(cookieMap.get("test5")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test5")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test5")["secure"], false);
+  assert_equals(cookieMap.get("test5")["httpOnly"], false);
+  assert_equals(cookieMap.get("test5")["expiry"], undefined);
+  assert_equals(cookieMap.get("test5")["sameSite"], "Strict");
+
+  // test6 [SameSite None] Omitted
+
+  // test7 [SameSite Lax]
+  assert_equals(cookieMap.get("test7")["name"], "test7");
+  assert_equals(cookieMap.get("test7")["value"], "7");
+  assert_equals(cookieMap.get("test7")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test7")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test7")["secure"], false);
+  assert_equals(cookieMap.get("test7")["httpOnly"], false);
+  assert_equals(cookieMap.get("test7")["expiry"], undefined);
+  assert_equals(cookieMap.get("test7")["sameSite"], "Lax");
+}, "Get all HTTP cookies");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.html.headers b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.html.headers
new file mode 100644
index 0000000..3dc39a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.html.headers
@@ -0,0 +1 @@
+Set-Cookie: test3=3; HttpOnly
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.https.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.https.html
new file mode 100644
index 0000000..1359a75
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.https.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>TestDriver get_all_cookies method in HTTPS</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+promise_test(async t => {
+  const kTenDaysFromNow = new Date(Date.now() + 10 * 24 * 60 * 60 * 1000);
+  document.cookie = "test0=0";
+  document.cookie = `test1=1; Expires=${kTenDaysFromNow.toUTCString()}`;
+  document.cookie = "test2=2; Path=/";
+  // document.cookie = "test3=3; HttpOnly"; This is set in the headers file.
+  document.cookie = "test4=4; Secure";
+  document.cookie = "test5=5; SameSite=Strict";
+  document.cookie = "test6=6; SameSite=None; Secure";
+  document.cookie = "test7=7; SameSite=Lax";
+  const cookies = await test_driver.get_all_cookies();
+  assert_equals(cookies.length, 8);
+  let cookieMap = new Map();
+  for (const cookie of cookies) {
+    cookieMap.set(cookie["name"], cookie);
+  }
+
+  // test0
+  assert_equals(cookieMap.get("test0")["name"], "test0");
+  assert_equals(cookieMap.get("test0")["value"], "0");
+  assert_equals(cookieMap.get("test0")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test0")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test0")["secure"], false);
+  assert_equals(cookieMap.get("test0")["httpOnly"], false);
+  assert_equals(cookieMap.get("test0")["expiry"], undefined);
+  assert_equals(cookieMap.get("test0")["sameSite"], undefined);
+
+  // test1 [Expires in 10 days]
+  assert_equals(cookieMap.get("test1")["name"], "test1");
+  assert_equals(cookieMap.get("test1")["value"], "1");
+  assert_equals(cookieMap.get("test1")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test1")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test1")["secure"], false);
+  assert_equals(cookieMap.get("test1")["httpOnly"], false);
+  assert_equals(cookieMap.get("test1")["expiry"], Math.floor(kTenDaysFromNow.getTime()/1000));
+  assert_equals(cookieMap.get("test1")["sameSite"], undefined);
+
+  // test2 [Path /]
+  assert_equals(cookieMap.get("test2")["name"], "test2");
+  assert_equals(cookieMap.get("test2")["value"], "2");
+  assert_equals(cookieMap.get("test2")["path"], "/");
+  assert_equals(cookieMap.get("test2")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test2")["secure"], false);
+  assert_equals(cookieMap.get("test2")["httpOnly"], false);
+  assert_equals(cookieMap.get("test2")["expiry"], undefined);
+  assert_equals(cookieMap.get("test2")["sameSite"], undefined);
+
+  // test3 [HttpOnly]
+  assert_equals(cookieMap.get("test3")["name"], "test3");
+  assert_equals(cookieMap.get("test3")["value"], "3");
+  assert_equals(cookieMap.get("test3")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test3")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test3")["secure"], false);
+  assert_equals(cookieMap.get("test3")["httpOnly"], true);
+  assert_equals(cookieMap.get("test3")["expiry"], undefined);
+  assert_equals(cookieMap.get("test3")["sameSite"], undefined);
+
+  // test4 [Secure]
+  assert_equals(cookieMap.get("test4")["name"], "test4");
+  assert_equals(cookieMap.get("test4")["value"], "4");
+  assert_equals(cookieMap.get("test4")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test4")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test4")["secure"], true);
+  assert_equals(cookieMap.get("test4")["httpOnly"], false);
+  assert_equals(cookieMap.get("test4")["expiry"], undefined);
+  assert_equals(cookieMap.get("test4")["sameSite"], undefined);
+
+  // test5 [SameSite Strict]
+  assert_equals(cookieMap.get("test5")["name"], "test5");
+  assert_equals(cookieMap.get("test5")["value"], "5");
+  assert_equals(cookieMap.get("test5")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test5")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test5")["secure"], false);
+  assert_equals(cookieMap.get("test5")["httpOnly"], false);
+  assert_equals(cookieMap.get("test5")["expiry"], undefined);
+  assert_equals(cookieMap.get("test5")["sameSite"], "Strict");
+
+  // test6 [SameSite None]
+  assert_equals(cookieMap.get("test6")["name"], "test6");
+  assert_equals(cookieMap.get("test6")["value"], "6");
+  assert_equals(cookieMap.get("test6")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test6")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test6")["secure"], true);
+  assert_equals(cookieMap.get("test6")["httpOnly"], false);
+  assert_equals(cookieMap.get("test6")["expiry"], undefined);
+  assert_equals(cookieMap.get("test6")["sameSite"], "None");
+
+  // test7 [SameSite Lax]
+  assert_equals(cookieMap.get("test7")["name"], "test7");
+  assert_equals(cookieMap.get("test7")["value"], "7");
+  assert_equals(cookieMap.get("test7")["path"], "/infrastructure/testdriver");
+  assert_equals(cookieMap.get("test7")["domain"], "{{host}}");
+  assert_equals(cookieMap.get("test7")["secure"], false);
+  assert_equals(cookieMap.get("test7")["httpOnly"], false);
+  assert_equals(cookieMap.get("test7")["expiry"], undefined);
+  assert_equals(cookieMap.get("test7")["sameSite"], "Lax");
+}, "Get all HTTPS cookies");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.https.html.headers b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.https.html.headers
new file mode 100644
index 0000000..3dc39a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_all_cookies.sub.https.html.headers
@@ -0,0 +1 @@
+Set-Cookie: test3=3; HttpOnly
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.html
new file mode 100644
index 0000000..2a6fb57
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>TestDriver get_named_cookie method HTTP</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+promise_test(async t => {
+  const kTenDaysFromNow = new Date(Date.now() + 10 * 24 * 60 * 60 * 1000);
+  document.cookie = "test0=0";
+  document.cookie = `test1=1; Expires=${kTenDaysFromNow.toUTCString()}`;
+  document.cookie = "test2=2; Path=/";
+  // document.cookie = "test3=3; HttpOnly"; This is set in the headers file.
+  document.cookie = "test4=4; Secure";
+  document.cookie = "test5=5; SameSite=Strict";
+  document.cookie = "test6=6; SameSite=None; Secure";
+  document.cookie = "test7=7; SameSite=Lax";
+
+  // test0
+  let cookie = await test_driver.get_named_cookie("test0");
+  assert_equals(cookie["name"], "test0");
+  assert_equals(cookie["value"], "0");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test1 [Expires in 10 days]
+  cookie = await test_driver.get_named_cookie("test1");
+  assert_equals(cookie["name"], "test1");
+  assert_equals(cookie["value"], "1");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], Math.floor(kTenDaysFromNow.getTime()/1000));
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test2 [Path /]
+  cookie = await test_driver.get_named_cookie("test2");
+  assert_equals(cookie["name"], "test2");
+  assert_equals(cookie["value"], "2");
+  assert_equals(cookie["path"], "/");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test3 [HttpOnly]
+  cookie = await test_driver.get_named_cookie("test3");
+  assert_equals(cookie["name"], "test3");
+  assert_equals(cookie["value"], "3");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], true);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test4 [Secure] Omitted
+  cookie = await test_driver.get_named_cookie("test4");
+  assert_equals(cookie, null);
+
+  // test5 [SameSite Strict]
+  cookie = await test_driver.get_named_cookie("test5");
+  assert_equals(cookie["name"], "test5");
+  assert_equals(cookie["value"], "5");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], "Strict");
+
+  // test6 [SameSite None] Omitted
+  cookie = await test_driver.get_named_cookie("test6");
+  assert_equals(cookie, null);
+
+  // test7 [SameSite Strict]
+  cookie = await test_driver.get_named_cookie("test7");
+  assert_equals(cookie["name"], "test7");
+  assert_equals(cookie["value"], "7");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], "Lax");
+}, "Get Named HTTP cookie");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.html.headers b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.html.headers
new file mode 100644
index 0000000..3dc39a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.html.headers
@@ -0,0 +1 @@
+Set-Cookie: test3=3; HttpOnly
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.https.html b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.https.html
new file mode 100644
index 0000000..0a995901
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.https.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>TestDriver get_named_cookie method HTTPS</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+promise_test(async t => {
+  const kTenDaysFromNow = new Date(Date.now() + 10 * 24 * 60 * 60 * 1000);
+  document.cookie = "test0=0";
+  document.cookie = `test1=1; Expires=${kTenDaysFromNow.toUTCString()}`;
+  document.cookie = "test2=2; Path=/";
+  // document.cookie = "test3=3; HttpOnly"; This is set in the headers file.
+  document.cookie = "test4=4; Secure";
+  document.cookie = "test5=5; SameSite=Strict";
+  document.cookie = "test6=6; SameSite=None; Secure";
+  document.cookie = "test7=7; SameSite=Lax";
+
+  // test0
+  let cookie = await test_driver.get_named_cookie("test0");
+  assert_equals(cookie["name"], "test0");
+  assert_equals(cookie["value"], "0");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test1 [Expires in 10 days]
+  cookie = await test_driver.get_named_cookie("test1");
+  assert_equals(cookie["name"], "test1");
+  assert_equals(cookie["value"], "1");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], Math.floor(kTenDaysFromNow.getTime()/1000));
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test2 [Path /]
+  cookie = await test_driver.get_named_cookie("test2");
+  assert_equals(cookie["name"], "test2");
+  assert_equals(cookie["value"], "2");
+  assert_equals(cookie["path"], "/");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test3 [HttpOnly]
+  cookie = await test_driver.get_named_cookie("test3");
+  assert_equals(cookie["name"], "test3");
+  assert_equals(cookie["value"], "3");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], true);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test4 [Secure]
+  cookie = await test_driver.get_named_cookie("test4");
+  assert_equals(cookie["name"], "test4");
+  assert_equals(cookie["value"], "4");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], true);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], undefined);
+
+  // test5 [SameSite Strict]
+  cookie = await test_driver.get_named_cookie("test5");
+  assert_equals(cookie["name"], "test5");
+  assert_equals(cookie["value"], "5");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], "Strict");
+
+  // test6 [SameSite None]
+  cookie = await test_driver.get_named_cookie("test6");
+  assert_equals(cookie["name"], "test6");
+  assert_equals(cookie["value"], "6");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], true);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], "None");
+
+  // test7 [SameSite Strict]
+  cookie = await test_driver.get_named_cookie("test7");
+  assert_equals(cookie["name"], "test7");
+  assert_equals(cookie["value"], "7");
+  assert_equals(cookie["path"], "/infrastructure/testdriver");
+  assert_equals(cookie["domain"], "{{host}}");
+  assert_equals(cookie["secure"], false);
+  assert_equals(cookie["httpOnly"], false);
+  assert_equals(cookie["expiry"], undefined);
+  assert_equals(cookie["sameSite"], "Lax");
+}, "Get Named HTTPS cookie");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.https.html.headers b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.https.html.headers
new file mode 100644
index 0000000..3dc39a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/testdriver/get_named_cookie.sub.https.html.headers
@@ -0,0 +1 @@
+Set-Cookie: test3=3; HttpOnly
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-pressure-service.js b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-pressure-service.js
index b6b1f2715..27a684e1 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-pressure-service.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-pressure-service.js
@@ -19,11 +19,6 @@
       ['power-supply', PressureFactor.kPowerSupply]
     ]);
     this.pressureServiceReadingTimerId_ = null;
-    // Sets a timestamp by creating a DOMHighResTimeStamp from a given
-    // platform timestamp. In this mock implementation we use a starting value
-    // and an increment step value that resemble a platform timestamp
-    // reasonably enough.
-    this.timestamp_ = window.performance.timeOrigin;
   }
 
   start() {
@@ -64,11 +59,28 @@
     if (this.pressureServiceReadingTimerId_ != null)
       stopPlatformCollector();
 
+    // The following code for calculating the timestamp was taken from
+    // https://source.chromium.org/chromium/chromium/src/+/main:third_party/
+    // blink/web_tests/http/tests/resources/
+    // geolocation-mock.js;l=131;drc=37a9b6c03b9bda9fcd62fc0e5e8016c278abd31f
+
+    // The new Date().getTime() returns the number of milliseconds since the
+    // UNIX epoch (1970-01-01 00::00:00 UTC), while |internalValue| of the
+    // device.mojom.PressureUpdate represents the value of microseconds since
+    // the Windows FILETIME epoch (1601-01-01 00:00:00 UTC). So add the delta
+    // when sets the |internalValue|. See more info in //base/time/time.h.
+    const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
+    const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
+    // |epochDeltaInMs| equals to base::Time::kTimeTToMicrosecondsOffset.
+    const epochDeltaInMs = unixEpoch - windowsEpoch;
+
     const timeout = (1 / sampleRate) * 1000;
     this.pressureServiceReadingTimerId_ = window.setInterval(() => {
       if (this.pressureUpdate_ === null || this.observer_ === null)
         return;
-      this.pressureUpdate_.timestamp = this.timestamp_++;
+      this.pressureUpdate_.timestamp = {
+        internalValue: BigInt((new Date().getTime() + epochDeltaInMs) * 1000)
+      };
       this.observer_.onUpdate(this.pressureUpdate_);
       this.updatesDelivered_++;
     }, timeout);
@@ -102,7 +114,6 @@
     this.pressureUpdate_ = {
       state: this.mojomStateType_.get(state),
       factors: pressureFactors,
-      timestamp: window.performance.timeOrigin
     };
   }
 }
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver.js b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
index 0737e64..5a59c724 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
@@ -185,6 +185,38 @@
         },
 
         /**
+         * Get details for all cookies in the current context.
+         * See https://w3c.github.io/webdriver/#get-all-cookies
+         *
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} Returns an array of cookies objects as defined in the spec:
+         *                    https://w3c.github.io/webdriver/#cookies
+         */
+         get_all_cookies: function(context=null) {
+            return window.test_driver_internal.get_all_cookies(context);
+        },
+
+        /**
+         * Get details for a cookie in the current context by name if it exists.
+         * See https://w3c.github.io/webdriver/#get-named-cookie
+         *
+         * @param {String} name - The name of the cookie to get.
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} Returns null if no such cookie exists or
+         *                    the matching cookie object as defined in the spec:
+         *                    https://w3c.github.io/webdriver/#cookies
+         */
+         get_named_cookie: function(name, context=null) {
+            return window.test_driver_internal.get_named_cookie(name, context);
+        },
+
+        /**
          * Send keys to an element.
          *
          * If ``element`` isn't inside the
@@ -637,6 +669,14 @@
             return Promise.reject(new Error("unimplemented"));
         },
 
+        get_all_cookies: function(context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
+        delete_named_cookie: function(name, context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
         send_keys: function(element, keys) {
             if (this.in_automation) {
                 return Promise.reject(new Error('Not implemented'));
diff --git a/third_party/blink/web_tests/resources/testdriver-vendor.js b/third_party/blink/web_tests/resources/testdriver-vendor.js
index e66c5ad..5530677 100644
--- a/third_party/blink/web_tests/resources/testdriver-vendor.js
+++ b/third_party/blink/web_tests/resources/testdriver-vendor.js
@@ -445,6 +445,14 @@
     return internals.deleteAllCookies();
   }
 
+  window.test_driver_internal.get_all_cookies = function() {
+    return internals.getAllCookies();
+  }
+
+  window.test_driver_internal.get_named_cookie = function(name) {
+    return internals.getNamedCookie(name);
+  }
+
   window.test_driver_internal.minimize_window = async () => {
     window.testRunner.setMainWindowHidden(true);
     // Wait until the new state is reflected in the document
diff --git a/third_party/blink/web_tests/resources/testdriver.js b/third_party/blink/web_tests/resources/testdriver.js
index 0737e64..6aef79b 100644
--- a/third_party/blink/web_tests/resources/testdriver.js
+++ b/third_party/blink/web_tests/resources/testdriver.js
@@ -185,6 +185,38 @@
         },
 
         /**
+         * Get details for all cookies in the current context.
+         * See https://w3c.github.io/webdriver/#get-all-cookies
+         *
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} Returns an array of cookies objects as defined in the spec:
+         *                    https://w3c.github.io/webdriver/#cookies
+         */
+         get_all_cookies: function(context=null) {
+            return window.test_driver_internal.get_all_cookies(context);
+        },
+
+        /**
+         * Get details for a cookie in the current context by name if it exists.
+         * See https://w3c.github.io/webdriver/#get-named-cookie
+         *
+         * @param {String} name - The name of the cookie to get.
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} Returns null if no such cookie exists or
+         *                    the matching cookie object as defined in the spec:
+         *                    https://w3c.github.io/webdriver/#cookies
+         */
+         get_named_cookie: function(name, context=null) {
+            return window.test_driver_internal.get_named_cookie(name, context);
+        },
+
+        /**
          * Send keys to an element.
          *
          * If ``element`` isn't inside the
@@ -637,6 +669,14 @@
             return Promise.reject(new Error("unimplemented"));
         },
 
+        get_all_cookies: function(context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
+        get_named_cookie: function(name, context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
         send_keys: function(element, keys) {
             if (this.in_automation) {
                 return Promise.reject(new Error('Not implemented'));
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index d2741b8..507e8ed2 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-12-1-99-g8faf57dd1
-Revision: 8faf57dd17088c37fa947fd565870648bbdbad18
+Version: VER-2-12-1-101-g0417527d5
+Revision: 0417527d5b5abc3ee9426f31bd95209ca97502a5
 CPEPrefix: cpe:/a:freetype:freetype:2.11.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 16d62b6..ebda0d38 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
 # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
-CLANG_REVISION = 'llvmorg-16-init-6084-g2f3d7c2c'
+CLANG_REVISION = 'llvmorg-16-init-6457-g20a269cf'
 CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index c49e28c3..3c816de 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -2474,11 +2474,11 @@
     ],
 
     'dawn_tests_android_release_trybot': [
-      'dawn_tests', 'android', 'release_trybot_minimal_symbols',
+      'dawn_no_gl', 'android', 'release_trybot_minimal_symbols',
     ],
 
     'dawn_tests_android_release_trybot_reclient': [
-      'dawn_tests', 'android', 'release_trybot_minimal_symbols_reclient',
+      'dawn_no_gl', 'android', 'release_trybot_minimal_symbols_reclient',
     ],
 
     'dawn_tests_asan_release_bot_dcheck_always_on_reclient': [
@@ -4117,6 +4117,10 @@
       'gn_args': 'dawn_enable_desktop_gl=true',
     },
 
+    'dawn_no_gl': {
+      'gn_args': 'use_dawn=true',
+    },
+
     'dawn_tests': {
       'gn_args': 'use_dawn=true dawn_enable_opengles=true',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.dawn.json b/tools/mb/mb_config_expectations/chromium.dawn.json
index 953a0fc..036b446 100644
--- a/tools/mb/mb_config_expectations/chromium.dawn.json
+++ b/tools/mb/mb_config_expectations/chromium.dawn.json
@@ -1,7 +1,6 @@
 {
   "Dawn Android arm DEPS Release (Pixel 4)": {
     "gn_args": {
-      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "debuggable_apks": false,
       "ffmpeg_branding": "Chrome",
@@ -16,7 +15,6 @@
   },
   "Dawn Android arm Release (Pixel 4)": {
     "gn_args": {
-      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "debuggable_apks": false,
       "ffmpeg_branding": "Chrome",
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json b/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
index b5cdacd3..4dc12ed 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.dawn.json
@@ -1,7 +1,6 @@
 {
   "android-dawn-arm-rel": {
     "gn_args": {
-      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "debuggable_apks": false,
       "ffmpeg_branding": "Chrome",
@@ -16,7 +15,6 @@
   },
   "dawn-android-arm-deps-rel": {
     "gn_args": {
-      "dawn_enable_opengles": true,
       "dcheck_always_on": true,
       "debuggable_apks": false,
       "ffmpeg_branding": "Chrome",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f21f536c..5eb6cf14 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -35687,6 +35687,7 @@
       label="PASSWORDSPRIVATE_SWITCHBIOMETRICAUTHBEFOREFILLINGSTATE"/>
   <int value="1718" label="WMDESKSPRIVATE_GETACTIVEDESK"/>
   <int value="1719" label="WMDESKSPRIVATE_SWITCHDESK"/>
+  <int value="1720" label="OS_TELEMETRY_GETTPMINFO"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -80400,6 +80401,7 @@
   <int value="40" label="kActivatedBeforeStarted"/>
   <int value="41" label="kInactivePageRestriction"/>
   <int value="42" label="kStartFailed"/>
+  <int value="43" label="kTimeoutBackgrounded"/>
 </enum>
 
 <enum name="PrerenderHoverEvent">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index ad0cb8f..eabd7d7 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1167,7 +1167,7 @@
 </histogram>
 
 <histogram name="Android.DirectAction.Perform" enum="DirectActionId"
-    expires_after="2022-10-10">
+    expires_after="2023-10-10">
   <owner>arbesser@google.com</owner>
   <owner>autofill_assistant@google.com</owner>
   <summary>
@@ -1443,7 +1443,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.FromWebContent.Duration{DropResult}"
-    units="ms" expires_after="2022-11-13">
+    units="ms" expires_after="2023-03-19">
   <owner>wenyufu@chromium.org</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1475,7 +1475,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.AllExpired" units="ms"
-    expires_after="2022-11-13">
+    expires_after="2023-03-19">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1495,7 +1495,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.FirstAttempt" units="ms"
-    expires_after="2023-02-19">
+    expires_after="2023-03-19">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1509,7 +1509,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.FirstExpired" units="ms"
-    expires_after="2022-11-13">
+    expires_after="2023-03-19">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1528,7 +1528,7 @@
 </histogram>
 
 <histogram name="Android.DragDrop.Image.OpenFileTime.LastAttempt" units="ms"
-    expires_after="2023-02-19">
+    expires_after="2023-03-19">
   <owner>shuyng@google.com</owner>
   <owner>clank-large-form-factors@google.com</owner>
   <summary>
@@ -1893,7 +1893,7 @@
 </histogram>
 
 <histogram name="Android.InstantApps.ApiCallDurationWithoutApp" units="ms"
-    expires_after="2023-01-31">
+    expires_after="2023-04-02">
   <owner>sbirch@google.com</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -2862,7 +2862,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionView.Reused" enum="BooleanReused"
-    expires_after="2023-01-31">
+    expires_after="2023-04-02">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2937,7 +2937,7 @@
 </histogram>
 
 <histogram name="Android.OmniboxFocusReason" enum="OmniboxFocusReason"
-    expires_after="2023-01-31">
+    expires_after="2023-04-02">
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <owner>amaralp@chromium.org</owner>
@@ -3906,7 +3906,7 @@
 </histogram>
 
 <histogram name="Android.WebView.ComponentUpdater.GetFilesDuration" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hazems@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3939,7 +3939,7 @@
 </histogram>
 
 <histogram name="Android.WebView.ComponentUpdater.UnexpectedExit"
-    enum="Boolean" expires_after="2023-02-01">
+    enum="Boolean" expires_after="2023-04-02">
   <owner>hazems@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3950,7 +3950,7 @@
 </histogram>
 
 <histogram name="Android.WebView.ComponentUpdater.UpdateJobDuration" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hazems@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 9f3c4c0..84eb841 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -1647,7 +1647,7 @@
 </histogram>
 
 <histogram name="Apps.AppListSearchResultOpenTypeV2" enum="AppListSearchResult"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
 <!-- Name completed by histogram_suffixes name="TabletOrClamshellMode" -->
 
   <owner>tbarzic@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index a1df29ec..bddd54d 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1183,7 +1183,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Intents.LinkCapturingEvent2"
-    enum="LinkCapturingEvent" expires_after="2023-03-26">
+    enum="LinkCapturingEvent" expires_after="2023-04-02">
   <owner>vpao@google.com</owner>
   <owner>chromeos-apps-foundation-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index 851a73c..565aadb 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -354,7 +354,7 @@
 </histogram>
 
 <histogram name="Compositing.Display.OverlayCombinationCache.NumIdsEvicted"
-    units="units" expires_after="2023-01-29">
+    units="units" expires_after="2023-04-02">
   <owner>khaslett@chromium.org</owner>
   <owner>kylechar@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cros_ml/histograms.xml b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
index c99fdd42..c0f2977 100644
--- a/tools/metrics/histograms/metadata/cros_ml/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
@@ -34,7 +34,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.CpuUsageMilliPercent"
-    units="1/1000ths of %" expires_after="2023-01-29">
+    units="1/1000ths of %" expires_after="2023-04-02">
   <owner>alanlxl@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cross_device/histograms.xml b/tools/metrics/histograms/metadata/cross_device/histograms.xml
index 4e936a4..23a3ac98 100644
--- a/tools/metrics/histograms/metadata/cross_device/histograms.xml
+++ b/tools/metrics/histograms/metadata/cross_device/histograms.xml
@@ -263,7 +263,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.DeviceActivityGetter.ApiCallResult.GetDevicesActivityStatus"
-    enum="CryptAuthApiCallResult" expires_after="2023-02-01">
+    enum="CryptAuthApiCallResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -311,7 +311,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.DeviceSyncer.AsyncTaskResult.DeviceMetadataDecryption"
-    enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01">
+    enum="CryptAuthAsyncTaskResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -326,7 +326,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.DeviceSyncer.AsyncTaskResult.GroupPrivateKeyDecryption"
-    enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01">
+    enum="CryptAuthAsyncTaskResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -385,7 +385,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataDecryptionSuccess"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -496,7 +496,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.FeatureStatusSetter.ApiCallResult.SetFeatureStatuses"
-    enum="CryptAuthApiCallResult" expires_after="2023-02-01">
+    enum="CryptAuthApiCallResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -520,7 +520,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ApiCallResult.ShareGroupPrivateKey"
-    enum="CryptAuthApiCallResult" expires_after="2023-02-01">
+    enum="CryptAuthApiCallResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -547,7 +547,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.EncryptionSuccess"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -608,7 +608,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.MetadataSyncer.ApiCallResult.FirstSyncMetadata"
-    enum="CryptAuthApiCallResult" expires_after="2023-02-01">
+    enum="CryptAuthApiCallResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -620,7 +620,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.MetadataSyncer.ApiCallResult.SecondSyncMetadata"
-    enum="CryptAuthApiCallResult" expires_after="2023-02-01">
+    enum="CryptAuthApiCallResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -632,7 +632,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.MetadataSyncer.AsyncTaskResult.GroupKeyCreation"
-    enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01">
+    enum="CryptAuthAsyncTaskResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -644,7 +644,7 @@
 
 <histogram
     name="CryptAuth.DeviceSyncV2.MetadataSyncer.AsyncTaskResult.LocalDeviceMetadataEncryption"
-    enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01">
+    enum="CryptAuthAsyncTaskResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -809,7 +809,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.Result.ResultCode"
-    enum="CryptAuthV2DeviceSyncResultCode" expires_after="2023-02-01">
+    enum="CryptAuthV2DeviceSyncResultCode" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -860,7 +860,7 @@
 </histogram>
 
 <histogram name="CryptAuth.EnrollmentV2.AsyncTaskResult.KeyCreation"
-    enum="CryptAuthAsyncTaskResult" expires_after="2023-02-01">
+    enum="CryptAuthAsyncTaskResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -914,7 +914,7 @@
 </histogram>
 
 <histogram name="CryptAuth.EnrollmentV2.Result.ResultCode"
-    enum="CryptAuthV2EnrollmentResult" expires_after="2023-02-01">
+    enum="CryptAuthV2EnrollmentResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1437,7 +1437,7 @@
 </histogram>
 
 <histogram name="InstantTethering.KeepAliveTickle.Result" enum="BooleanSuccess"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1567,7 +1567,7 @@
 </histogram>
 
 <histogram name="MultiDevice.BetterTogetherSuite.MultiDeviceFeatureState"
-    enum="MultiDevice_FeatureState" expires_after="2023-02-01">
+    enum="MultiDevice_FeatureState" expires_after="2023-04-02">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1619,7 +1619,7 @@
 
 <histogram name="MultiDevice.DeviceSyncService.ForceSyncNow.Result"
     enum="MultiDevice_DeviceSyncService_ForceCryptAuthOperationResult"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>Result for when ForceSyncNow is called.</summary>
@@ -1735,7 +1735,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.StartScanToAuthenticationDuration.Background"
-    units="ms" expires_after="2023-02-01">
+    units="ms" expires_after="2023-04-02">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1768,7 +1768,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.StartScanToReceiveAdvertisementDuration.Background"
-    units="ms" expires_after="2023-02-01">
+    units="ms" expires_after="2023-04-02">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1784,7 +1784,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.ReceiveAdvertisementToAuthentication.EffectiveSuccessRateWithRetries"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1807,7 +1807,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.ConnectionMedium"
-    enum="SecureChannelNearbyConnectionMedium" expires_after="2023-02-01">
+    enum="SecureChannelNearbyConnectionMedium" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1822,7 +1822,7 @@
 
 <histogram name="MultiDevice.SecureChannel.Nearby.ConnectionResult"
     enum="MultiDeviceNearbyConnectionsInitiatorResult"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1850,7 +1850,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.EffectiveConnectionResult"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1888,7 +1888,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.MessageAction"
-    enum="MultiDeviceNearbyMessageAction" expires_after="2023-02-01">
+    enum="MultiDeviceNearbyMessageAction" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1958,7 +1958,7 @@
 </histogram>
 
 <histogram name="MultiDevice.Setup.HostStatus"
-    enum="MultiDevice_Setup_HostStatus" expires_after="2023-02-01">
+    enum="MultiDevice_Setup_HostStatus" expires_after="2023-04-02">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2177,7 +2177,7 @@
 </histogram>
 
 <histogram name="SmartLock.AuthResult.Unlock" enum="BooleanSuccess"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2234,7 +2234,7 @@
 </histogram>
 
 <histogram name="SmartLock.FindAndConnectToHostResult.Unlock"
-    enum="SmartLockFindAndConnectToHostResult" expires_after="2023-02-01">
+    enum="SmartLockFindAndConnectToHostResult" expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2282,7 +2282,7 @@
 </histogram>
 
 <histogram name="SmartLock.GetRemoteStatus.Unlock" enum="BooleanSuccess"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2338,7 +2338,7 @@
 
 <histogram
     name="SmartLock.Performance.ShowLockScreenToShowFirstStatusToUserDuration.Unlock"
-    units="ms" expires_after="2023-02-01">
+    units="ms" expires_after="2023-04-02">
 <!-- Name completed by histogram_suffixes name="SmartLockStatusTypes" -->
 
   <owner>hansberry@chromium.org</owner>
@@ -2361,7 +2361,7 @@
 
 <histogram
     name="SmartLock.Performance.StartScanToReceiveFirstRemoteStatusDuration.Unlock"
-    units="ms" expires_after="2023-02-01">
+    units="ms" expires_after="2023-04-02">
 <!-- Name completed by histogram_suffixes name="SmartLockStatusTypes" -->
 
   <owner>hansberry@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index d725140..db5d7fe 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -368,7 +368,7 @@
 </histogram>
 
 <histogram name="Cryptohome.Errors" enum="CryptohomeError"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>apronin@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>Cryptohome errors.</summary>
@@ -417,7 +417,7 @@
 </histogram>
 
 <histogram name="Cryptohome.FreeDiskSpaceTotalTime2" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>vsavu@google.com</owner>
   <owner>dlunev@chromium.org</owner>
   <owner>sarthakkukreti@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 33458fe..efd48bd9 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -110,7 +110,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ClientAppId" enum="ClientAppId"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>yusufo@chromium.org</owner>
   <summary>
     Android: AppId declared by the launching application in EXTRA_APPLICATION_ID
diff --git a/tools/metrics/histograms/metadata/event/histograms.xml b/tools/metrics/histograms/metadata/event/histograms.xml
index 4eb40ca..0d413e0 100644
--- a/tools/metrics/histograms/metadata/event/histograms.xml
+++ b/tools/metrics/histograms/metadata/event/histograms.xml
@@ -990,7 +990,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.JankyDuration" units="ms"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>ddrone@google.com</owner>
   <owner>chrometto-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/gcm/histograms.xml b/tools/metrics/histograms/metadata/gcm/histograms.xml
index 7245dd3..0152c6b 100644
--- a/tools/metrics/histograms/metadata/gcm/histograms.xml
+++ b/tools/metrics/histograms/metadata/gcm/histograms.xml
@@ -306,7 +306,7 @@
 </histogram>
 
 <histogram name="GCM.RegistrationRequestStatus"
-    enum="GCMRegistrationRequestStatus" expires_after="2023-01-29">
+    enum="GCMRegistrationRequestStatus" expires_after="2023-04-02">
   <owner>peter@chromium.org</owner>
   <summary>
     Status code of the outcome of a GCM registration request. The Unknown error
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index b37485a..c1891d1 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -608,7 +608,7 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Browser2.Tiny" units="bytes"
-    expires_after="2023-03-26">
+    expires_after="2023-04-02">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
@@ -1896,7 +1896,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.Compression.SizeKb" units="KB"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>lizeb@chromium.org</owner>
   <owner>thiabaud@google.com</owner>
   <summary>
@@ -1969,7 +1969,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.DiskReadTime.5min" units="ms"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
@@ -2113,7 +2113,7 @@
 </histogram>
 
 <histogram name="Memory.ParkableString.TotalSizeKb.5min" units="KB"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>lizeb@chromium.org</owner>
   <owner>pasko@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/nearby/histograms.xml b/tools/metrics/histograms/metadata/nearby/histograms.xml
index 9f6af6bc..d03f0a2 100644
--- a/tools/metrics/histograms/metadata/nearby/histograms.xml
+++ b/tools/metrics/histograms/metadata/nearby/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Nearby.Connections.Bluetooth.Adapter.SetName.Result"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -33,7 +33,7 @@
 </histogram>
 
 <histogram name="Nearby.Connections.Bluetooth.Adapter.SetScanMode.Result"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -44,7 +44,7 @@
 
 <histogram
     name="Nearby.Connections.Bluetooth.ClassicMedium.ConnectToService.Duration"
-    units="ms" expires_after="2023-02-01">
+    units="ms" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -67,7 +67,7 @@
 </histogram>
 
 <histogram name="Nearby.Connections.Bluetooth.LEMedium.StartAdvertising.Result"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -76,7 +76,7 @@
 </histogram>
 
 <histogram name="Nearby.Connections.Bluetooth.LEMedium.StartScanning.Result"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -206,7 +206,7 @@
 
 <histogram name="Nearby.Connections.UtilityProcessShutdownReason"
     enum="NearbyConnectionsUtilityProcessShutdownReason"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -393,7 +393,7 @@
 
 <histogram
     name="Nearby.Share.Certificates.Manager.BluetoothMacAddressPresentForPrivateCertificateCreation"
-    enum="BooleanPresent" expires_after="2023-02-01">
+    enum="BooleanPresent" expires_after="2023-04-02">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -456,7 +456,7 @@
 
 <histogram
     name="Nearby.Share.Certificates.Manager.DownloadPublicCertificatesSuccessRate"
-    enum="BooleanSuccess" expires_after="2023-02-01">
+    enum="BooleanSuccess" expires_after="2023-04-02">
   <owner>cclem@chromium.org</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -607,7 +607,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Contacts.DownloadResult" enum="BooleanSuccess"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -621,7 +621,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Contacts.HttpResult" enum="NearbyShareHttpResult"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -822,7 +822,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.LocalDeviceData.DeviceDataUpdater.HttpResult"
-    enum="NearbyShareHttpResult" expires_after="2023-02-01">
+    enum="NearbyShareHttpResult" expires_after="2023-04-02">
   <owner>cclem@chromium.org</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -869,7 +869,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Onboarding.Duration" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -901,7 +901,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Onboarding.Result"
-    enum="NearbyShareOnboardingFinalState" expires_after="2023-02-01">
+    enum="NearbyShareOnboardingFinalState" expires_after="2023-04-02">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 3e4b08f0..b46e1eee8 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -7333,7 +7333,7 @@
 </histogram>
 
 <histogram name="InstanceID.GetToken.RequestStatus"
-    enum="GCMRegistrationRequestStatus" expires_after="2023-01-29">
+    enum="GCMRegistrationRequestStatus" expires_after="2023-04-02">
   <owner>peter@chromium.org</owner>
   <summary>Status code of the outcome of GetToken request.</summary>
 </histogram>
@@ -11617,7 +11617,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.CanCheckUrl" enum="BooleanCanCheckUrl"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -15151,7 +15151,7 @@
 </histogram>
 
 <histogram name="WebFont.DownloadTime.LoadError" units="ms"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>kenjibaheux@chromium.org</owner>
   <owner>ksakamoto@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index 34643848..8a05987 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -282,7 +282,7 @@
 </histogram>
 
 <histogram name="Permissions.CrowdDeny.PreloadData.VersionAtAbuseCheckTime"
-    units="date" expires_after="2023-01-29">
+    units="date" expires_after="2023-04-02">
   <owner>elklm@chromium.org</owner>
   <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
   <summary>
@@ -1046,7 +1046,7 @@
 </histogram>
 
 <histogram name="SiteEngagementService.EngagementType"
-    enum="SiteEngagementServiceEngagementType" expires_after="2023-01-29">
+    enum="SiteEngagementServiceEngagementType" expires_after="2023-04-02">
   <owner>calamity@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
   <summary>
@@ -1208,6 +1208,19 @@
   </summary>
 </histogram>
 
+<histogram name="WebsiteSettings.GetAllSitesLoadTime" units="ms"
+    expires_after="M111">
+  <owner>olesiamarukhno@google.com</owner>
+  <owner>sauski@google.com</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <summary>
+    Records the load time of the &quot;All sites&quot; settings page. The load
+    time is the time between the &quot;GetAllSites&quot; request start and the
+    time when the storage is fetched and the full information is returned to the
+    UI.
+  </summary>
+</histogram>
+
 <histogram name="WebsiteSettings.Menu.PermissionChanged" enum="ContentType"
     expires_after="M81">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/phonehub/histograms.xml b/tools/metrics/histograms/metadata/phonehub/histograms.xml
index 635bd44..9b2d80b 100644
--- a/tools/metrics/histograms/metadata/phonehub/histograms.xml
+++ b/tools/metrics/histograms/metadata/phonehub/histograms.xml
@@ -199,7 +199,7 @@
 </histogram>
 
 <histogram name="PhoneHub.Connection.Duration" units="ms"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -219,7 +219,7 @@
 </histogram>
 
 <histogram name="PhoneHub.Connection.Result" enum="BooleanSuccess"
-    expires_after="2023-02-01">
+    expires_after="2023-04-02">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index 87e0cdb..6e4d015e 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -1555,7 +1555,7 @@
 </histogram>
 
 <histogram name="Platform.Trunks.TpmErrorCode" enum="TPMResponseCode"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>yich@google.com</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index b20fc25..7fe51be3 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -754,7 +754,7 @@
 </histogram>
 
 <histogram name="Power.BatteryDischargeRate" units="mW"
-    expires_after="2023-03-26">
+    expires_after="2023-04-02">
   <owner>puthik@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index c35adbe..db48159 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -1214,7 +1214,10 @@
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
     Records the extension telemetry signal type. Logged each time a signal is
-    triggered (eg. a tabs.executeScript API call is invoked).
+    triggered (eg. a tabs.executeScript API call is invoked). The signal can be
+    a raw signal that is created externally and passed to extension service or a
+    combined signal that is created internally and derived from other raw
+    signals.
   </summary>
 </histogram>
 
@@ -1757,7 +1760,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.IsLookupServiceAvailable"
-    enum="BooleanAvailable" expires_after="2023-01-29">
+    enum="BooleanAvailable" expires_after="2023-04-02">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1769,7 +1772,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.IsLookupSuccessful" enum="BooleanSuccess"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1810,7 +1813,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.LocalMatch.Result"
-    enum="SafeBrowsingAllowlistAsyncMatch" expires_after="2023-01-29">
+    enum="SafeBrowsingAllowlistAsyncMatch" expires_after="2023-04-02">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index a0a13b4..02e6a2fe 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -241,7 +241,7 @@
 </histogram>
 
 <histogram name="Security.PageInfo.AdPersonalizationRowShown" enum="Boolean"
-    expires_after="M109">
+    expires_after="M112">
   <owner>dullweber@chromium.org</owner>
   <owner>sauski@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/startup/histograms.xml b/tools/metrics/histograms/metadata/startup/histograms.xml
index ce71c57..5bc4dbe97 100644
--- a/tools/metrics/histograms/metadata/startup/histograms.xml
+++ b/tools/metrics/histograms/metadata/startup/histograms.xml
@@ -90,7 +90,7 @@
 </histogram>
 
 <histogram name="Startup.Android.Cold.FirstSafeBrowsingResponseTime.Tabbed"
-    units="ms" expires_after="2023-01-29">
+    units="ms" expires_after="2023-04-02">
   <owner>pasko@chromium.org</owner>
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -102,7 +102,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.Cold.TimeToFirstContentfulPaint"
-    units="ms" expires_after="2023-01-29">
+    units="ms" expires_after="2023-04-02">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
@@ -115,7 +115,7 @@
 </histogram>
 
 <histogram base="true" name="Startup.Android.Cold.TimeToFirstNavigationCommit"
-    units="ms" expires_after="2023-01-29">
+    units="ms" expires_after="2023-04-02">
   <owner>pasko@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index de5d851..ab776e4 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -413,7 +413,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.ActivationList"
-    enum="ActivationList" expires_after="2023-01-29">
+    enum="ActivationList" expires_after="2023-04-02">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -423,7 +423,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.ActivationState"
-    enum="SubresourceFilterActivationState" expires_after="2023-01-29">
+    enum="SubresourceFilterActivationState" expires_after="2023-04-02">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 4d66bd8..4df4aee 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -634,7 +634,7 @@
 </histogram>
 
 <histogram name="Sync.Local.ReadPlatformFileError" enum="PlatformFileError"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>igorruvinov@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -990,7 +990,7 @@
 </histogram>
 
 <histogram name="Sync.PasswordNotesStateInUpdate"
-    enum="SyncPasswordNotesStateInUpdate" expires_after="2023-02-01">
+    enum="SyncPasswordNotesStateInUpdate" expires_after="2023-04-02">
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1046,7 +1046,7 @@
 </histogram>
 
 <histogram name="Sync.PostedDataTypeCommitRequest" enum="SyncModelTypes"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <component>Services&gt;Sync</component>
@@ -1060,7 +1060,7 @@
 </histogram>
 
 <histogram name="Sync.PostedDataTypeGetUpdatesRequest" enum="SyncModelTypes"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <component>Services&gt;Sync</component>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index a2dbe4d..b314cd7 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -457,7 +457,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.TimeToFirstUsableFrameAfterStartCapture"
-    units="ms" expires_after="2023-01-29">
+    units="ms" expires_after="2023-04-02">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index dbc4979..6d74583ce 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -2266,7 +2266,7 @@
 </histogram>
 
 <histogram name="V8.WasmTierUpModuleMicroSeconds" units="microseconds"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index 3373f59..b5e237c 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -76,7 +76,7 @@
 </histogram>
 
 <histogram name="WebApk.Install.GooglePlayInstallResult"
-    enum="WebApkGooglePlayInstallResult" expires_after="2023-01-29">
+    enum="WebApkGooglePlayInstallResult" expires_after="2023-04-02">
   <owner>hartmanng@chromium.org</owner>
   <owner>
     src/chrome/android/java/src/org/chromium/chrome/browser/webapps/OWNERS
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index 468e0cf..2e8f5ee 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -534,7 +534,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.DelayedPacketOutageEventMs" units="ms"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>hlundin@chromium.org</owner>
   <summary>
     Measures the duration of each packet loss concealment (a.k.a. expand) event
@@ -799,7 +799,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.ExpandRatePercent" units="%"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>hlundin@chromium.org</owner>
   <summary>
     Measures the expand rate for an incoming WebRTC audio stream. The expand
@@ -1561,6 +1561,10 @@
 
 <histogram name="WebRTC.PeerConnection.KeyProtocol"
     enum="PeerConnectionKeyProtocol" expires_after="2022-10-30">
+  <obsolete>
+    The SDES protocol is no longer available in Chrome as of M98. DTLS is the
+    only possible value.
+  </obsolete>
   <owner>hta@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1965,7 +1969,7 @@
 </histogram>
 
 <histogram name="WebRTC.ReceivedAudioTrackDuration" units="ms"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>perkj@chromium.org</owner>
   <summary>
     Durations of audio tracks received over a PeerConnection. The stopwatch
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 5f7f2e2..6dd43e4 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -38,7 +38,7 @@
 </histogram>
 
 <histogram name="AppBanners.DismissEvent" enum="AppBannersDismissEvent"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>pjmclachlan@google.com</owner>
   <owner>pcovell@google.com</owner>
   <summary>
@@ -50,7 +50,7 @@
 </histogram>
 
 <histogram name="AppBanners.DisplayEvent" enum="AppBannersDisplayEvent"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>pjmclachlan@google.com</owner>
   <owner>pcovell@google.com</owner>
   <summary>
@@ -975,7 +975,7 @@
 </histogram>
 
 <histogram name="Webapp.SystemApps.FreshInstallDuration" units="ms"
-    expires_after="2023-01-29">
+    expires_after="2023-04-02">
   <owner>ortuno@chromium.org</owner>
   <owner>qjw@chromium.org</owner>
   <summary>
@@ -1078,7 +1078,7 @@
 </histogram>
 
 <histogram name="Webapp.WebAppUrlLoaderPrepareForLoadResult"
-    enum="WebAppUrlLoaderResult" expires_after="2023-01-29">
+    enum="WebAppUrlLoaderResult" expires_after="2023-04-02">
   <owner>qjw@chromium.org</owner>
   <owner>ortuno@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py
index 7628455..b7f75a89 100644
--- a/tools/perf/benchmarks/benchmark_smoke_unittest.py
+++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -119,6 +119,8 @@
     'v8.runtime_stats.top_25',  # Fails in Windows, crbug.com/1043048
     'wasmpspdfkit',  # Fails in Chrome OS, crbug.com/1191938
     'memory.desktop' if sys.platform == 'darwin' else None,  # crbug.com/1277277
+    'desktop_ui' if sys.platform == 'darwin' else None,  # crbug.com/1370958
+    'power.desktop' if sys.platform == 'darwin' else None,  # crbug.com/1370958
 ]
 
 
diff --git a/tools/perf/core/benchmark_runner_test.py b/tools/perf/core/benchmark_runner_test.py
index efb7c85..412e91e 100644
--- a/tools/perf/core/benchmark_runner_test.py
+++ b/tools/perf/core/benchmark_runner_test.py
@@ -100,7 +100,8 @@
 
   @decorators.Disabled(
       'chromeos',  # TODO(https://crbug.com/1098412): Fix the test.
-      'android-nougat')  # Flaky: https://crbug.com/1342706
+      'android-nougat',  # Flaky: https://crbug.com/1342706
+      'mac')  # Failing: https://crbug.com/1370958
   def testTimelineBasedEndToEnd(self):
     class TestTimelineBasedBenchmark(perf_benchmark.PerfBenchmark):
       """A dummy benchmark that records a trace and runs sampleMetric on it."""
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 224283c..04e0f87 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,16 +5,16 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "61d0dbe93ee69ed61e5b397ecb71680e7bccc7d0",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6b54769fde8e593a5ad7142596935d8198b511f1/trace_processor_shell.exe"
+            "hash": "91a42a523f0546aec9aca55dd0d8d5160b6c66c9",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/73436050f398f0197bed1279f92ae87a6b7ec8ca/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "482fe6e201c0309c90dd65cccc479b8b31658b20",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/6b54769fde8e593a5ad7142596935d8198b511f1/trace_processor_shell"
+            "hash": "52d287934ab7750cc91b878dcb9eac92642d4b57",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/cfd7aed8ea3518c78eb91ef185961be0ab784447/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
diff --git a/ui/gfx/color_conversions.cc b/ui/gfx/color_conversions.cc
index 4f96c2e..409f366 100644
--- a/ui/gfx/color_conversions.cc
+++ b/ui/gfx/color_conversions.cc
@@ -9,6 +9,7 @@
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/modules/skcms/skcms.h"
 #include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/angle_conversions.h"
 
 namespace gfx {
 
@@ -126,8 +127,12 @@
   if (!h.has_value())
     return std::make_tuple(l, 0, 0);
 
-  return std::make_tuple(l, c * std::cos(h.value() * 3.141592f / 180.0f),
-                         c * std::sin(h.value() * 3.141592f / 180.0f));
+  return std::make_tuple(l, c * std::cos(gfx::DegToRad(h.value())),
+                         c * std::sin(gfx::DegToRad(h.value())));
+}
+std::tuple<float, float, float> LabToLCH(float l, float a, float b) {
+  return std::make_tuple(l, std::sqrt(a * a + b * b),
+                         gfx::RadToDeg(std::atan2f(b, a)));
 }
 
 std::tuple<float, float, float> LabToXYZD50(float l, float a, float b) {
diff --git a/ui/gfx/color_conversions.h b/ui/gfx/color_conversions.h
index 99c538d3..8e88f2f 100644
--- a/ui/gfx/color_conversions.h
+++ b/ui/gfx/color_conversions.h
@@ -64,6 +64,9 @@
                                                     absl::optional<float> h);
 
 // Method exposed for testing purposes.
+GFX_EXPORT std::tuple<float, float, float> LabToLCH(float l, float a, float b);
+
+// Method exposed for testing purposes.
 GFX_EXPORT std::tuple<float, float, float> OKLchToLab(float l,
                                                       float c,
                                                       absl::optional<float> h);
diff --git a/ui/gfx/color_conversions_unittest.cc b/ui/gfx/color_conversions_unittest.cc
index 7d5c6548..07f751e7 100644
--- a/ui/gfx/color_conversions_unittest.cc
+++ b/ui/gfx/color_conversions_unittest.cc
@@ -171,47 +171,83 @@
         48.586294664234586f}}};  // green
 
   for (auto& color_pair : colors_tests) {
-    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [input_l, input_c, input_h] = color_pair.input;
     auto [expected_l, expected_a, expected_b] = color_pair.expected;
     auto [output_l, output_a, output_b] =
-        LchToLab(input_l, input_a, absl::optional<float>(input_b));
+        LchToLab(input_l, input_c, absl::optional<float>(input_h));
     EXPECT_NEAR(output_l, expected_l, 0.001f)
-        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_l
         << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
         << ' ' << output_a << ' ' << output_b;
     EXPECT_NEAR(output_a, expected_a, 0.001f)
-        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_l
         << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
         << ' ' << output_a << ' ' << output_b;
     EXPECT_NEAR(output_b, expected_b, 0.001f)
-        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << input_l << ' ' << input_c << ' ' << input_h << " to " << expected_l
         << ' ' << expected_a << ' ' << expected_b << " produced " << output_l
         << ' ' << output_a << ' ' << output_b;
   }
 
   // Try with a none hue value (white).
   float input_l = 100.0f;
-  float input_a = 0.000010331815288315629f;
+  float input_c = 0.000010331815288315629f;
   absl::optional<float> input_h = absl::nullopt;
   float expected_l = 100.0f;
   float expected_a = -0.000007807961277528364f;
   float expected_b = 0.000006766250648659877f;
   auto [output_l, output_a, output_b] =
-      LchToLab(input_l, input_a, absl::optional<float>(input_h));
+      LchToLab(input_l, input_c, absl::optional<float>(input_h));
   EXPECT_NEAR(output_l, expected_l, 0.001f)
-      << input_l << ' ' << input_a << ' ' << "none"
+      << input_l << ' ' << input_c << ' ' << "none"
       << " to " << expected_l << ' ' << expected_a << ' ' << expected_b
       << " produced " << output_l << ' ' << output_a << ' ' << output_b;
   EXPECT_NEAR(output_a, expected_a, 0.001f)
-      << input_l << ' ' << input_a << ' ' << "none"
+      << input_l << ' ' << input_c << ' ' << "none"
       << " to " << expected_l << ' ' << expected_a << ' ' << expected_b
       << " produced " << output_l << ' ' << output_a << ' ' << output_b;
   EXPECT_NEAR(output_b, expected_b, 0.001f)
-      << input_l << ' ' << input_a << ' ' << "none"
+      << input_l << ' ' << input_c << ' ' << "none"
       << " to " << expected_l << ' ' << expected_a << ' ' << expected_b
       << " produced " << output_l << ' ' << output_a << ' ' << output_b;
 }
 
+TEST(ColorConversions, LabToLCH) {
+  // Color conversions obtained from
+  // https://colorjs.io/apps/convert/?color=purple&precision=4
+  ColorTest colors_tests[] = {
+      {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}},  // black
+      {{100.0f, 0.0f, 0.0f}, {100.0f, 0.0f, 0.0f}},
+      {{89.11f, -65.472265155436f, 21.906713478207564f},
+       {89.11f, 69.04f, 161.5f}},
+      {{29.6915239933531f, 56.11167248735513f, -36.292665028011974f},
+       {29.6915239933531f, 66.82572352143814f,
+        -32.894523620605469f}},  // purple
+      {{38.14895894517021f, 50.38364171345111f, 31.834803335164764f},
+       {38.14895894517021f, 59.598372928277406f,
+        32.286662896162966f}},  // brown
+      {{46.27770902748027f, -47.55240796497723f, 48.586294664234586f},
+       {46.27770902748027f, 67.9842594463414f, 134.3838583288382f}}};  // green
+
+  for (auto& color_pair : colors_tests) {
+    auto [input_l, input_a, input_b] = color_pair.input;
+    auto [expected_l, expected_c, expected_h] = color_pair.expected;
+    auto [output_l, output_c, output_h] = LabToLCH(input_l, input_a, input_b);
+    EXPECT_NEAR(output_l, expected_l, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << ' ' << expected_c << ' ' << expected_h << " produced " << output_l
+        << ' ' << output_c << ' ' << output_h;
+    EXPECT_NEAR(output_c, expected_c, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << ' ' << expected_c << ' ' << expected_h << " produced " << output_l
+        << ' ' << output_c << ' ' << output_h;
+    EXPECT_NEAR(output_h, expected_h, 0.001f)
+        << input_l << ' ' << input_a << ' ' << input_b << " to " << expected_l
+        << ' ' << expected_c << ' ' << expected_h << " produced " << output_l
+        << ' ' << output_c << ' ' << output_h;
+  }
+}
+
 TEST(ColorConversions, LchToSkColor4f) {
   // Color conversions obtained from
   // https://colorjs.io/apps/convert/?color=purple&precision=4
diff --git a/ui/gl/dc_renderer_layer_params.h b/ui/gl/dc_renderer_layer_params.h
index aad14f5..344ea33 100644
--- a/ui/gl/dc_renderer_layer_params.h
+++ b/ui/gl/dc_renderer_layer_params.h
@@ -66,7 +66,7 @@
 
   gfx::HDRMetadata hdr_metadata;
 
-  bool is_video_fullscreen_letterboxing;
+  bool is_video_fullscreen_letterboxing = false;
 };
 
 }  // namespace ui
diff --git a/ui/gl/gl_surface_egl_surface_control.cc b/ui/gl/gl_surface_egl_surface_control.cc
index 01027a2..f06e2e4 100644
--- a/ui/gl/gl_surface_egl_surface_control.cc
+++ b/ui/gl/gl_surface_egl_surface_control.cc
@@ -21,7 +21,6 @@
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_fence_android_native_fence_sync.h"
-#include "ui/gl/gl_image_ahardwarebuffer.h"
 #include "ui/gl/gl_utils.h"
 
 namespace gl {
diff --git a/ui/ozone/platform/x11/gl_ozone_glx.cc b/ui/ozone/platform/x11/gl_ozone_glx.cc
index 626d75a..f8de6c9 100644
--- a/ui/ozone/platform/x11/gl_ozone_glx.cc
+++ b/ui/ozone/platform/x11/gl_ozone_glx.cc
@@ -10,7 +10,6 @@
 #include "ui/gl/gl_context_glx.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_glx_api_implementation.h"
-#include "ui/gl/gl_image_glx_native_pixmap.h"
 #include "ui/gl/gl_surface_glx_x11.h"
 #include "ui/gl/gl_utils.h"
 #include "ui/ozone/platform/x11/native_pixmap_glx_binding.h"