diff --git a/AUTHORS b/AUTHORS
index 29011ff..61e471e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -260,6 +260,7 @@
 Deokjin Kim <deokjin81.kim@samsung.com>
 Derek Halman <d.halman@gmail.com>
 Devlin Cronin <rdevlin.cronin@gmail.com>
+Dhi Aurrahman <dio@rockybars.com>
 Diana Suvorova <diana.suvorova@gmail.com>
 Diego Ferreiro Val <elfogris@gmail.com>
 Dillon Sellars <dill.sellars@gmail.com>
diff --git a/DEPS b/DEPS
index 65b47cb..6cd0aeb1 100644
--- a/DEPS
+++ b/DEPS
@@ -200,11 +200,11 @@
   # 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': '39b4c86fe9146508a2d92d5775331c8aa1a04693',
+  'skia_revision': '48a99420a2495a665a732a7fe335e93ec7a83418',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'fe46597df7664f8fe317c2363cfad28007283e75',
+  'v8_revision': '8040ad59abd7119457d5acbde8602fde8a5291e5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -212,11 +212,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '939fcd1a14f5ddc43e300bcb2d5d1f680c50ab85',
+  'angle_revision': '8420c5acaa568cacb7283116fce3ad283075166e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '43bb60e1fa119d80b449c6550dd2b72328b101b9',
+  'swiftshader_revision': '9dff6a3bb5950edd890e5141f2d3d2f3b4ef351e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -259,7 +259,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': 'c39ab82c90479341dcf28eaa8174af6f08c0d7ae',
+  'harfbuzz_revision': '53806e5b83cee0e275eac038d0780f95ac56588c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Emoji Segmenter
   # and whatever else without interference from each other.
@@ -275,7 +275,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': 'abee7cf3a9897cce6d02571c4aa37a06509c2358',
+  'devtools_frontend_revision': '3383020812d40c5b2839233c6817d2d8f84eff43',
   # 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.
@@ -327,7 +327,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': '9175f00eec91c6c9b3c3e264d6d8ec7879881844',
+  'dawn_revision': '71279dcde7cd1b718b137b9ed0384803f347a359',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -899,7 +899,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9ed30bc3ed292b02d85fde89c64207484b7a3aa4',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fc23bc1c96acb0fa2fcc526204333fb9a373ecb6',
       'condition': 'checkout_chromeos',
   },
 
@@ -1369,7 +1369,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'pYEJYcwmc7764Ioa_c8OSqFs8pflIE1Y-yEvVdFb3zUC'
+              'version': 'Ts7JI7CLxH4-h5lAQEsDnba0CD6d4ACU00UDPQXaW0wC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1551,7 +1551,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b6b599fc588e59142df343d2d2897203468aacea',
+    Var('webrtc_git') + '/src.git' + '@' + '0d863f72a8c747c1b41f2798e5201e1abcdaec2b',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1623,7 +1623,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dee1cdf147f77fcf5597d87931226c519f49c66c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d90991c959bad057c3109394079ce08097d66e43',
     'condition': 'checkout_src_internal',
   },
 
@@ -1642,7 +1642,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'UEDryZf_VuC_LfcEXlTJ_7tEzVfL6qKZIy8qC-wjmbIC',
+        'version': '2vgKL8p-SUW0S-OFCowtelp1xuW-s-25Zm0z7AwTFPMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/login/ui/login_user_view.cc b/ash/login/ui/login_user_view.cc
index 1e46272..95589bc 100644
--- a/ash/login/ui/login_user_view.cc
+++ b/ash/login/ui/login_user_view.cc
@@ -17,6 +17,7 @@
 #include "ash/public/cpp/login_constants.h"
 #include "ash/public/cpp/session/user_info.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/bind.h"
@@ -171,6 +172,7 @@
     enterprise_icon_->SetVisible(false);
     AddChildView(enterprise_icon_);
   }
+
   ~UserImage() override = default;
 
   void UpdateForUser(const LoginUserInfo& user) {
@@ -461,6 +463,9 @@
   hover_notifier_ = std::make_unique<HoverNotifier>(
       this,
       base::BindRepeating(&LoginUserView::OnHover, base::Unretained(this)));
+
+  if (ash::Shell::HasInstance())
+    display_observation_.Observe(ash::Shell::Get()->display_configurator());
 }
 
 LoginUserView::~LoginUserView() = default;
@@ -538,6 +543,12 @@
                                         : FocusBehavior::NEVER);
 }
 
+void LoginUserView::OnPowerStateChanged(
+    chromeos::DisplayPowerState power_state) {
+  bool is_display_on = power_state != chromeos::DISPLAY_POWER_ALL_OFF;
+  user_image_->SetAnimationEnabled(is_display_on && is_opaque_);
+}
+
 const char* LoginUserView::GetClassName() const {
   return kUserViewClassName;
 }
diff --git a/ash/login/ui/login_user_view.h b/ash/login/ui/login_user_view.h
index a333c751..f3bfa31 100644
--- a/ash/login/ui/login_user_view.h
+++ b/ash/login/ui/login_user_view.h
@@ -11,6 +11,8 @@
 #include "ash/login/ui/login_user_menu_view.h"
 #include "ash/public/cpp/login_types.h"
 #include "base/macros.h"
+#include "base/scoped_observation.h"
+#include "ui/display/manager/display_configurator.h"
 #include "ui/views/view.h"
 
 namespace ash {
@@ -20,7 +22,8 @@
 
 // Display the user's profile icon, name, and a menu icon in various layout
 // styles.
-class ASH_EXPORT LoginUserView : public views::View {
+class ASH_EXPORT LoginUserView : public views::View,
+                                 public display::DisplayConfigurator::Observer {
  public:
   // TestApi is used for tests to get internal implementation details.
   class ASH_EXPORT TestApi {
@@ -71,6 +74,9 @@
   // Enables or disables tapping the view.
   void SetTapEnabled(bool enabled);
 
+  // DisplayConfigurator::Observer
+  void OnPowerStateChanged(chromeos::DisplayPowerState power_state) override;
+
   const LoginUserInfo& current_user() const { return current_user_; }
 
   // views::View:
@@ -128,6 +134,10 @@
   // state.
   bool force_opaque_ = false;
 
+  base::ScopedObservation<display::DisplayConfigurator,
+                          display::DisplayConfigurator::Observer>
+      display_observation_{this};
+
   DISALLOW_COPY_AND_ASSIGN(LoginUserView);
 };
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5830912..05fb2f7d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3865,6 +3865,7 @@
     ]
 
     sources = [
+      "test/android/javatests/src/org/chromium/base/test/BaseActivityTestRule.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java",
       "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index a2570a5a..be169dac 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -55,12 +55,11 @@
 
   root->total_size_of_direct_mapped_pages.fetch_add(reserved_size,
                                                     std::memory_order_relaxed);
-  root->IncreaseCommittedPages(slot_size);
 
   char* slot = ptr + PartitionPageSize();
   RecommitSystemPages(ptr + SystemPageSize(), SystemPageSize(), PageReadWrite,
                       PageUpdatePermissions);
-  RecommitSystemPages(slot, slot_size, PageReadWrite, PageUpdatePermissions);
+  root->RecommitSystemPagesForData(slot, slot_size, PageUpdatePermissions);
 
   auto* metadata = reinterpret_cast<PartitionDirectMapMetadata<thread_safe>*>(
       PartitionSuperPageToMetadataArea(ptr));
@@ -220,9 +219,8 @@
 
   // System pages in the super page come in a decommited state. Commit them
   // before vending them back.
-  RecommitSystemPages(ret, slot_span_committed_size, PageReadWrite,
-                      PageUpdatePermissions);
-  root->IncreaseCommittedPages(slot_span_committed_size);
+  root->RecommitSystemPagesForData(ret, slot_span_committed_size,
+                                   PageUpdatePermissions);
   root->next_partition_page += slot_span_reserved_size;
   // Double check that we had enough space in the super page for the new slot
   // span.
@@ -569,9 +567,9 @@
       PA_DCHECK(new_slot_span->is_decommitted());
       decommitted_slot_spans_head = new_slot_span->next_slot_span;
       void* addr = SlotSpanMetadata<thread_safe>::ToPointer(new_slot_span);
-      root->RecommitSystemPages(addr,
-                                new_slot_span->bucket->get_bytes_per_span(),
-                                PageKeepPermissionsIfPossible);
+      root->RecommitSystemPagesForData(
+          addr, new_slot_span->bucket->get_bytes_per_span(),
+          PageKeepPermissionsIfPossible);
       new_slot_span->Reset();
       *is_already_zeroed = kDecommittedPagesAreAlwaysZeroed;
     }
diff --git a/base/allocator/partition_allocator/partition_page.cc b/base/allocator/partition_allocator/partition_page.cc
index e65c795..aafb7ba0 100644
--- a/base/allocator/partition_allocator/partition_page.cc
+++ b/base/allocator/partition_allocator/partition_page.cc
@@ -41,6 +41,7 @@
     extent->next_extent->prev_extent = extent->prev_extent;
   }
 
+  // The actual decommit is deferred, when releasing the reserved memory region.
   root->DecreaseCommittedPages(slot_span->bucket->slot_size);
 
   size_t reserved_size =
@@ -166,8 +167,8 @@
   PA_DCHECK(is_empty());
   PA_DCHECK(!bucket->is_direct_mapped());
   void* addr = SlotSpanMetadata::ToPointer(this);
-  root->DecommitSystemPages(addr, bucket->get_bytes_per_span(),
-                            PageKeepPermissionsIfPossible);
+  root->DecommitSystemPagesForData(addr, bucket->get_bytes_per_span(),
+                                   PageKeepPermissionsIfPossible);
 
   // We actually leave the decommitted slot span in the active list. We'll sweep
   // it on to the decommitted list when we next walk the active list.
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 95243580..fb63b42a 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -456,15 +456,16 @@
 
     // Shrink by decommitting unneeded pages and making them inaccessible.
     size_t decommit_size = current_slot_size - new_slot_size;
-    DecommitSystemPages(char_ptr + new_slot_size, decommit_size,
-                        PageUpdatePermissions);
+    DecommitSystemPagesForData(char_ptr + new_slot_size, decommit_size,
+                               PageUpdatePermissions);
   } else if (new_slot_size <=
              DirectMapExtent::FromSlotSpan(slot_span)->map_size) {
     // Grow within the actually allocated memory. Just need to make the
     // pages accessible again.
     size_t recommit_slot_size_growth = new_slot_size - current_slot_size;
-    RecommitSystemPages(char_ptr + current_slot_size, recommit_slot_size_growth,
-                        PageUpdatePermissions);
+    RecommitSystemPagesForData(char_ptr + current_slot_size,
+                               recommit_slot_size_growth,
+                               PageUpdatePermissions);
 
 #if DCHECK_IS_ON()
     memset(char_ptr + current_slot_size, kUninitializedByte,
@@ -596,10 +597,12 @@
 template <bool thread_safe>
 void PartitionRoot<thread_safe>::PurgeMemory(int flags) {
   // TODO(chromium:1129751): Change to LIKELY once PCScan is enabled by default.
-  if (UNLIKELY(IsScanEnabled()) && (flags & PartitionPurgeForceAllFreed)) {
-    PCScan::Instance().PerformScanIfNeeded(PCScan::InvocationMode::kBlocking);
+  if (UNLIKELY(IsScanEnabled())) {
+    if (flags & PartitionPurgeForceAllFreed)
+      PCScan::Instance().PerformScan(PCScan::InvocationMode::kBlocking);
+    else
+      PCScan::Instance().PerformScanIfNeeded(PCScan::InvocationMode::kBlocking);
   }
-
   {
     ScopedGuard guard{lock_};
     if (flags & PartitionPurgeDecommitEmptySlotSpans)
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index fe5008e0..5406b25d 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -212,12 +212,12 @@
 
   ALWAYS_INLINE void IncreaseCommittedPages(size_t len);
   ALWAYS_INLINE void DecreaseCommittedPages(size_t len);
-  ALWAYS_INLINE void DecommitSystemPages(
+  ALWAYS_INLINE void DecommitSystemPagesForData(
       void* address,
       size_t length,
       PageAccessibilityDisposition accessibility_disposition)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  ALWAYS_INLINE void RecommitSystemPages(
+  ALWAYS_INLINE void RecommitSystemPagesForData(
       void* address,
       size_t length,
       PageAccessibilityDisposition accessibility_disposition)
@@ -856,21 +856,21 @@
 }
 
 template <bool thread_safe>
-ALWAYS_INLINE void PartitionRoot<thread_safe>::DecommitSystemPages(
+ALWAYS_INLINE void PartitionRoot<thread_safe>::DecommitSystemPagesForData(
     void* address,
     size_t length,
     PageAccessibilityDisposition accessibility_disposition) {
-  ::base::DecommitSystemPages(address, length, accessibility_disposition);
+  DecommitSystemPages(address, length, accessibility_disposition);
   DecreaseCommittedPages(length);
 }
 
 template <bool thread_safe>
-ALWAYS_INLINE void PartitionRoot<thread_safe>::RecommitSystemPages(
+ALWAYS_INLINE void PartitionRoot<thread_safe>::RecommitSystemPagesForData(
     void* address,
     size_t length,
     PageAccessibilityDisposition accessibility_disposition) {
-  ::base::RecommitSystemPages(address, length, PageReadWrite,
-                              accessibility_disposition);
+  RecommitSystemPages(address, length, PageReadWrite,
+                      accessibility_disposition);
   IncreaseCommittedPages(length);
 }
 
diff --git a/base/allocator/partition_allocator/pcscan.cc b/base/allocator/partition_allocator/pcscan.cc
index 05825b2..49ccdc0 100644
--- a/base/allocator/partition_allocator/pcscan.cc
+++ b/base/allocator/partition_allocator/pcscan.cc
@@ -533,13 +533,11 @@
   auto task = std::make_unique<PCScanTask>(*this);
 
   // Post PCScan task.
-  const auto callback = [](PCScanTask task) { std::move(task).RunOnce(); };
-  if (UNLIKELY(invocation_mode == InvocationMode::kBlocking)) {
-    // Blocking is only used for testing.
-    callback(std::move(*task));
-  } else {
-    PA_DCHECK(InvocationMode::kNonBlocking == invocation_mode);
+  if (LIKELY(invocation_mode == InvocationMode::kNonBlocking)) {
     PCScanThread::Instance().PostTask(std::move(task));
+  } else {
+    PA_DCHECK(InvocationMode::kBlocking == invocation_mode);
+    std::move(*task).RunOnce();
   }
 }
 
diff --git a/base/allocator/partition_allocator/pcscan.h b/base/allocator/partition_allocator/pcscan.h
index 8bc1410..dca4d9b 100644
--- a/base/allocator/partition_allocator/pcscan.h
+++ b/base/allocator/partition_allocator/pcscan.h
@@ -66,6 +66,9 @@
 
   ALWAYS_INLINE void MoveToQuarantine(void* ptr, SlotSpan* slot_span);
 
+  // Performs scanning unconditionally.
+  void PerformScan(InvocationMode invocation_mode);
+  // Performs scanning only if a certain quarantine threshold was reached.
   void PerformScanIfNeeded(InvocationMode invocation_mode);
 
   void ClearRootsForTesting();
@@ -135,8 +138,6 @@
 
   constexpr PCScan() = default;
 
-  void PerformScan(InvocationMode invocation_mode);
-
   static PCScan instance_ PA_CONSTINIT;
 
   Roots roots_{};
diff --git a/base/allocator/partition_allocator/thread_cache.cc b/base/allocator/partition_allocator/thread_cache.cc
index 1ca1fe1..ebe3d58 100644
--- a/base/allocator/partition_allocator/thread_cache.cc
+++ b/base/allocator/partition_allocator/thread_cache.cc
@@ -148,6 +148,24 @@
 ThreadCache::ThreadCache(PartitionRoot<ThreadSafe>* root)
     : buckets_(), stats_(), root_(root), next_(nullptr), prev_(nullptr) {
   ThreadCacheRegistry::Instance().RegisterThreadCache(this);
+
+  for (int index = 0; index < kBucketCount; index++) {
+    const auto& root_bucket = root->buckets[index];
+    // Invalid bucket.
+    if (!root_bucket.active_slot_spans_head)
+      continue;
+
+    // Smaller allocations are more frequent, and more performance-sensitive.
+    // Cache more small objects, and fewer larger ones, to save memory.
+    size_t element_size = root_bucket.slot_size;
+    if (element_size <= 128) {
+      buckets_[index].limit = 128;
+    } else if (element_size <= 256) {
+      buckets_[index].limit = 64;
+    } else {
+      buckets_[index].limit = 32;
+    }
+  }
 }
 
 ThreadCache::~ThreadCache() {
diff --git a/base/allocator/partition_allocator/thread_cache.h b/base/allocator/partition_allocator/thread_cache.h
index 10805e7..3d4f3ffd 100644
--- a/base/allocator/partition_allocator/thread_cache.h
+++ b/base/allocator/partition_allocator/thread_cache.h
@@ -161,8 +161,9 @@
 
  private:
   struct Bucket {
-    size_t count;
     PartitionFreelistEntry* freelist_head;
+    uint16_t count;
+    uint16_t limit;
   };
 
   explicit ThreadCache(PartitionRoot<ThreadSafe>* root);
@@ -172,16 +173,13 @@
 
   // TODO(lizeb): Optimize the threshold.
   static constexpr size_t kSizeThreshold = 512;
-  static constexpr size_t kBucketCount =
+  static constexpr uint16_t kBucketCount =
       ((ConstexprLog2(kSizeThreshold) - kMinBucketedOrder + 1)
        << kNumBucketsPerOrderBits) +
       1;
   static_assert(
       kBucketCount < kNumBuckets,
       "Cannot have more cached buckets than what the allocator supports");
-  // TODO(lizeb): Tune this constant, and adapt it to the bucket size /
-  // allocation patterns.
-  static constexpr size_t kMaxCountPerBucket = 100;
 
   std::atomic<bool> should_purge_;
   Bucket buckets_[kBucketCount];
@@ -208,7 +206,7 @@
 
   INCREMENT_COUNTER(stats_.cache_fill_count);
 
-  if (bucket_index >= kBucketCount) {
+  if (UNLIKELY(bucket_index >= kBucketCount)) {
     INCREMENT_COUNTER(stats_.cache_fill_misses);
     return false;
   }
@@ -225,8 +223,8 @@
   INCREMENT_COUNTER(stats_.cache_fill_hits);
 
   // Batched deallocation, amortizing lock acquisitions.
-  if (bucket.count >= kMaxCountPerBucket) {
-    ClearBucket(bucket, kMaxCountPerBucket / 2);
+  if (UNLIKELY(bucket.count >= bucket.limit)) {
+    ClearBucket(bucket, bucket.limit >> 1);
   }
 
   return true;
@@ -235,7 +233,7 @@
 ALWAYS_INLINE void* ThreadCache::GetFromCache(size_t bucket_index) {
   INCREMENT_COUNTER(stats_.alloc_count);
   // Only handle "small" allocations.
-  if (bucket_index >= kBucketCount) {
+  if (UNLIKELY(bucket_index >= kBucketCount)) {
     INCREMENT_COUNTER(stats_.alloc_miss_too_large);
     INCREMENT_COUNTER(stats_.alloc_misses);
     return nullptr;
@@ -243,7 +241,7 @@
 
   auto& bucket = buckets_[bucket_index];
   auto* result = bucket.freelist_head;
-  if (!result) {
+  if (UNLIKELY(!result)) {
     PA_DCHECK(bucket.count == 0);
     INCREMENT_COUNTER(stats_.alloc_miss_empty);
     INCREMENT_COUNTER(stats_.alloc_misses);
diff --git a/base/allocator/partition_allocator/thread_cache_unittest.cc b/base/allocator/partition_allocator/thread_cache_unittest.cc
index cf40720..b92d817d 100644
--- a/base/allocator/partition_allocator/thread_cache_unittest.cc
+++ b/base/allocator/partition_allocator/thread_cache_unittest.cc
@@ -299,19 +299,20 @@
 
   tcache->Purge();
   cache_fill_counter.Reset();
-  // Bucket are never full, fill always succeeds.
-  size_t bucket_index = FillThreadCacheAndReturnIndex(
-      kTestSize, ThreadCache::kMaxCountPerBucket + 10);
-  EXPECT_EQ(ThreadCache::kMaxCountPerBucket + 10, cache_fill_counter.Delta());
+  constexpr size_t kMaxCountForBucket = 128;
+  // Buckets are never full, fill always succeeds.
+  size_t bucket_index =
+      FillThreadCacheAndReturnIndex(kTestSize, kMaxCountForBucket + 10);
+  EXPECT_EQ(kMaxCountForBucket + 10, cache_fill_counter.Delta());
   EXPECT_EQ(0u, cache_fill_misses_counter.Delta());
 
   // Memory footprint.
   ThreadCacheStats stats;
   ThreadCacheRegistry::Instance().DumpStats(true, &stats);
   // Bucket was cleared (count halved, then refilled).
-  EXPECT_EQ(g_root->buckets[bucket_index].slot_size *
-                (ThreadCache::kMaxCountPerBucket / 2 + 10),
-            stats.bucket_total_memory);
+  EXPECT_EQ(
+      g_root->buckets[bucket_index].slot_size * (kMaxCountForBucket / 2 + 10),
+      stats.bucket_total_memory);
   EXPECT_EQ(sizeof(ThreadCache), stats.metadata_overhead);
 }
 
diff --git a/base/android/java/src/org/chromium/base/JNIUtils.java b/base/android/java/src/org/chromium/base/JNIUtils.java
index 1b53b9f06..44c6a83 100644
--- a/base/android/java/src/org/chromium/base/JNIUtils.java
+++ b/base/android/java/src/org/chromium/base/JNIUtils.java
@@ -4,6 +4,9 @@
 
 package org.chromium.base;
 
+import android.content.Context;
+import android.text.TextUtils;
+
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.MainDex;
 
@@ -20,14 +23,24 @@
      * is needed for the few cases where the JNI mechanism is unable to automatically determine the
      * appropriate ClassLoader instance.
      */
-    @CalledByNative
-    public static Object getClassLoader() {
+    private static ClassLoader getClassLoader() {
         if (sJniClassLoader == null) {
             return JNIUtils.class.getClassLoader();
         }
         return sJniClassLoader;
     }
 
+    /** Returns a ClassLoader which can load Java classes from the specified split. */
+    @CalledByNative
+    public static ClassLoader getSplitClassLoader(String splitName) {
+        Context context = ContextUtils.getApplicationContext();
+        if (!TextUtils.isEmpty(splitName)
+                && BundleUtils.isIsolatedSplitInstalled(context, splitName)) {
+            return BundleUtils.createIsolatedSplitContext(context, splitName).getClassLoader();
+        }
+        return getClassLoader();
+    }
+
     /**
      * Sets the ClassLoader to be used for loading Java classes from native.
      * @param classLoader the ClassLoader to use.
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc
index 679a7d2..1f02bbd0 100644
--- a/base/android/jni_android.cc
+++ b/base/android/jni_android.cc
@@ -11,19 +11,22 @@
 
 #include "base/android/java_exception_reporter.h"
 #include "base/android/jni_string.h"
+#include "base/android/jni_utils.h"
+#include "base/containers/flat_map.h"
 #include "base/debug/debugging_buildflags.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
 #include "base/threading/thread_local.h"
 
+namespace base {
+namespace android {
 namespace {
-using base::android::GetClass;
-using base::android::MethodID;
-using base::android::ScopedJavaLocalRef;
 
 JavaVM* g_jvm = NULL;
-base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject>>::Leaky
-    g_class_loader = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<ScopedJavaGlobalRef<jobject>>::Leaky g_class_loader =
+    LAZY_INSTANCE_INITIALIZER;
 jmethodID g_class_loader_load_class_method_id = 0;
 
 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
@@ -33,10 +36,61 @@
 
 bool g_fatal_exception_occurred = false;
 
-}  // namespace
+// Returns a ClassLoader instance which will be able to load classes from the
+// specified split.
+jobject GetCachedClassLoader(JNIEnv* env, const std::string& split_name) {
+  DCHECK(!split_name.empty());
+  static base::NoDestructor<base::Lock> lock;
+  static base::NoDestructor<
+      base::flat_map<std::string, ScopedJavaGlobalRef<jobject>>>
+      split_class_loader_map;
 
-namespace base {
-namespace android {
+  base::AutoLock guard(*lock);
+  auto it = split_class_loader_map->find(split_name);
+  if (it != split_class_loader_map->end()) {
+    return it->second.obj();
+  }
+
+  ScopedJavaGlobalRef<jobject> class_loader(
+      GetSplitClassLoader(env, split_name));
+  jobject class_loader_obj = class_loader.obj();
+  split_class_loader_map->insert({split_name, std::move(class_loader)});
+  return class_loader_obj;
+}
+
+ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
+                                            const char* class_name,
+                                            jobject class_loader) {
+  jclass clazz;
+  if (class_loader != nullptr) {
+    // ClassLoader.loadClass expects a classname with components separated by
+    // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
+    // generator generates names with slashes, so we have to replace them here.
+    // TODO(torne): move to an approach where we always use ClassLoader except
+    // for the special case of base::android::GetClassLoader(), and change the
+    // JNI generator to generate dot-separated names. http://crbug.com/461773
+    size_t bufsize = strlen(class_name) + 1;
+    char dotted_name[bufsize];
+    memmove(dotted_name, class_name, bufsize);
+    for (size_t i = 0; i < bufsize; ++i) {
+      if (dotted_name[i] == '/') {
+        dotted_name[i] = '.';
+      }
+    }
+
+    clazz = static_cast<jclass>(
+        env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
+                              ConvertUTF8ToJavaString(env, dotted_name).obj()));
+  } else {
+    clazz = env->FindClass(class_name);
+  }
+  if (ClearException(env) || !clazz) {
+    LOG(FATAL) << "Failed to find class " << class_name;
+  }
+  return ScopedJavaLocalRef<jclass>(env, clazz);
+}
+
+}  // namespace
 
 JNIEnv* AttachCurrentThread() {
   DCHECK(g_jvm);
@@ -109,41 +163,44 @@
   g_class_loader.Get().Reset(class_loader);
 }
 
-ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
-  jclass clazz;
-  if (!g_class_loader.Get().is_null()) {
-    // ClassLoader.loadClass expects a classname with components separated by
-    // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
-    // generator generates names with slashes, so we have to replace them here.
-    // TODO(torne): move to an approach where we always use ClassLoader except
-    // for the special case of base::android::GetClassLoader(), and change the
-    // JNI generator to generate dot-separated names. http://crbug.com/461773
-    size_t bufsize = strlen(class_name) + 1;
-    char dotted_name[bufsize];
-    memmove(dotted_name, class_name, bufsize);
-    for (size_t i = 0; i < bufsize; ++i) {
-      if (dotted_name[i] == '/') {
-        dotted_name[i] = '.';
-      }
-    }
-
-    clazz = static_cast<jclass>(
-        env->CallObjectMethod(g_class_loader.Get().obj(),
-                              g_class_loader_load_class_method_id,
-                              ConvertUTF8ToJavaString(env, dotted_name).obj()));
-  } else {
-    clazz = env->FindClass(class_name);
-  }
-  if (ClearException(env) || !clazz) {
-    LOG(FATAL) << "Failed to find class " << class_name;
-  }
-  return ScopedJavaLocalRef<jclass>(env, clazz);
+ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
+                                    const char* class_name,
+                                    const std::string& split_name) {
+  return GetClassInternal(env, class_name,
+                          GetCachedClassLoader(env, split_name));
 }
 
-jclass LazyGetClass(
-    JNIEnv* env,
-    const char* class_name,
-    std::atomic<jclass>* atomic_class_id) {
+ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
+  return GetClassInternal(env, class_name, g_class_loader.Get().obj());
+}
+
+// This is duplicated with LazyGetClass below because these are performance
+// sensitive.
+jclass LazyGetClass(JNIEnv* env,
+                    const char* class_name,
+                    const std::string& split_name,
+                    std::atomic<jclass>* atomic_class_id) {
+  const jclass value = std::atomic_load(atomic_class_id);
+  if (value)
+    return value;
+  ScopedJavaGlobalRef<jclass> clazz;
+  clazz.Reset(GetClass(env, class_name, split_name));
+  jclass cas_result = nullptr;
+  if (std::atomic_compare_exchange_strong(atomic_class_id, &cas_result,
+                                          clazz.obj())) {
+    // We intentionally leak the global ref since we now storing it as a raw
+    // pointer in |atomic_class_id|.
+    return clazz.Release();
+  } else {
+    return cas_result;
+  }
+}
+
+// This is duplicated with LazyGetClass above because these are performance
+// sensitive.
+jclass LazyGetClass(JNIEnv* env,
+                    const char* class_name,
+                    std::atomic<jclass>* atomic_class_id) {
   const jclass value = std::atomic_load(atomic_class_id);
   if (value)
     return value;
diff --git a/base/android/jni_android.h b/base/android/jni_android.h
index 0e8e322..41ab9c3 100644
--- a/base/android/jni_android.h
+++ b/base/android/jni_android.h
@@ -96,6 +96,9 @@
 // This method triggers a fatal assertion if the class could not be found.
 // Use HasClass if you need to check whether the class exists.
 BASE_EXPORT ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
+                                                const char* class_name,
+                                                const std::string& split_name);
+BASE_EXPORT ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
                                                 const char* class_name);
 
 // The method will initialize |atomic_class_id| to contain a global ref to the
@@ -104,6 +107,10 @@
 // The caller is responsible to zero-initialize |atomic_method_id|.
 // It's fine to simultaneously call this on multiple threads referencing the
 // same |atomic_method_id|.
+BASE_EXPORT jclass LazyGetClass(JNIEnv* env,
+                                const char* class_name,
+                                const std::string& split_name,
+                                std::atomic<jclass>* atomic_class_id);
 BASE_EXPORT jclass LazyGetClass(
     JNIEnv* env,
     const char* class_name,
diff --git a/base/android/jni_generator/golden/SampleForTestsWithSplit_jni.golden b/base/android/jni_generator/golden/SampleForTestsWithSplit_jni.golden
new file mode 100644
index 0000000..c7bd774e
--- /dev/null
+++ b/base/android/jni_generator/golden/SampleForTestsWithSplit_jni.golden
@@ -0,0 +1,527 @@
+// Copyright 2014 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.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+
+// Step 1: Forward declarations.
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests[] =
+    "org/chromium/example/jni_generator/SampleForTests";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA[] =
+    "org/chromium/example/jni_generator/SampleForTests$InnerStructA";
+
+JNI_REGISTRATION_EXPORT extern const char
+    kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB[];
+const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB[] =
+    "org/chromium/example/jni_generator/SampleForTests$InnerStructB";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT std::atomic<jclass>
+    g_org_chromium_example_jni_1generator_SampleForTests_clazz(nullptr);
+#ifndef org_chromium_example_jni_1generator_SampleForTests_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_clazz(JNIEnv* env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests, "sample",
+      &g_org_chromium_example_jni_1generator_SampleForTests_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT std::atomic<jclass>
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(nullptr);
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(JNIEnv*
+    env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA, "sample",
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT std::atomic<jclass>
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(nullptr);
+#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz_defined
+#define org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz_defined
+inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(JNIEnv*
+    env) {
+  return base::android::LazyGetClass(env,
+      kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB, "sample",
+      &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz);
+}
+#endif
+
+
+// Step 2: Constants (optional).
+
+
+// Step 3: Method stubs.
+namespace base {
+namespace android {
+
+static jlong JNI_SampleForTests_Init(JNIEnv* env, const base::android::JavaParamRef<jobject>&
+    caller,
+    const base::android::JavaParamRef<jstring>& param);
+
+JNI_GENERATOR_EXPORT jlong
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1init(
+    JNIEnv* env,
+    jclass jcaller,
+    jobject caller,
+    jstring param) {
+  return JNI_SampleForTests_Init(env, base::android::JavaParamRef<jobject>(env, caller),
+      base::android::JavaParamRef<jstring>(env, param));
+}
+
+JNI_GENERATOR_EXPORT void
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1destroy(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong nativeCPPClass,
+    jobject caller) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Destroy");
+  return native->Destroy(env, base::android::JavaParamRef<jobject>(env, caller));
+}
+
+static jdouble JNI_SampleForTests_GetDoubleFunction(JNIEnv* env, const
+    base::android::JavaParamRef<jobject>& caller);
+
+JNI_GENERATOR_EXPORT jdouble
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1getDoubleFunction(
+    JNIEnv* env,
+    jclass jcaller,
+    jobject caller) {
+  return JNI_SampleForTests_GetDoubleFunction(env, base::android::JavaParamRef<jobject>(env,
+      caller));
+}
+
+static jfloat JNI_SampleForTests_GetFloatFunction(JNIEnv* env);
+
+JNI_GENERATOR_EXPORT jfloat
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1getFloatFunction(
+    JNIEnv* env,
+    jclass jcaller) {
+  return JNI_SampleForTests_GetFloatFunction(env);
+}
+
+static void JNI_SampleForTests_SetNonPODDatatype(JNIEnv* env, const
+    base::android::JavaParamRef<jobject>& caller,
+    const base::android::JavaParamRef<jobject>& rect);
+
+JNI_GENERATOR_EXPORT void
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1setNonPODDatatype(
+    JNIEnv* env,
+    jclass jcaller,
+    jobject caller,
+    jobject rect) {
+  return JNI_SampleForTests_SetNonPODDatatype(env, base::android::JavaParamRef<jobject>(env,
+      caller), base::android::JavaParamRef<jobject>(env, rect));
+}
+
+static base::android::ScopedJavaLocalRef<jobject> JNI_SampleForTests_GetNonPODDatatype(JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& caller);
+
+JNI_GENERATOR_EXPORT jobject
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1getNonPODDatatype(
+    JNIEnv* env,
+    jclass jcaller,
+    jobject caller) {
+  return JNI_SampleForTests_GetNonPODDatatype(env, base::android::JavaParamRef<jobject>(env,
+      caller)).Release();
+}
+
+JNI_GENERATOR_EXPORT jint
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1method(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong nativeCPPClass,
+    jobject caller) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
+  return native->Method(env, base::android::JavaParamRef<jobject>(env, caller));
+}
+
+JNI_GENERATOR_EXPORT jdouble
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1methodOtherP0(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong nativePtr,
+    jobject caller) {
+  CPPClass::InnerClass* native = reinterpret_cast<CPPClass::InnerClass*>(nativePtr);
+  CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0);
+  return native->MethodOtherP0(env, base::android::JavaParamRef<jobject>(env, caller));
+}
+
+JNI_GENERATOR_EXPORT void
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1addStructB(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong nativeCPPClass,
+    jobject caller,
+    jobject b) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "AddStructB");
+  return native->AddStructB(env, base::android::JavaParamRef<jobject>(env, caller),
+      base::android::JavaParamRef<jobject>(env, b));
+}
+
+JNI_GENERATOR_EXPORT void
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1iterateAndDoSomethingWithStructB(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong nativeCPPClass,
+    jobject caller) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "IterateAndDoSomethingWithStructB");
+  return native->IterateAndDoSomethingWithStructB(env, base::android::JavaParamRef<jobject>(env,
+      caller));
+}
+
+JNI_GENERATOR_EXPORT jstring
+    Java_org_chromium_base_natives_GEN_1JNI_org_1chromium_1example_1jni_11generator_1SampleForTests_1returnAString(
+    JNIEnv* env,
+    jclass jcaller,
+    jlong nativeCPPClass,
+    jobject caller) {
+  CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+  CHECK_NATIVE_PTR(env, jcaller, native, "ReturnAString", NULL);
+  return native->ReturnAString(env, base::android::JavaParamRef<jobject>(env, caller)).Release();
+}
+
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_javaMethod(nullptr);
+static jint Java_SampleForTests_javaMethod(JNIEnv* env, const base::android::JavaRef<jobject>& obj,
+    JniIntWrapper foo,
+    JniIntWrapper bar) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "javaMethod",
+          "(II)I",
+          &g_org_chromium_example_jni_1generator_SampleForTests_javaMethod);
+
+  jint ret =
+      env->CallIntMethod(obj.obj(),
+          call_context.base.method_id, as_jint(foo), as_jint(bar));
+  return ret;
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_staticJavaMethod(nullptr);
+static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, clazz,
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), false);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_STATIC>(
+          env,
+          clazz,
+          "staticJavaMethod",
+          "()Z",
+          &g_org_chromium_example_jni_1generator_SampleForTests_staticJavaMethod);
+
+  jboolean ret =
+      env->CallStaticBooleanMethod(clazz,
+          call_context.base.method_id);
+  return ret;
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_packagePrivateJavaMethod(nullptr);
+static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "packagePrivateJavaMethod",
+          "()V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_packagePrivateJavaMethod);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_methodWithGenericParams(nullptr);
+static void Java_SampleForTests_methodWithGenericParams(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj, const base::android::JavaRef<jobject>& foo,
+    const base::android::JavaRef<jobject>& bar) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "methodWithGenericParams",
+          "(Ljava/util/Map;Ljava/util/LinkedList;)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_methodWithGenericParams);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id, foo.obj(), bar.obj());
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_Constructor(nullptr);
+static base::android::ScopedJavaLocalRef<jobject> Java_SampleForTests_Constructor(JNIEnv* env,
+    JniIntWrapper foo,
+    JniIntWrapper bar) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, clazz,
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "<init>",
+          "(II)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_Constructor);
+
+  jobject ret =
+      env->NewObject(clazz,
+          call_context.base.method_id, as_jint(foo), as_jint(bar));
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException(nullptr);
+static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+
+  jni_generator::JniJavaCallContextUnchecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "methodThatThrowsException",
+          "()V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.method_id);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_javaMethodWithAnnotatedParam(nullptr);
+static void Java_SampleForTests_javaMethodWithAnnotatedParam(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj, JniIntWrapper foo,
+    JniIntWrapper bar,
+    JniIntWrapper baz,
+    JniIntWrapper bat) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "javaMethodWithAnnotatedParam",
+          "(IIII)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_javaMethodWithAnnotatedParam);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id, as_jint(foo), as_jint(bar), as_jint(baz), as_jint(bat));
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_create(nullptr);
+static base::android::ScopedJavaLocalRef<jobject> Java_InnerStructA_create(JNIEnv* env, jlong l,
+    JniIntWrapper i,
+    const base::android::JavaRef<jstring>& s) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env);
+  CHECK_CLAZZ(env, clazz,
+      org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env), NULL);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_STATIC>(
+          env,
+          clazz,
+          "create",
+          "(JILjava/lang/String;)Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_create);
+
+  jobject ret =
+      env->CallStaticObjectMethod(clazz,
+          call_context.base.method_id, l, as_jint(i), s.obj());
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_addStructA(nullptr);
+static void Java_SampleForTests_addStructA(JNIEnv* env, const base::android::JavaRef<jobject>& obj,
+    const base::android::JavaRef<jobject>& a) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "addStructA",
+          "(Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;)V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_addStructA);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id, a.obj());
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_iterateAndDoSomething(nullptr);
+static void Java_SampleForTests_iterateAndDoSomething(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "iterateAndDoSomething",
+          "()V",
+          &g_org_chromium_example_jni_1generator_SampleForTests_iterateAndDoSomething);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getKey(nullptr);
+static jlong Java_InnerStructB_getKey(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), 0);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "getKey",
+          "()J",
+          &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getKey);
+
+  jlong ret =
+      env->CallLongMethod(obj.obj(),
+          call_context.base.method_id);
+  return ret;
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getValue(nullptr);
+static base::android::ScopedJavaLocalRef<jstring> Java_InnerStructB_getValue(JNIEnv* env, const
+    base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), NULL);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "getValue",
+          "()Ljava/lang/String;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getValue);
+
+  jstring ret =
+      static_cast<jstring>(env->CallObjectMethod(obj.obj(),
+          call_context.base.method_id));
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_getInnerInterface(nullptr);
+static base::android::ScopedJavaLocalRef<jobject> Java_SampleForTests_getInnerInterface(JNIEnv* env)
+    {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, clazz,
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_STATIC>(
+          env,
+          clazz,
+          "getInnerInterface",
+          "()Lorg/chromium/example/jni_generator/SampleForTests$InnerInterface;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_getInnerInterface);
+
+  jobject ret =
+      env->CallStaticObjectMethod(clazz,
+          call_context.base.method_id);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static std::atomic<jmethodID>
+    g_org_chromium_example_jni_1generator_SampleForTests_getInnerEnum(nullptr);
+static base::android::ScopedJavaLocalRef<jobject> Java_SampleForTests_getInnerEnum(JNIEnv* env) {
+  jclass clazz = org_chromium_example_jni_1generator_SampleForTests_clazz(env);
+  CHECK_CLAZZ(env, clazz,
+      org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_STATIC>(
+          env,
+          clazz,
+          "getInnerEnum",
+          "()Lorg/chromium/example/jni_generator/SampleForTests$InnerEnum;",
+          &g_org_chromium_example_jni_1generator_SampleForTests_getInnerEnum);
+
+  jobject ret =
+      env->CallStaticObjectMethod(clazz,
+          call_context.base.method_id);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+}  // namespace android
+}  // namespace base
+
+#endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 6de4ea7..743e694 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -363,6 +363,7 @@
         'Ljava/lang/Object',
         'Ljava/lang/String',
         'Ljava/lang/Class',
+        'Ljava/lang/ClassLoader',
         'Ljava/lang/CharSequence',
         'Ljava/lang/Runnable',
         'Ljava/lang/Throwable',
@@ -969,10 +970,12 @@
 class HeaderFileGeneratorHelper(object):
   """Include helper methods for header generators."""
 
-  def __init__(self, class_name, fully_qualified_class, use_proxy_hash):
+  def __init__(self, class_name, fully_qualified_class, use_proxy_hash,
+               split_name):
     self.class_name = class_name
     self.fully_qualified_class = fully_qualified_class
     self.use_proxy_hash = use_proxy_hash
+    self.split_name = split_name
 
   def GetStubName(self, native):
     """Return the name of the stub function for this native method.
@@ -1043,7 +1046,7 @@
 #define ${JAVA_CLASS}_clazz_defined
 inline jclass ${JAVA_CLASS}_clazz(JNIEnv* env) {
   return base::android::LazyGetClass(env, kClassPath_${JAVA_CLASS}, \
-&g_${JAVA_CLASS}_clazz);
+${MAYBE_SPLIT_NAME_ARG}&g_${JAVA_CLASS}_clazz);
 }
 #endif
 """
@@ -1059,7 +1062,10 @@
 
     for full_clazz in classes.values():
       values = {
-          'JAVA_CLASS': EscapeClassName(full_clazz),
+          'JAVA_CLASS':
+          EscapeClassName(full_clazz),
+          'MAYBE_SPLIT_NAME_ARG':
+          (('"%s", ' % self.split_name) if self.split_name else '')
       }
       # Since all proxy methods use the same class, defining this in every
       # header file would result in duplicated extern initializations.
@@ -1083,8 +1089,10 @@
     self.constant_fields = constant_fields
     self.jni_params = jni_params
     self.options = options
-    self.helper = HeaderFileGeneratorHelper(
-        self.class_name, fully_qualified_class, self.options.use_proxy_hash)
+    self.helper = HeaderFileGeneratorHelper(self.class_name,
+                                            fully_qualified_class,
+                                            self.options.use_proxy_hash,
+                                            self.options.split_name)
 
   def GetContent(self):
     """Returns the content of the JNI binding file."""
@@ -1578,6 +1586,9 @@
       action='store_true',
       help='Hashes the native declaration of methods used '
       'in @JniNatives interface.')
+  parser.add_argument(
+      '--split_name',
+      help='Split name that the Java classes should be loaded from.')
   args = parser.parse_args()
   input_files = args.input_files
   output_files = args.output_files
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index fbaf9fb..934dea9e 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -52,6 +52,7 @@
     self.enable_tracing = False
     self.use_proxy_hash = False
     self.always_mangle = False
+    self.split_name = None
 
 
 class BaseTest(unittest.TestCase):
@@ -1264,6 +1265,15 @@
                                                     TestOptions())
     self.AssertGoldenTextEquals(jni_from_java.GetContent())
 
+  def testSplitNameExample(self):
+    opts = TestOptions()
+    opts.split_name = "sample"
+    generated_text = self._CreateJniHeaderFromFile(
+        os.path.join(_JAVA_SRC_DIR, 'SampleForTests.java'),
+        'org/chromium/example/jni_generator/SampleForTests', opts)
+    self.AssertGoldenTextEquals(
+        generated_text, golden_file='SampleForTestsWithSplit_jni.golden')
+
 
 class ProxyTestGenerator(BaseTest):
 
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
index 7665671..66fa71cf 100755
--- a/base/android/jni_generator/jni_registration_generator.py
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -326,7 +326,7 @@
     self.class_name = self.fully_qualified_class.split('/')[-1]
     self.main_dex = main_dex
     self.helper = jni_generator.HeaderFileGeneratorHelper(
-        self.class_name, fully_qualified_class, use_proxy_hash)
+        self.class_name, fully_qualified_class, use_proxy_hash, None)
     self.use_proxy_hash = use_proxy_hash
     self.registration_dict = None
 
diff --git a/base/android/jni_utils.cc b/base/android/jni_utils.cc
index 7ca8a64c..ebcd1e1 100644
--- a/base/android/jni_utils.cc
+++ b/base/android/jni_utils.cc
@@ -4,6 +4,7 @@
 
 #include "base/android/jni_utils.h"
 
+#include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 
 #include "base/base_jni_headers/JNIUtils_jni.h"
@@ -12,7 +13,14 @@
 namespace android {
 
 ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env) {
-  return Java_JNIUtils_getClassLoader(env);
+  return Java_JNIUtils_getSplitClassLoader(env,
+                                           ConvertUTF8ToJavaString(env, ""));
+}
+
+ScopedJavaLocalRef<jobject> GetSplitClassLoader(JNIEnv* env,
+                                                const std::string& split_name) {
+  return Java_JNIUtils_getSplitClassLoader(
+      env, ConvertUTF8ToJavaString(env, split_name));
 }
 
 bool IsSelectiveJniRegistrationEnabled(JNIEnv* env) {
diff --git a/base/android/jni_utils.h b/base/android/jni_utils.h
index c626ba4..d5e08e16 100644
--- a/base/android/jni_utils.h
+++ b/base/android/jni_utils.h
@@ -18,6 +18,12 @@
 // via JNI from Java.
 BASE_EXPORT ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env);
 
+// Gets a ClassLoader instance which can load Java classes from the specified
+// split.
+BASE_EXPORT ScopedJavaLocalRef<jobject> GetSplitClassLoader(
+    JNIEnv* env,
+    const std::string& split_name);
+
 // Returns true if the current process permits selective JNI registration.
 BASE_EXPORT bool IsSelectiveJniRegistrationEnabled(JNIEnv* env);
 
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
index 36bc0929..351a079 100644
--- a/base/files/important_file_writer.cc
+++ b/base/files/important_file_writer.cc
@@ -371,8 +371,15 @@
 
 void ImportantFileWriter::DoScheduledWrite() {
   DCHECK(serializer_);
-  std::unique_ptr<std::string> data(new std::string);
+  auto data = std::make_unique<std::string>();
+
+  // Pre-allocate previously needed memory plus 1kB for potential growth of
+  // data. Reduces the number of memory allocations to grow |data| step by step
+  // from tiny to very large.
+  data->reserve(previous_data_size_ + 1024);
+
   if (serializer_->SerializeData(data.get())) {
+    previous_data_size_ = data->size();
     WriteNow(std::move(data));
   } else {
     DLOG(WARNING) << "failed to serialize data to be saved in "
diff --git a/base/files/important_file_writer.h b/base/files/important_file_writer.h
index 1accf1d8..6358d5b 100644
--- a/base/files/important_file_writer.h
+++ b/base/files/important_file_writer.h
@@ -117,6 +117,10 @@
   // Overrides the timer to use for scheduling writes with |timer_override|.
   void SetTimerForTesting(OneShotTimer* timer_override);
 
+#if defined(UNIT_TEST)
+  size_t previous_data_size() const { return previous_data_size_; }
+#endif
+
  private:
   const OneShotTimer& timer() const {
     return timer_override_ ? *timer_override_ : timer_;
@@ -169,6 +173,11 @@
   // Custom histogram suffix.
   const std::string histogram_suffix_;
 
+  // Memorizes the amount of data written on the previous write. This helps
+  // preallocating memory for the data serialization. It is only used for
+  // scheduled writes.
+  size_t previous_data_size_ = 0;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   WeakPtrFactory<ImportantFileWriter> weak_factory_{this};
diff --git a/base/files/important_file_writer_unittest.cc b/base/files/important_file_writer_unittest.cc
index baa1d5d..00dcadb 100644
--- a/base/files/important_file_writer_unittest.cc
+++ b/base/files/important_file_writer_unittest.cc
@@ -234,6 +234,7 @@
   MockOneShotTimer timer;
   ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get(),
                              kCommitInterval);
+  EXPECT_EQ(0u, writer.previous_data_size());
   writer.SetTimerForTesting(&timer);
   EXPECT_FALSE(writer.HasPendingWrite());
   DataSerializer serializer("foo");
@@ -247,6 +248,7 @@
   RunLoop().RunUntilIdle();
   ASSERT_TRUE(PathExists(writer.path()));
   EXPECT_EQ("foo", GetFileContent(writer.path()));
+  EXPECT_EQ(3u, writer.previous_data_size());
 }
 
 TEST_F(ImportantFileWriterTest, DoScheduledWrite) {
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index 12986dc..ff2394b 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -236,10 +236,11 @@
                         .SetRandomisedSamplingEnabled(false)
                         .SetTickClock(mock_tick_clock())
                         .Build();
-    sequence_manager_ = SequenceManagerForTest::Create(
+    auto thread_controller =
         std::make_unique<ThreadControllerWithMessagePumpImpl>(std::move(pump),
-                                                              settings),
-        std::move(settings));
+                                                              settings);
+    sequence_manager_ = SequenceManagerForTest::Create(
+        std::move(thread_controller), std::move(settings));
     sequence_manager_->SetDefaultTaskRunner(MakeRefCounted<NullTaskRunner>());
 
     // The SequenceManager constructor calls Now() once for setting up
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseActivityTestRule.java b/base/test/android/javatests/src/org/chromium/base/test/BaseActivityTestRule.java
new file mode 100644
index 0000000..af570fb
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseActivityTestRule.java
@@ -0,0 +1,94 @@
+// Copyright 2020 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.
+
+package org.chromium.base.test;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.test.runner.lifecycle.Stage;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.test.util.ApplicationTestUtils;
+
+/**
+ * A replacement for ActivityTestRule, designed for use in Chromium. This implementation supports
+ * launching the target activity through a launcher or redirect from another Activity.
+ *
+ * @param <T> The type of Activity this Rule will use.
+ */
+public class BaseActivityTestRule<T extends Activity> implements TestRule {
+    private static final String TAG = "BaseActivityTestRule";
+
+    private final Class<T> mActivityClass;
+    private boolean mFinishActivity = true;
+    private T mActivity;
+
+    /**
+     * @param activityClass The Class of the Activity the TestRule will use.
+     */
+    public BaseActivityTestRule(Class<T> activityClass) {
+        mActivityClass = activityClass;
+    }
+
+    @Override
+    public Statement apply(final Statement base, final Description desc) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                base.evaluate();
+                if (mFinishActivity && mActivity != null) {
+                    ApplicationTestUtils.finishActivity(mActivity);
+                }
+            }
+        };
+    }
+
+    /**
+     * @param finishActivity Whether to finish the Activity between tests. This is only meaningful
+     *     in the context of {@link Batch} tests. Non-batched tests will always finish Activities
+     *     between tests.
+     */
+    public void setFinishActivity(boolean finishActivity) {
+        mFinishActivity = finishActivity;
+    }
+
+    /**
+     * @return The activity under test.
+     */
+    public T getActivity() {
+        return mActivity;
+    }
+
+    /**
+     * Set the Activity to be used by this TestRule.
+     */
+    public void setActivity(T activity) {
+        mActivity = activity;
+    }
+
+    /**
+     * Launches the Activity under test using the provided intent.
+     */
+    public void launchActivity(@NonNull Intent startIntent) {
+        String packageName = ContextUtils.getApplicationContext().getPackageName();
+        Assert.assertTrue(TextUtils.equals(startIntent.getPackage(), packageName)
+                || TextUtils.equals(startIntent.getComponent().getPackageName(), packageName));
+
+        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        Log.d(TAG, String.format("Launching activity %s", mActivityClass.getName()));
+
+        mActivity = ApplicationTestUtils.waitForActivityWithClass(mActivityClass, Stage.CREATED,
+                () -> ContextUtils.getApplicationContext().startActivity(startIntent));
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/ApplicationTestUtils.java b/base/test/android/javatests/src/org/chromium/base/test/util/ApplicationTestUtils.java
index 799c803..c044cb4 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/ApplicationTestUtils.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/ApplicationTestUtils.java
@@ -5,6 +5,9 @@
 package org.chromium.base.test.util;
 
 import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.provider.Settings;
 import android.support.test.runner.lifecycle.ActivityLifecycleCallback;
 import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
 import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
@@ -26,9 +29,16 @@
 
     /** Waits until the given activity transitions to the given state. */
     public static void waitForActivityState(Activity activity, Stage stage) {
-        CriteriaHelper.pollUiThread(() -> {
-            return sMonitor.getLifecycleStageOf(activity) == stage;
-        }, ScalableTimeout.scaleTimeout(10000), CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        waitForActivityState(null, activity, stage);
+    }
+
+    /** Waits until the given activity transitions to the given state. */
+    public static void waitForActivityState(String failureReason, Activity activity, Stage stage) {
+        CriteriaHelper.pollUiThread(
+                ()
+                        -> { return sMonitor.getLifecycleStageOf(activity) == stage; },
+                failureReason, ScalableTimeout.scaleTimeout(10000),
+                CriteriaHelper.DEFAULT_POLLING_INTERVAL);
     }
 
     /** Finishes the given activity and waits for its onDestroy() to be called. */
@@ -38,7 +48,24 @@
                 activity.finish();
             }
         });
-        waitForActivityState(activity, Stage.DESTROYED);
+        try {
+            waitForActivityState(
+                    "Failed to finish the Activity. Did you start a second Activity and not finish"
+                            + " it?",
+                    activity, Stage.DESTROYED);
+        } catch (Throwable e) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) throw e;
+
+            // On L, there's a framework bug where Activities sometimes just don't get finished
+            // unless you start another Activity.
+            Intent intent = new Intent(Settings.ACTION_SETTINGS);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity.startActivity(intent);
+            waitForActivityState(
+                    "Failed to finish the Activity. Did you start a second Activity and not finish"
+                            + " it?",
+                    activity, Stage.DESTROYED);
+        }
     }
 
     /**
@@ -48,11 +75,25 @@
      * @return The newly created Activity.
      */
     public static <T extends Activity> T recreateActivity(T activity) {
-        final Class<?> activityClass = activity.getClass();
+        return waitForActivityWithClass(
+                activity.getClass(), Stage.RESUMED, () -> activity.recreate());
+    }
+
+    /**
+     * Waits for an activity of the specified class to reach the specified Activity {@link Stage},
+     * triggered by running the provided trigger.
+     *
+     * @param activityClass The class type to wait for.
+     * @param state The Activity {@link Stage} to wait for an activity of the right class type to
+     * reach.
+     * @param trigger The Runnable that will trigger the state change to wait for.
+     */
+    public static <T extends Activity> T waitForActivityWithClass(
+            Class<? extends Activity> activityClass, Stage stage, Runnable trigger) {
         final CallbackHelper activityCallback = new CallbackHelper();
         final AtomicReference<T> activityRef = new AtomicReference<>();
-        ActivityLifecycleCallback stateListener = (Activity newActivity, Stage stage) -> {
-            if (stage == Stage.RESUMED) {
+        ActivityLifecycleCallback stateListener = (Activity newActivity, Stage newStage) -> {
+            if (newStage == stage) {
                 if (!activityClass.isAssignableFrom(newActivity.getClass())) return;
 
                 activityRef.set((T) newActivity);
@@ -62,8 +103,8 @@
         sMonitor.addLifecycleCallback(stateListener);
 
         try {
-            ThreadUtils.runOnUiThreadBlocking(() -> activity.recreate());
-            activityCallback.waitForCallback("Activity did not start as expected", 0);
+            ThreadUtils.runOnUiThreadBlocking(() -> trigger.run());
+            activityCallback.waitForCallback("No Activity reached target state.", 0);
             T createdActivity = activityRef.get();
             Assert.assertNotNull("Activity reference is null.", createdActivity);
             return createdActivity;
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index c6cd584c..a453337 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -192,6 +192,9 @@
           args += [ "-n ${invoker.namespace}" ]
         }
       }
+      if (defined(invoker.split_name)) {
+        args += [ "--split_name=${invoker.split_name}" ]
+      }
 
       outputs = []
       foreach(_name, _input_names) {
diff --git a/build/config/mac/base_rules.gni b/build/config/mac/base_rules.gni
index 4678e95..d767ce82 100644
--- a/build/config/mac/base_rules.gni
+++ b/build/config/mac/base_rules.gni
@@ -24,7 +24,7 @@
 #     string, path to the converted plist, must be under $root_build_dir
 #
 #   format:
-#     string, the format to `plutil -convert` the plist to.
+#     string, the format to convert the plist to. Either "binary1" or "xml1".
 template("convert_plist") {
   assert(defined(invoker.source), "source must be defined for $target_name")
   assert(defined(invoker.output), "output must be defined for $target_name")
@@ -38,17 +38,12 @@
                              "deps",
                            ])
 
-    script = "//build/gn_run_binary.py"
+    script = "//build/config/mac/plist_util.py"
     sources = [ invoker.source ]
     outputs = [ invoker.output ]
-
-    # /usr/bin/plutil is present on all images of macOS. If at some point in the
-    # future we need to ship our own copy [e.g. for cross-compile], we can add a
-    # layer of indirection.
     args = [
-      "/usr/bin/plutil",
-      "-convert",
-      invoker.format,
+      "merge",
+      "--format=${invoker.format}",
       "-o",
       rebase_path(invoker.output, root_build_dir),
       rebase_path(invoker.source, root_build_dir),
diff --git a/build/config/mac/plist_util.py b/build/config/mac/plist_util.py
index 7c03780..e975083 100644
--- a/build/config/mac/plist_util.py
+++ b/build/config/mac/plist_util.py
@@ -109,15 +109,15 @@
 
 def SavePList(path, format, data):
   """Saves |data| as a Plist to |path| in the specified |format|."""
+  # The below does not replace the destination file but update it in place,
+  # so if more than one hardlink points to destination all of them will be
+  # modified. This is not what is expected, so delete destination file if
+  # it does exist.
+  if os.path.exists(path):
+    os.unlink(path)
   if sys.version_info.major == 2:
     fd, name = tempfile.mkstemp()
     try:
-      # "plutil" does not replace the destination file but update it in place,
-      # so if more than one hardlink points to destination all of them will be
-      # modified. This is not what is expected, so delete destination file if
-      # it does exist.
-      if os.path.exists(path):
-        os.unlink(path)
       with os.fdopen(fd, 'wb') as f:
         plistlib.writePlist(data, f)
       subprocess.check_call(['plutil', '-convert', format, '-o', path, name])
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index b1e0b1d..3208790d 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201125.2.1
+0.20201126.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b1e0b1d..3208790d 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201125.2.1
+0.20201126.0.1
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index 26b42ef..982e79da 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -45,9 +45,6 @@
     // http://crbug.com/244856
     "race:libpulsecommon*.so\n"
 
-    // http://crbug.com/246968
-    "race:webrtc::VideoCodingModuleImpl::RegisterPacketRequestCallback\n"
-
     // http://crbug.com/258479
     "race:g_trace_state\n"
 
@@ -73,10 +70,6 @@
     // http://crbug.com/328868
     "race:PR_Lock\n"
 
-    // http://crbug.com/348982
-    "race:cricket::P2PTransportChannel::OnConnectionDestroyed\n"
-    "race:cricket::P2PTransportChannel::AddConnection\n"
-
     // http://crbug.com/348984
     "race:sctp_express_handle_sack\n"
     "race:system_base_info\n"
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index aca2eaf..3f187ae 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -59,7 +59,6 @@
   int int_fields[6];
   gfx::Vector2dF offset;
   unsigned bitfields;
-  SkColor safe_opaque_background_color;
   void* debug_info;
 };
 
@@ -87,19 +86,13 @@
 Layer::Inputs::~Inputs() = default;
 
 Layer::LayerTreeInputs::LayerTreeInputs()
-    : mask_layer(nullptr),
-      opacity(1.f),
-      blend_mode(SkBlendMode::kSrcOver),
-      masks_to_bounds(false),
+    : masks_to_bounds(false),
       is_fast_rounded_corner(false),
       user_scrollable_horizontal(true),
       user_scrollable_vertical(true),
       trilinear_filtering(false),
       hide_layer_and_subtree(false),
-      scrollable(false),
-      backdrop_filter_quality(1.0f),
-      mirror_count(0),
-      corner_radii({0, 0, 0, 0}) {}
+      scrollable(false) {}
 
 Layer::LayerTreeInputs::~LayerTreeInputs() = default;
 
@@ -128,8 +121,7 @@
       needs_show_scrollbars_(false),
       has_transform_node_(false),
       has_clip_node_(false),
-      subtree_has_copy_request_(false),
-      safe_opaque_background_color_(0) {}
+      subtree_has_copy_request_(false) {}
 
 Layer::~Layer() {
   // Our parent should be holding a reference to us so there should be no
@@ -496,25 +488,41 @@
 
 void Layer::SetSafeOpaqueBackgroundColor(SkColor background_color) {
   DCHECK(IsPropertyChangeAllowed());
-  SkColor opaque_color = SkColorSetA(background_color, 255);
-  if (safe_opaque_background_color_ == opaque_color)
+  SkColor opaque_color = SkColorSetA(background_color, SK_AlphaOPAQUE);
+  auto& inputs = EnsureLayerTreeInputs();
+  if (inputs.safe_opaque_background_color == opaque_color)
     return;
-  safe_opaque_background_color_ = opaque_color;
+  inputs.safe_opaque_background_color = opaque_color;
   SetNeedsPushProperties();
 }
 
 SkColor Layer::SafeOpaqueBackgroundColor() const {
   if (contents_opaque()) {
-    // TODO(936906): We should uncomment this DCHECK, since the
-    // |safe_opaque_background_color_| could be transparent if it is never set
-    // (the default is 0). But to do that, one test needs to be fixed.
-    // DCHECK_EQ(SkColorGetA(safe_opaque_background_color_), SK_AlphaOPAQUE);
-    return safe_opaque_background_color_;
+    if (!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()) {
+      // In layer tree mode, PropertyTreeBuilder should have calculated the safe
+      // opaque background color and called SetSafeOpaqueBackgroundColor().
+      DCHECK(layer_tree_inputs());
+      DCHECK_EQ(SkColorGetA(layer_tree_inputs()->safe_opaque_background_color),
+                SK_AlphaOPAQUE);
+      return layer_tree_inputs()->safe_opaque_background_color;
+    }
+    // In layer list mode, the PropertyTreeBuilder algorithm doesn't apply
+    // because it depends on the layer tree hierarchy. Instead we use
+    // background_color() if it's not transparent, or layer_tree_host_'s
+    // background_color(), with the alpha channel forced to be opaque.
+    SkColor color = background_color() == SK_ColorTRANSPARENT
+                        ? layer_tree_host_->background_color()
+                        : background_color();
+    return SkColorSetA(color, SK_AlphaOPAQUE);
   }
-  SkColor color = background_color();
-  if (SkColorGetA(color) == 255)
-    color = SK_ColorTRANSPARENT;
-  return color;
+  if (SkColorGetA(background_color()) == SK_AlphaOPAQUE) {
+    // The layer is not opaque while the background color is, meaning that the
+    // background color doesn't cover the whole layer. Use SK_ColorTRANSPARENT
+    // to avoid intrusive checkerboard where the layer is not covered by the
+    // background color.
+    return SK_ColorTRANSPARENT;
+  }
+  return background_color();
 }
 
 void Layer::SetMasksToBounds(bool masks_to_bounds) {
@@ -1305,7 +1313,7 @@
   layer->SetElementId(inputs_.element_id);
   layer->SetHasTransformNode(has_transform_node_);
   layer->SetBackgroundColor(inputs_.background_color);
-  layer->SetSafeOpaqueBackgroundColor(safe_opaque_background_color_);
+  layer->SetSafeOpaqueBackgroundColor(SafeOpaqueBackgroundColor());
   layer->SetBounds(inputs_.bounds);
   layer->SetTransformTreeIndex(transform_tree_index());
   layer->SetEffectTreeIndex(effect_tree_index());
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 69915971..3f4189ef 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -151,22 +151,22 @@
   virtual void SetBackgroundColor(SkColor background_color);
   SkColor background_color() const { return inputs_.background_color; }
 
-  // Internal to property tree generation. Sets an opaque background color for
-  // the layer, to be used in place of the background_color() if the layer says
-  // contents_opaque() is true.
+  // For layer tree mode only. In layer list mode, client doesn't need to set
+  // it. Sets an opaque background color for the layer, to be used in place of
+  // the background_color() if the layer says contents_opaque() is true.
   void SetSafeOpaqueBackgroundColor(SkColor background_color);
-  // Returns a background color with opaque-ness equal to the value of
+
+  // Returns a background color with opaqueness equal to the value of
   // contents_opaque().
-  // If the layer says contents_opaque() is true, this returns the value set by
-  // SetSafeOpaqueBackgroundColor() which should be an opaque color. Otherwise,
-  // it returns something non-opaque. It prefers to return the
+  // If the layer says contents_opaque() is true, in layer tree mode, this
+  // returns the value set by SetSafeOpaqueBackgroundColor() which should be an
+  // opaque color, and in layer list mode, returns an opaque color calculated
+  // from background_color() and layer_tree_host()->background_clor().
+  // Otherwise, it returns something non-opaque. It prefers to return the
   // background_color(), but if the background_color() is opaque (and this layer
-  // claims to not be), then SK_ColorTRANSPARENT is returned.
+  // claims to not be), then SK_ColorTRANSPARENT is returned to avoid intrusive
+  // checkerboard where the layer is not covered by the background_color().
   SkColor SafeOpaqueBackgroundColor() const;
-  // For testing, return the actual stored value.
-  SkColor ActualSafeOpaqueBackgroundColorForTesting() const {
-    return safe_opaque_background_color_;
-  }
 
   // For layer tree mode only.
   // Set and get the position of this layer, relative to its parent. This is
@@ -862,10 +862,10 @@
 
     // If not null, points to one of child layers which is set as mask layer
     // by SetMaskLayer().
-    PictureLayer* mask_layer;
+    PictureLayer* mask_layer = nullptr;
 
-    float opacity;
-    SkBlendMode blend_mode;
+    float opacity = 1.0f;
+    SkBlendMode blend_mode = SkBlendMode::kSrcOver;
 
     bool masks_to_bounds : 1;
 
@@ -889,12 +889,14 @@
     gfx::Transform transform;
     gfx::Point3F transform_origin;
 
+    SkColor safe_opaque_background_color = SK_ColorTRANSPARENT;
+
     FilterOperations filters;
     FilterOperations backdrop_filters;
     base::Optional<gfx::RRectF> backdrop_filter_bounds;
-    float backdrop_filter_quality;
+    float backdrop_filter_quality = 1.0f;
 
-    int mirror_count;
+    int mirror_count = 0;
 
     gfx::ScrollOffset scroll_offset;
     // Size of the scroll container that this layer scrolls in.
@@ -945,8 +947,6 @@
   // This value is valid only when LayerTreeHost::has_copy_request() is true
   bool subtree_has_copy_request_ : 1;
 
-  SkColor safe_opaque_background_color_;
-
   std::unique_ptr<LayerDebugInfo> debug_info_;
 
   static constexpr gfx::Transform kIdentityTransform{};
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 460019a..920a3f5 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -555,20 +555,6 @@
   safe_opaque_background_color_ = background_color;
 }
 
-SkColor LayerImpl::SafeOpaqueBackgroundColor() const {
-  if (contents_opaque()) {
-    // TODO(936906): We should uncomment this DCHECK, since the
-    // |safe_opaque_background_color_| could be transparent if it is never set
-    // (the default is 0). But to do that, one test needs to be fixed.
-    // DCHECK_EQ(SkColorGetA(safe_opaque_background_color_), SK_AlphaOPAQUE);
-    return safe_opaque_background_color_;
-  }
-  SkColor color = background_color();
-  if (SkColorGetA(color) == 255)
-    color = SK_ColorTRANSPARENT;
-  return color;
-}
-
 void LayerImpl::SetContentsOpaque(bool opaque) {
   contents_opaque_ = opaque;
   contents_opaque_for_text_ = opaque;
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 6af8b62c..66c0b80 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -163,9 +163,12 @@
   void SetBackgroundColor(SkColor background_color);
   SkColor background_color() const { return background_color_; }
   void SetSafeOpaqueBackgroundColor(SkColor background_color);
-  // If contents_opaque(), return an opaque color else return a
-  // non-opaque color.  Tries to return background_color(), if possible.
-  SkColor SafeOpaqueBackgroundColor() const;
+  SkColor safe_opaque_background_color() const {
+    // Layer::SafeOpaqueBackgroundColor() should ensure this.
+    DCHECK_EQ(contents_opaque(),
+              SkColorGetA(safe_opaque_background_color_) == SK_AlphaOPAQUE);
+    return safe_opaque_background_color_;
+  }
 
   // See Layer::SetContentsOpaque() and SetContentsOpaqueForText() for the
   // relationship between the two flags.
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index c26d781..2f6a867 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -511,7 +511,7 @@
 
     if (!has_draw_quad) {
       // Checkerboard.
-      SkColor color = SafeOpaqueBackgroundColor();
+      SkColor color = safe_opaque_background_color();
       if (ShowDebugBorders(DebugBorderType::LAYER)) {
         // Fill the whole tile with the missing tile color.
         color = DebugColors::DefaultCheckerboardColor();
diff --git a/cc/test/test_layer_tree_host_base.cc b/cc/test/test_layer_tree_host_base.cc
index 96b1b7a..26b5ef5 100644
--- a/cc/test/test_layer_tree_host_base.cc
+++ b/cc/test/test_layer_tree_host_base.cc
@@ -123,6 +123,7 @@
     pending_layer_->SetDrawsContent(true);
     // LCD-text tests require the layer to be initially opaque.
     pending_layer_->SetContentsOpaque(true);
+    pending_layer_->SetSafeOpaqueBackgroundColor(SK_ColorWHITE);
 
     pending_tree->SetElementIdsForTesting();
     SetupRootProperties(pending_root);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java
index 80b4009..2717c72 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java
@@ -23,10 +23,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.verify;
 
-import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntil;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
 
-import android.app.Activity;
+import android.support.test.runner.lifecycle.Stage;
 import android.text.Spanned;
 import android.text.style.ClickableSpan;
 import android.view.View;
@@ -43,10 +42,10 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import org.chromium.base.ActivityState;
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.Callback;
+import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
@@ -57,7 +56,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -295,8 +293,11 @@
                                     spannedMessage.getSpanEnd(spans[0]))
                             .toString());
         });
-        spans[0].onClick(termsMessage);
-        waitUntil(() -> getOpenedUrlSpec().equals(expectedTermsUrl));
+        CustomTabActivity activity = ApplicationTestUtils.waitForActivityWithClass(
+                CustomTabActivity.class, Stage.RESUMED, () -> spans[0].onClick(termsMessage));
+        CriteriaHelper.pollUiThread(
+                () -> activity.getActivityTab().getUrlString().equals(expectedTermsUrl));
+        activity.finish();
     }
 
     @Test
@@ -356,12 +357,13 @@
                             .toString()
                             .replaceAll("\\s+", " "));
         });
-        spans[0].onClick(termsMessage);
-        waitUntil(()
-                          -> getOpenedUrlSpec().equals(
-                                  mActivity.getResources()
-                                          .getText(R.string.autofill_assistant_google_terms_url)
-                                          .toString()));
+        CustomTabActivity activity = ApplicationTestUtils.waitForActivityWithClass(
+                CustomTabActivity.class, Stage.RESUMED, () -> spans[0].onClick(termsMessage));
+        String url = mActivity.getResources()
+                             .getText(R.string.autofill_assistant_google_terms_url)
+                             .toString();
+        CriteriaHelper.pollUiThread(() -> activity.getActivityTab().getUrlString().equals(url));
+        activity.finish();
     }
 
     /** Trigger onboarding and wait until it is fully displayed. */
@@ -370,21 +372,4 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> coordinator.show(callback));
         waitUntilViewMatchesCondition(withId(R.id.button_init_ok), isCompletelyDisplayed());
     }
-
-    // Get the newly opened Activity (through CustomTabActivity.showInfoPage) that happens on
-    // terms click. Return the URL of the current tab on that activity.
-    private String getOpenedUrlSpec() {
-        for (Activity runningActivity : ApplicationStatus.getRunningActivities()) {
-            if (runningActivity instanceof CustomTabActivity
-                    && ApplicationStatus.getStateForActivity(runningActivity)
-                            == ActivityState.RESUMED) {
-                return ChromeTabUtils
-                        .getUrlOnUiThread(((CustomTabActivity) runningActivity)
-                                                  .getTabModelSelector()
-                                                  .getCurrentTab())
-                        .getSpec();
-            }
-        }
-        return "";
-    }
 }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
index 51b95d2..a3ae2c4 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -65,6 +65,7 @@
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone;
@@ -172,7 +173,7 @@
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_LAUNCHER);
         mActivityTestRule.prepareUrlIntent(intent, null);
-        mActivityTestRule.startActivityCompletely(intent);
+        mActivityTestRule.launchActivity(intent);
     }
 
     public static Bitmap createThumbnailBitmapAndWriteToFile(int tabId) {
@@ -271,7 +272,8 @@
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> mActivityTestRule.getActivity().startDelayedNativeInitializationForTests());
         CriteriaHelper.pollUiThread(
-                mActivityTestRule.getActivity().getTabModelSelector()::isTabStateInitialized);
+                mActivityTestRule.getActivity().getTabModelSelector()::isTabStateInitialized,
+                ScalableTimeout.scaleTimeout(10000L), CriteriaHelper.DEFAULT_POLLING_INTERVAL);
         Assert.assertTrue(LibraryLoader.getInstance().isInitialized());
     }
 
@@ -568,6 +570,7 @@
             RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(i);
             if (viewHolder != null) {
                 ImageView thumbnail = viewHolder.itemView.findViewById(R.id.tab_thumbnail);
+                if (!(thumbnail.getDrawable() instanceof BitmapDrawable)) return false;
                 BitmapDrawable drawable = (BitmapDrawable) thumbnail.getDrawable();
                 Bitmap bitmap = drawable.getBitmap();
                 if (bitmap == null) return false;
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceNoTabsTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceNoTabsTest.java
index 19a30c7..260b84d6 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceNoTabsTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceNoTabsTest.java
@@ -91,7 +91,7 @@
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_LAUNCHER);
         mActivityTestRule.prepareUrlIntent(intent, null);
-        mActivityTestRule.startActivityCompletely(intent);
+        mActivityTestRule.launchActivity(intent);
     }
 
     @Before
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index 9d29913..c7b94a6 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -811,7 +811,7 @@
     @CommandLineFlags.Add({BASE_PARAMS + "/single/exclude_mv_tiles/true"
             + "/show_last_active_tab_only/true/show_stack_tab_switcher/true"})
     public void
-    testShow_SingleAsHomepageV2_FromResumeShowStart() throws ExecutionException {
+    testShow_SingleAsHomepageV2_FromResumeShowStart() throws Exception {
         // clang-format on
         if (!mImmediateReturn) return;
 
@@ -831,7 +831,7 @@
         pressHome();
 
         // Simulates pressing Chrome's icon and launching Chrome from warm start.
-        startMainActivityFromLauncher();
+        mActivityTestRule.resumeMainActivityFromLauncher();
 
         CriteriaHelper.pollUiThread(
                 () -> cta.getLayoutManager() != null && cta.getLayoutManager().overviewVisible());
diff --git a/chrome/android/java/res/layout/custom_tabs_toolbar.xml b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
index fac032c..83c8fb6 100644
--- a/chrome/android/java/res/layout/custom_tabs_toolbar.xml
+++ b/chrome/android/java/res/layout/custom_tabs_toolbar.xml
@@ -14,15 +14,16 @@
         style="@style/ToolbarButton"
         android:layout_gravity="start|center_vertical"
         android:contentDescription="@string/close_tab" />
-    <org.chromium.ui.widget.ChromeImageButton
-        android:id="@+id/incognito_cct_logo_button"
+    <org.chromium.ui.widget.ChromeImageView
+        android:id="@+id/incognito_cct_logo_image_view"
         style="@style/LocationBarButton"
         android:layout_width="@dimen/location_bar_icon_width"
         android:layout_height="match_parent"
         android:layout_gravity="start"
         android:scaleType="center"
         app:srcCompat="@drawable/ic_incognito_cct_24dp"
-        android:visibility="gone" />
+        android:visibility="gone"
+        android:contentDescription="@string/accessibility_incognito_badge"/>
     <FrameLayout
         android:id="@+id/location_bar_frame_layout"
         android:layout_width="match_parent"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 5ab3865..84509ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -1097,7 +1097,7 @@
                             .setRedirectHandler(mRedirectHandler)
                             .setIsMainFrame(navigationParams.isMainFrame)
                             .build();
-            if (externalNavHandler.shouldOverrideUrlLoading(params)
+            if (externalNavHandler.shouldOverrideUrlLoading(params).getResultType()
                     != OverrideUrlLoadingResultType.NO_OVERRIDE) {
                 return false;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
index 24150a9e..18c6d73 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -30,6 +30,7 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -131,7 +132,7 @@
     private View mLiteStatusView;
     private View mLiteStatusSeparatorView;
     private TextView mTitleBar;
-    private ImageButton mIncognitoButton;
+    private ImageView mIncognitoImageView;
     private ImageButton mSecurityButton;
     private LinearLayout mCustomActionButtons;
     private ImageButton mCloseButton;
@@ -184,7 +185,7 @@
         mLocationBarFrameLayout = findViewById(R.id.location_bar_frame_layout);
         mTitleUrlContainer = findViewById(R.id.title_url_container);
         mTitleUrlContainer.setOnLongClickListener(this);
-        mIncognitoButton = findViewById(R.id.incognito_cct_logo_button);
+        mIncognitoImageView = findViewById(R.id.incognito_cct_logo_image_view);
         mSecurityButton = findViewById(R.id.security_button);
         mCustomActionButtons = findViewById(R.id.action_buttons);
         mCloseButton = findViewById(R.id.close_button);
@@ -376,7 +377,7 @@
 
     private void updateToolbarLayoutMargin() {
         // We show the Incognito logo for Incognito CCT case
-        if (getToolbarDataProvider().isIncognito()) mIncognitoButton.setVisibility(VISIBLE);
+        if (getToolbarDataProvider().isIncognito()) mIncognitoImageView.setVisibility(VISIBLE);
 
         int startMargin = calculateStartMarginWhenCloseButtonVisibilityGone();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
index 01441c0e..9297716 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java
@@ -35,7 +35,7 @@
 import org.chromium.components.external_intents.ExternalNavigationDelegate;
 import org.chromium.components.external_intents.ExternalNavigationDelegate.StartActivityIfNeededResult;
 import org.chromium.components.external_intents.ExternalNavigationHandler;
-import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
+import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.components.external_intents.ExternalNavigationParams;
 import org.chromium.components.external_intents.RedirectHandler;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -149,14 +149,13 @@
     }
 
     @Override
-    public @OverrideUrlLoadingResultType int handleIncognitoIntentTargetingSelf(
+    public OverrideUrlLoadingResult handleIncognitoIntentTargetingSelf(
             final Intent intent, final String referrerUrl, final String fallbackUrl) {
         String primaryUrl = intent.getDataString();
         boolean isUrlLoadedInTheSameTab = ExternalNavigationHandler.loadUrlFromIntent(
                 referrerUrl, primaryUrl, fallbackUrl, this, false, true);
-        return (isUrlLoadedInTheSameTab)
-                ? OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB
-                : OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+        return (isUrlLoadedInTheSameTab) ? OverrideUrlLoadingResult.forClobberingTab()
+                                         : OverrideUrlLoadingResult.forExternalIntent();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java
index 3991bede..a1e05b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDataCache.java
@@ -45,8 +45,7 @@
  * should be provided by calling {@link #update(List)}
  */
 @MainThread
-public class ProfileDataCache implements ProfileDownloader.Observer, ProfileDataSource.Observer,
-                                         IdentityManager.Observer {
+public class ProfileDataCache implements ProfileDataSource.Observer, IdentityManager.Observer {
     /**
      * Observer to get notifications about changes in profile data.
      */
@@ -151,7 +150,7 @@
 
         for (int i = 0; i < accounts.size(); i++) {
             if (mCachedProfileData.get(accounts.get(i)) == null) {
-                ProfileDownloader.startFetchingAccountInfoFor(
+                ProfileDownloader.get().startFetchingAccountInfoFor(
                         mContext, accounts.get(i), mImageSize, true);
             }
         }
@@ -204,7 +203,7 @@
                 mProfileDataSource.addObserver(this);
                 updateCacheFromProfileDataSource();
             } else {
-                ProfileDownloader.addObserver(this);
+                ProfileDownloader.get().addObserver(this);
             }
             mIdentityManager.addObserver(this);
         }
@@ -221,7 +220,7 @@
             if (mProfileDataSource != null) {
                 mProfileDataSource.removeObserver(this);
             } else {
-                ProfileDownloader.removeObserver(this);
+                ProfileDownloader.get().removeObserver(this);
             }
             mIdentityManager.removeObserver(this);
         }
@@ -242,11 +241,9 @@
     }
 
     @Override
-    public void onProfileDownloaded(
-            String accountEmail, String fullName, String givenName, Bitmap bitmap) {
+    public void onProfileDataUpdated(ProfileDataSource.ProfileData profileData) {
         ThreadUtils.assertOnUiThread();
-        updateCachedProfileDataAndNotifyObservers(new DisplayableProfileData(
-                accountEmail, prepareAvatar(bitmap, accountEmail), fullName, givenName));
+        updateCachedProfileDataAndNotifyObservers(createDisplayableProfileData(profileData));
     }
 
     @Override
@@ -255,13 +252,18 @@
         ProfileDataSource.ProfileData profileData =
                 mProfileDataSource.getProfileDataForAccount(accountEmail);
         if (profileData == null) {
-            mCachedProfileData.remove(accountEmail);
-            notifyObservers(accountEmail);
+            removeProfileData(accountEmail);
         } else {
-            updateCachedProfileDataAndNotifyObservers(createDisplayableProfileData(profileData));
+            onProfileDataUpdated(profileData);
         }
     }
 
+    @Override
+    public void removeProfileData(String accountEmail) {
+        mCachedProfileData.remove(accountEmail);
+        notifyObservers(accountEmail);
+    }
+
     /**
      * Implements {@link IdentityManager.Observer}.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDownloader.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDownloader.java
index 2cb164a..804be944 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDownloader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ProfileDownloader.java
@@ -13,6 +13,7 @@
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.signin.AccountTrackerService;
+import org.chromium.components.signin.ProfileDataSource;
 
 import java.util.ArrayList;
 
@@ -21,38 +22,38 @@
  * The native ProfileDownloader requires its access to be in the UI thread.
  * See chrome/browser/profiles/profile_downloader.h/cc for more details.
  */
-public class ProfileDownloader {
-    private static final ObserverList<Observer> sObservers = new ObserverList<Observer>();
+class ProfileDownloader {
+    private static ProfileDownloader sInstance;
 
-    /**
-     * Interface for receiving notifications on account information updates.
-     */
-    public interface Observer {
-        /**
-         * Notifies that an account data in the profile has been updated.
-         * @param accountEmail An account email.
-         * @param fullName A full name.
-         * @param givenName A given name.
-         * @param bitmap A user picture.
-         */
-        void onProfileDownloaded(
-                String accountEmail, String fullName, String givenName, Bitmap bitmap);
+    private final ObserverList<ProfileDataSource.Observer> mObservers = new ObserverList<>();
+
+    static synchronized ProfileDownloader get() {
+        if (sInstance == null) {
+            sInstance = new ProfileDownloader();
+        }
+        return sInstance;
     }
 
     /**
      * Add an observer.
      * @param observer An observer.
      */
-    public static void addObserver(Observer observer) {
-        sObservers.addObserver(observer);
+    public void addObserver(ProfileDataSource.Observer observer) {
+        mObservers.addObserver(observer);
     }
 
     /**
      * Remove an observer.
      * @param observer An observer.
      */
-    public static void removeObserver(Observer observer) {
-        sObservers.removeObserver(observer);
+    public void removeObserver(ProfileDataSource.Observer observer) {
+        mObservers.removeObserver(observer);
+    }
+
+    private void notifyObservers(ProfileDataSource.ProfileData profileData) {
+        for (ProfileDataSource.Observer observer : mObservers) {
+            observer.onProfileDataUpdated(profileData);
+        }
     }
 
     /**
@@ -74,7 +75,7 @@
             mImageSidePixels = new ArrayList<>();
         }
 
-        public static PendingProfileDownloads get(Context context) {
+        static PendingProfileDownloads get(Context context) {
             ThreadUtils.assertOnUiThread();
             if (sPendingProfileDownloads == null) {
                 sPendingProfileDownloads = new PendingProfileDownloads();
@@ -120,7 +121,7 @@
      * @param accountEmail Account email to fetch the information for
      * @param imageSidePixels Request image side (in pixels)
      */
-    public static void startFetchingAccountInfoFor(
+    public void startFetchingAccountInfoFor(
             Context context, String accountEmail, int imageSidePixels, boolean isPreSignin) {
         ThreadUtils.assertOnUiThread();
         Profile profile = Profile.getLastUsedRegularProfile();
@@ -137,11 +138,10 @@
 
     @CalledByNative
     private static void onProfileDownloadSuccess(
-            String accountEmail, String fullName, String givenName, Bitmap bitmap) {
+            String accountEmail, String fullName, String givenName, Bitmap avatar) {
         ThreadUtils.assertOnUiThread();
-        for (Observer observer : sObservers) {
-            observer.onProfileDownloaded(accountEmail, fullName, givenName, bitmap);
-        }
+        ProfileDownloader.get().notifyObservers(
+                new ProfileDataSource.ProfileData(accountEmail, avatar, fullName, givenName));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index 2d53db6..55cc509 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -130,10 +130,10 @@
 
     @Override
     public boolean hasTab() {
-        // TODO(dtrainor, tedchoc): Remove the isInitialized() check when we no longer wait for
-        // TAB_CLOSED events to remove this tab.  Otherwise there is a chance we use this tab after
-        // {@link ChromeTab#destroy()} is called.
-        return mTab != null && mTab.isInitialized();
+        // TODO(https://crbug.com/1147131): Remove the isInitialized() and isDestroyed checks when
+        // we no longer wait for TAB_CLOSED events to remove this tab.  Otherwise there is a chance
+        // we use this tab after {@link Tab#destroy()} is called.
+        return mTab != null && mTab.isInitialized() && !mTab.isDestroyed();
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
index 279eb8d3..d2e0260 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/CopylessPasteTest.java
@@ -15,7 +15,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
@@ -72,7 +71,6 @@
     @After
     public void tearDown() throws Exception {
         AppIndexingUtil.setCallbackForTesting(null);
-        ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
     }
 
     private static class CopylessHelper extends CallbackHelper {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java
index cee2800..723653c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java
@@ -8,13 +8,11 @@
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
@@ -36,11 +34,6 @@
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
-    @After
-    public void tearDown() throws Exception {
-        ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
-    }
-
     /**
      * Verify launch the activity with URL.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java
index 2d586b71..8640a48 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java
@@ -11,8 +11,11 @@
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
+import static org.hamcrest.CoreMatchers.allOf;
 import static org.junit.Assert.assertEquals;
 
+import static org.chromium.chrome.test.util.ViewUtils.onViewWaiting;
+
 import android.support.test.InstrumentationRegistry;
 
 import androidx.test.filters.MediumTest;
@@ -81,7 +84,7 @@
     @MediumTest
     public void testPromoNotShownAfterBeingDismissed() {
         mBookmarkTestRule.showBookmarkManager(mSyncTestRule.getActivity());
-        onView(withId(R.id.signin_promo_view_container)).check(matches(isDisplayed()));
+        onViewWaiting(allOf(withId(R.id.signin_promo_view_container), isDisplayed()));
         onView(withId(R.id.signin_promo_close_button)).perform(click());
         onView(withId(R.id.signin_promo_view_container)).check(doesNotExist());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
index 645fad0..733c40d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -204,8 +204,9 @@
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws Exception {
         if (mTestServer != null) mTestServer.stopAndDestroyServer();
+        if (mBookmarkActivity != null) ApplicationTestUtils.finishActivity(mBookmarkActivity);
     }
 
     @AfterClass
@@ -283,7 +284,7 @@
         // Click the star button again to launch the edit activity.
         MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
                 mActivityTestRule.getActivity(), R.id.bookmark_this_page_id);
-        waitForEditActivity();
+        waitForEditActivity().finish();
     }
 
     @Test
@@ -314,8 +315,9 @@
             currentSnackbar.getController().onAction(null);
         });
 
-        waitForEditActivity();
+        BookmarkEditActivity activity = waitForEditActivity();
         SnackbarManager.setDurationForTesting(0);
+        activity.finish();
     }
 
     @Test
@@ -1731,12 +1733,13 @@
         RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
     }
 
-    private void waitForEditActivity() {
+    private BookmarkEditActivity waitForEditActivity() {
         CriteriaHelper.pollUiThread(() -> {
             Criteria.checkThat(ApplicationStatus.getLastTrackedFocusedActivity(),
                     IsInstanceOf.instanceOf(BookmarkEditActivity.class));
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        return (BookmarkEditActivity) ApplicationStatus.getLastTrackedFocusedActivity();
     }
 
     private ChromeTabbedActivity waitForTabbedActivity() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
index 6fc157e..c6d9b533 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragmentBasicTest.java
@@ -17,6 +17,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
@@ -44,14 +45,18 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class ClearBrowsingDataFragmentBasicTest {
-    @Rule
     public final ChromeTabbedActivityTestRule mActivityTestRule =
             new ChromeTabbedActivityTestRule();
-    @Rule
     public final SettingsActivityTestRule<ClearBrowsingDataFragmentBasic>
             mSettingsActivityTestRule =
                     new SettingsActivityTestRule<>(ClearBrowsingDataFragmentBasic.class);
 
+    // SettingsActivity has to be finished before the outer CTA can be finished or trying to finish
+    // CTA won't work.
+    @Rule
+    public final RuleChain mRuleChain =
+            RuleChain.outerRule(mActivityTestRule).around(mSettingsActivityTestRule);
+
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
index 1225843..193430a1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
@@ -196,7 +196,7 @@
     public void toolbarHasIncognitoLogo() throws Exception {
         Intent intent = createMinimalIncognitoCustomTabIntent();
         launchIncognitoCustomTab(intent);
-        Espresso.onView(withId(R.id.incognito_cct_logo_button)).check(matches(isDisplayed()));
+        Espresso.onView(withId(R.id.incognito_cct_logo_image_view)).check(matches(isDisplayed()));
     }
 
     @Test
@@ -205,7 +205,8 @@
     public void toolbarDoesNotHaveIncognitoLogo() throws Exception {
         Intent intent = createMinimalIncognitoCustomTabIntent();
         launchIncognitoCustomTab(intent);
-        Espresso.onView(withId(R.id.incognito_cct_logo_button)).check(matches(not(isDisplayed())));
+        Espresso.onView(withId(R.id.incognito_cct_logo_image_view))
+                .check(matches(not(isDisplayed())));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 8101eec..96b2323a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -707,7 +707,7 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
         filter.addDataScheme(Uri.parse(mTestServer.getURL("/")).getScheme());
         final ActivityMonitor monitor =
-                InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, false);
+                InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, true);
         openAppMenuAndAssertMenuShown();
         PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
             MenuItem item =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java
index ff48c21..e832ecf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java
@@ -4,29 +4,20 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import android.app.Activity;
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
 
-import org.hamcrest.Matchers;
+import androidx.annotation.NonNull;
+
 import org.junit.Assert;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.FeatureList;
-import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.Criteria;
-import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.ScalableTimeout;
-import org.chromium.chrome.browser.DeferredStartupHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabTestUtils;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 
 import java.util.Collections;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Custom ActivityTestRule for all instrumentation tests that require a {@link CustomTabActivity}.
@@ -54,29 +45,13 @@
     }
 
     @Override
-    public void startActivityCompletely(Intent intent) {
+    public void launchActivity(@NonNull Intent intent) {
         if (!FeatureList.hasTestFeatures()) {
             FeatureList.setTestFeatures(
                     Collections.singletonMap(ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT, true));
         }
         putCustomTabIdInIntent(intent);
-        int currentIntentId = getCustomTabIdFromIntent(intent);
-
-        Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
-        Assert.assertNotNull("Main activity did not start", activity);
-        CriteriaHelper.pollUiThread(() -> {
-            for (Activity runningActivity : ApplicationStatus.getRunningActivities()) {
-                if (runningActivity instanceof CustomTabActivity) {
-                    CustomTabActivity customTabActivity = (CustomTabActivity) runningActivity;
-                    final int customTabIdInActivity =
-                            getCustomTabIdFromIntent(customTabActivity.getIntent());
-                    if (currentIntentId != customTabIdInActivity) continue;
-                    setActivity(customTabActivity);
-                    return true;
-                }
-            }
-            return false;
-        });
+        super.launchActivity(intent);
     }
 
     /**
@@ -84,33 +59,8 @@
      * initialized.
      */
     public void startCustomTabActivityWithIntent(Intent intent) {
-        DeferredStartupHandler.setExpectingActivityStartupForTesting();
         startActivityCompletely(intent);
-        waitForActivityNativeInitializationComplete();
-        CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat(getActivity().getActivityTab(), Matchers.notNullValue());
-        });
         final Tab tab = getActivity().getActivityTab();
-        final CallbackHelper pageLoadFinishedHelper = new CallbackHelper();
-        tab.addObserver(new EmptyTabObserver() {
-            @Override
-            public void onLoadStopped(Tab tab, boolean toDifferentDocument) {
-                pageLoadFinishedHelper.notifyCalled();
-            }
-        });
-        try {
-            if (tab.isLoading()) {
-                pageLoadFinishedHelper.waitForCallback(
-                        0, 1, LONG_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            }
-        } catch (TimeoutException e) {
-            Assert.fail();
-        }
-        Assert.assertTrue("Deferred startup never completed",
-                DeferredStartupHandler.waitForDeferredStartupCompleteForTesting(
-                        STARTUP_TIMEOUT_MS));
-        Assert.assertNotNull(tab);
-        Assert.assertNotNull(tab.getView());
         Assert.assertTrue(TabTestUtils.isCustomTab(tab));
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabExternalNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabExternalNavigationTest.java
index 7a6fbb1a..a39d907 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabExternalNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabExternalNavigationTest.java
@@ -27,6 +27,7 @@
 import org.chromium.chrome.browser.tab.TabTestUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.external_intents.ExternalNavigationHandler;
+import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
 import org.chromium.components.external_intents.ExternalNavigationParams;
 import org.chromium.net.test.EmbeddedTestServer;
@@ -107,9 +108,9 @@
         final String testUrl = "customtab://customtabtest/intent";
         ExternalNavigationParams params = new ExternalNavigationParams.Builder(testUrl, false)
                 .build();
-        @OverrideUrlLoadingResultType
-        int result = mUrlHandler.shouldOverrideUrlLoading(params);
-        Assert.assertEquals(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT, result);
+        OverrideUrlLoadingResult result = mUrlHandler.shouldOverrideUrlLoading(params);
+        Assert.assertEquals(
+                OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT, result.getResultType());
         Assert.assertTrue("A dummy activity should have been started to handle the special url.",
                 mNavigationDelegate.hasExternalActivityStarted());
     }
@@ -124,9 +125,8 @@
         final String testUrl = "http://customtabtest.com";
         ExternalNavigationParams params = new ExternalNavigationParams.Builder(testUrl, false)
                 .build();
-        @OverrideUrlLoadingResultType
-        int result = mUrlHandler.shouldOverrideUrlLoading(params);
-        Assert.assertEquals(OverrideUrlLoadingResultType.NO_OVERRIDE, result);
+        OverrideUrlLoadingResult result = mUrlHandler.shouldOverrideUrlLoading(params);
+        Assert.assertEquals(OverrideUrlLoadingResultType.NO_OVERRIDE, result.getResultType());
         Assert.assertFalse("External activities should not be started to handle the url",
                 mNavigationDelegate.hasExternalActivityStarted());
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
index a07414a0..3821874 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
@@ -111,8 +111,7 @@
     @Test
     @Feature("CustomTabFromChrome")
     @LargeTest
-    public void
-    testIntentWithRedirectToApp() {
+    public void testIntentWithRedirectToApp() {
         final String redirectUrl = "https://maps.google.com/maps?q=1600+amphitheatre+parkway";
         final String initialUrl =
                 mServerRule.getServer().getURL("/chrome/test/data/android/redirect/js_redirect.html"
@@ -123,7 +122,7 @@
                         + Base64.encodeToString(
                                 ApiCompatibilityUtils.getBytesUtf8(redirectUrl), Base64.URL_SAFE));
 
-        mActivityRule.startActivityCompletely(getCustomTabFromChromeIntent(initialUrl, true));
+        mActivityRule.launchActivity(getCustomTabFromChromeIntent(initialUrl, true));
         mActivityRule.waitForActivityNativeInitializationComplete();
 
         final AtomicReference<InterceptNavigationDelegateImpl> navigationDelegate =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java
index 0899713..af9727f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutTest.java
@@ -9,12 +9,10 @@
 
 import androidx.test.filters.LargeTest;
 
-import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
@@ -37,11 +35,6 @@
     public DisplayCutoutTestRule mTestRule =
             new DisplayCutoutTestRule<ChromeActivity>(ChromeActivity.class);
 
-    @After
-    public void tearDown() throws Exception {
-        ApplicationTestUtils.finishActivity(mTestRule.getActivity());
-    }
-
     /**
      * Test that no safe area is applied when we have viewport fit auto
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
index 3dc82eb..cc76495e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
@@ -13,6 +13,7 @@
 import android.net.Uri;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.lifecycle.Stage;
 import android.text.TextUtils;
 import android.util.Base64;
 
@@ -28,10 +29,14 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.ScalableTimeout;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -424,24 +429,36 @@
 
     @Test
     @SmallTest
-    public void testRedirectionFromIntent() {
-        // Test cold-start.
+    public void testRedirectionFromIntentCold() throws Exception {
+        Context context = ContextUtils.getApplicationContext();
         Intent intent = new Intent(Intent.ACTION_VIEW,
                 Uri.parse(mTestServer.getURL(NAVIGATION_FROM_JAVA_REDIRECTION_PAGE)));
-        Context targetContext = InstrumentationRegistry.getTargetContext();
-        intent.setClassName(targetContext, ChromeLauncherActivity.class.getName());
+        intent.setClassName(context, ChromeLauncherActivity.class.getName());
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+
+        ChromeTabbedActivity activity = ApplicationTestUtils.waitForActivityWithClass(
+                ChromeTabbedActivity.class, Stage.CREATED, () -> context.startActivity(intent));
+        mActivityTestRule.setActivity(activity);
+
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat(mActivityMonitor.getHits(), Matchers.is(1));
+        }, ScalableTimeout.scaleTimeout(10000L), CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        ApplicationTestUtils.waitForActivityState(activity, Stage.STOPPED);
+    }
+
+    @Test
+    @SmallTest
+    public void testRedirectionFromIntentWarm() throws Exception {
+        Context context = ContextUtils.getApplicationContext();
+        mActivityTestRule.startMainActivityOnBlankPage();
+        Intent intent = new Intent(Intent.ACTION_VIEW,
+                Uri.parse(mTestServer.getURL(NAVIGATION_FROM_JAVA_REDIRECTION_PAGE)));
+        intent.setClassName(context, ChromeLauncherActivity.class.getName());
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(intent);
 
         CriteriaHelper.pollUiThread(
                 () -> Criteria.checkThat(mActivityMonitor.getHits(), Matchers.is(1)));
-
-        // Test warm start.
-        mActivityTestRule.startMainActivityOnBlankPage();
-        targetContext.startActivity(intent);
-
-        CriteriaHelper.pollUiThread(
-                () -> Criteria.checkThat(mActivityMonitor.getHits(), Matchers.is(2)));
     }
 
     @Test
@@ -489,7 +506,6 @@
         String originalUrl = mTestServer.getURL(NAVIGATION_TO_FILE_SCHEME_FROM_INTENT_URI);
         loadUrlAndWaitForIntentUrl(originalUrl, true, false, false, null, false, "null_scheme");
     }
-
     @Test
     @LargeTest
     public void testIntentURIWithEmptySchemeDoesNothing() throws TimeoutException {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
index 7934a16..3579486 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
@@ -16,6 +16,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ActivityState;
@@ -64,12 +65,16 @@
 
     private EmbeddedTestServer mTestServer;
 
-    @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-    @Rule
     public SettingsActivityTestRule<HomepageSettings> mSettingsActivityTestRule =
             new SettingsActivityTestRule<>(HomepageSettings.class);
 
+    // SettingsActivity has to be finished before the outer CTA can be finished or trying to finish
+    // CTA won't work.
+    @Rule
+    public final RuleChain mRuleChain =
+            RuleChain.outerRule(mActivityTestRule).around(mSettingsActivityTestRule);
+
     @Rule
     public HomepageTestRule mHomepageTestRule = new HomepageTestRule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java
index 703521dd..a273db8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/settings/HomepageSettingsFragmentTest.java
@@ -14,6 +14,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UserActionTester;
@@ -104,9 +105,8 @@
         Assert.assertNotNull("Custom URI radio button is null.", mCustomUriRadioButton);
     }
 
-    private void finishSettingsActivity() {
-        mTestRule.getActivity().finish();
-        mTestRule.waitTillActivityIsDestroyed();
+    private void finishSettingsActivity() throws Exception {
+        ApplicationTestUtils.finishActivity(mTestRule.getActivity());
     }
 
     @Test
@@ -394,7 +394,7 @@
     @Test
     @SmallTest
     @Feature({"Homepage"})
-    public void testCheckRadioButtons() {
+    public void testCheckRadioButtons() throws Exception {
         mHomepageTestRule.useCustomizedHomepageForTest(TEST_URL_FOO);
         launchSettingsActivity();
         LocationChangedCounter counter = new LocationChangedCounter();
@@ -446,7 +446,7 @@
     @Test
     @SmallTest
     @Feature({"Homepage"})
-    public void testChangeCustomized() {
+    public void testChangeCustomized() throws Exception {
         mHomepageTestRule.useChromeNTPForTest();
         launchSettingsActivity();
         LocationChangedCounter actionCounter = new LocationChangedCounter();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
index 5c0af74..98254843 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
@@ -218,7 +218,7 @@
             // mSlowPage will hang for 2 seconds before sending a response. It should be enough to
             // put Chrome in background before the page is committed.
             mTabbedActivityTestRule.prepareUrlIntent(intent, mSlowPage);
-            mTabbedActivityTestRule.startActivityCompletely(intent);
+            mTabbedActivityTestRule.launchActivity(intent);
 
             // Put Chrome in background before the page is committed.
             ChromeApplicationTestUtils.fireHomeScreenIntent(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java
index cdbf6b55..8c2cbab 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorControllerTest.java
@@ -250,6 +250,7 @@
 
         // Offline indicator should not be shown.
         checkOfflineIndicatorVisibility(downloadActivity, false);
+        downloadActivity.finish();
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiTest.java
index 4485b232..012b466 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentUiTest.java
@@ -13,6 +13,8 @@
 
 import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.ASSISTANT_VOICE_SEARCH_ENABLED;
 
+import android.support.test.runner.lifecycle.Stage;
+
 import androidx.test.filters.MediumTest;
 
 import org.junit.After;
@@ -26,6 +28,7 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.Callback;
+import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -33,6 +36,7 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -130,16 +134,19 @@
     public void testDialogInteractivity_LearnMoreButton() {
         showConsentUi();
 
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            ClickUtils.clickButton(mAssistantVoiceSearchConsentUi.getContentView().findViewById(
-                    R.id.avs_consent_ui_learn_more));
-            mBottomSheetTestSupport.endAllAnimations();
-        });
+        SettingsActivity activity = ApplicationTestUtils.waitForActivityWithClass(
+                SettingsActivity.class, Stage.RESUMED, () -> {
+                    ClickUtils.clickButton(
+                            mAssistantVoiceSearchConsentUi.getContentView().findViewById(
+                                    R.id.avs_consent_ui_learn_more));
+                    mBottomSheetTestSupport.endAllAnimations();
+                });
 
         onView(withText(mActivityTestRule.getActivity().getResources().getString(
                        R.string.avs_setting_category_title)))
                 .check(matches(isDisplayed()));
         Mockito.verify(mCallback, Mockito.times(0)).onResult(/* meaningless value */ true);
+        activity.finish();
     }
 
     @Test
@@ -156,4 +163,4 @@
         });
         Mockito.verify(mCallback, Mockito.timeout(1000)).onResult(false);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
index 61515dd..419276e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -108,7 +108,7 @@
 
     private void loadUrlAndOpenPageInfo(String url) {
         mActivityTestRule.loadUrl(url);
-        onView(withId(R.id.location_bar_status_icon)).perform(click());
+        onViewWaiting(allOf(withId(R.id.location_bar_status_icon), isDisplayed())).perform(click());
     }
 
     private View getPageInfoView() {
@@ -187,8 +187,6 @@
         // Choose a fixed, "random" port to create stable screenshots.
         mTestServerRule.setServerPort(424242);
         mTestServerRule.setServerUsesHttps(true);
-
-        mActivityTestRule.startMainActivityOnBlankPage();
     }
 
     @After
@@ -212,6 +210,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowOnInsecureHttpWebsite() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         mTestServerRule.setServerUsesHttps(false);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_HttpWebsite");
@@ -225,6 +224,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowOnSecureWebsite() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_SecureWebsite");
     }
@@ -238,6 +238,7 @@
     @DisabledTest(message = "https://crbug.com/1133770")
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowOnExpiredCertificateWebsite() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         mTestServerRule.setCertificateType(ServerCertificate.CERT_EXPIRED);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_ExpiredCertWebsite");
@@ -251,6 +252,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testChromePage() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndOpenPageInfo("chrome://version/");
         mRenderTestRule.render(getPageInfoView(), "PageInfo_InternalSite");
     }
@@ -264,6 +266,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowWithPermissions() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         mIsSystemLocationSettingEnabled = false;
         addSomePermissions(mTestServerRule.getServer().getURL("/"));
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
@@ -278,6 +281,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowWithCookieBlocking() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_CookieBlocking");
@@ -291,6 +295,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowWithPermissionsAndCookieBlocking() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         addSomePermissions(mTestServerRule.getServer().getURL("/"));
         setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
@@ -305,6 +310,7 @@
     @Feature({"RenderTest"})
     @Features.DisableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowWithDefaultSettingPermissions() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         addDefaultSettingPermissions(mTestServerRule.getServer().getURL("/"));
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_DefaultSettingPermissions");
@@ -318,6 +324,7 @@
     @Feature({"RenderTest"})
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowOnSecureWebsiteV2() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_SecureWebsiteV2");
     }
@@ -346,6 +353,7 @@
     @Feature({"RenderTest"})
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowConnectionInfoSubpage() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         onView(withId(R.id.page_info_connection_row)).perform(click());
         mRenderTestRule.render(getPageInfoView(), "PageInfo_ConnectionInfoSubpage");
@@ -359,6 +367,7 @@
     @Feature({"RenderTest"})
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowPermissionsSubpage() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         addSomePermissions(mTestServerRule.getServer().getURL("/"));
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         onView(withId(R.id.page_info_permissions_row)).perform(click());
@@ -373,6 +382,7 @@
     @Feature({"RenderTest"})
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowCookiesSubpage() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         onView(withId(R.id.page_info_cookies_row)).perform(click());
@@ -387,6 +397,7 @@
     @MediumTest
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testNoPermissionsSubpage() throws IOException {
+        mActivityTestRule.startMainActivityOnBlankPage();
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(sSimpleHtml));
         View dialog = (View) getPageInfoView().getParent();
         onView(withId(R.id.page_info_permissions_row))
@@ -401,6 +412,7 @@
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     @FlakyTest(message = "https://crbug.com/1147236")
     public void testClearCookiesOnSubpage() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
         mActivityTestRule.loadUrl(mTestServerRule.getServer().getURL(sSiteDataHtml));
         // Create cookies.
         expectHasCookies(false);
@@ -426,6 +438,7 @@
     @MediumTest
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testResetPermissionsOnSubpage() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
         mActivityTestRule.loadUrl(mTestServerRule.getServer().getURL(sSiteDataHtml));
         String url = mTestServerRule.getServer().getURL("/");
         // Create permissions.
@@ -453,6 +466,7 @@
     @MediumTest
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testPaintPreview() {
+        mActivityTestRule.startMainActivityOnBlankPage();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             final ChromeActivity activity = mActivityTestRule.getActivity();
             final Tab tab = activity.getActivityTab();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
index 3b5375ab2..2c90ae80 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
@@ -1245,6 +1245,7 @@
         @Override
         public void create(PaymentAppFactoryDelegate delegate) {
             Runnable createApp = () -> {
+                if (delegate.getParams().hasClosed()) return;
                 boolean canMakePayment =
                         delegate.getParams().getMethodData().containsKey(mAppMethodName);
                 delegate.onCanMakePaymentCalculated(canMakePayment);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index aa1888d..947221f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -15,6 +15,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
+import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 import android.view.View;
 
@@ -101,7 +102,7 @@
     private final SyncTestRule mSyncTestRule = new SyncTestRule();
 
     private final SettingsActivityTestRule<MainSettings> mSettingsActivityTestRule =
-            new SettingsActivityTestRule<>(MainSettings.class, true);
+            new SettingsActivityTestRule<>(MainSettings.class);
 
     // SettingsActivity needs to be initialized and destroyed with the mock
     // signin environment setup in SyncTestRule
@@ -133,6 +134,7 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        InstrumentationRegistry.getInstrumentation().setInTouchMode(true);
         PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck);
         SigninActivityLauncherImpl.setLauncherForTest(mMockSigninActivityLauncherImpl);
         DeveloperSettings.setIsEnabledForTests(true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java
index 516bc1db0..67834235 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetRenderTest.java
@@ -22,7 +22,6 @@
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.filters.MediumTest;
 
-import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -34,7 +33,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterizedRunner;
-import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -120,11 +118,6 @@
         mActivityTestRule.startMainActivityOnBlankPage();
     }
 
-    @After
-    public void tearDown() throws Exception {
-        ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
-    }
-
     @AfterClass
     public static void tearDownAfterActivityDestroyed() {
         ChromeNightModeTestUtils.tearDownNightModeAfterChromeActivityDestroyed();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/ManageSpaceActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/ManageSpaceActivityTest.java
index 16095c4..5383798 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/ManageSpaceActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/ManageSpaceActivityTest.java
@@ -100,7 +100,7 @@
     @Test
     @SmallTest
     public void testLaunchActivity() {
-        startManageSpaceActivity();
+        startManageSpaceActivity().finish();
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java
index b42cd0d..d0a1c47 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AccountManagementFragmentTest.java
@@ -18,6 +18,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
@@ -40,14 +41,18 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class AccountManagementFragmentTest {
-    @Rule
     public final SettingsActivityTestRule<AccountManagementFragment> mSettingsActivityTestRule =
             new SettingsActivityTestRule<>(AccountManagementFragment.class);
 
-    @Rule
     public final ChromeTabbedActivityTestRule mActivityTestRule =
             new ChromeTabbedActivityTestRule();
 
+    // SettingsActivity has to be finished before the outer CTA can be finished or trying to finish
+    // CTA won't work.
+    @Rule
+    public final RuleChain mRuleChain =
+            RuleChain.outerRule(mActivityTestRule).around(mSettingsActivityTestRule);
+
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java
index dc9d76a..9b38421 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/GoogleServicesSettingsTest.java
@@ -16,6 +16,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
@@ -51,13 +52,17 @@
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
-    @Rule
     public final ChromeTabbedActivityTestRule mActivityTestRule =
             new ChromeTabbedActivityTestRule();
 
-    @Rule
     public final SettingsActivityTestRule<GoogleServicesSettings> mSettingsActivityTestRule =
-            new SettingsActivityTestRule<>(GoogleServicesSettings.class, true);
+            new SettingsActivityTestRule<>(GoogleServicesSettings.class);
+
+    // SettingsActivity has to be finished before the outer CTA can be finished or trying to finish
+    // CTA won't work.
+    @Rule
+    public final RuleChain mRuleChain =
+            RuleChain.outerRule(mActivityTestRule).around(mSettingsActivityTestRule);
 
     @Before
     public void setUp() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index 9c90fe6..2acd095 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -90,7 +90,7 @@
     private final SyncTestRule mSyncTestRule = new SyncTestRule();
 
     private final SettingsActivityTestRule<ManageSyncSettings> mSettingsActivityTestRule =
-            new SettingsActivityTestRule<>(ManageSyncSettings.class, true);
+            new SettingsActivityTestRule<>(ManageSyncSettings.class);
 
     // SettingsActivity needs to be initialized and destroyed with the mock
     // signin environment setup in SyncTestRule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
index 76da0b0..559a75f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
@@ -62,7 +62,7 @@
 
     @Rule
     public final SettingsActivityTestRule<ManageSyncSettings> mSettingsActivityTestRule =
-            new SettingsActivityTestRule<>(ManageSyncSettings.class, true);
+            new SettingsActivityTestRule<>(ManageSyncSettings.class);
 
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
index cfd5d948..458cdfa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
@@ -4,6 +4,12 @@
 
 package org.chromium.chrome.browser.sync;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -13,6 +19,7 @@
 
 import androidx.annotation.Nullable;
 import androidx.preference.TwoStatePreference;
+import androidx.test.espresso.contrib.RecyclerViewActions;
 
 import org.junit.Assert;
 import org.junit.runner.Description;
@@ -31,6 +38,7 @@
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.chrome.test.util.browser.signin.SigninTestUtil;
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
+import org.chromium.components.browser_ui.widget.R;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.sync.ModelType;
 import org.chromium.components.sync.protocol.AutofillWalletSpecifics;
@@ -410,12 +418,9 @@
 
     // UI interaction convenience methods.
     public void togglePreference(final TwoStatePreference pref) {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            boolean newValue = !pref.isChecked();
-            pref.getOnPreferenceChangeListener().onPreferenceChange(pref, newValue);
-            pref.setChecked(newValue);
-        });
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        onView(withId(R.id.recycler_view))
+                .perform(RecyclerViewActions.actionOnItem(
+                        hasDescendant(withText(pref.getTitle().toString())), click()));
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java
index 41f76677..ba3b0bbc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateTest.java
@@ -76,10 +76,9 @@
         }
 
         @Override
-        public @OverrideUrlLoadingResultType int shouldOverrideUrlLoading(
-                ExternalNavigationParams params) {
+        public OverrideUrlLoadingResult shouldOverrideUrlLoading(ExternalNavigationParams params) {
             mExternalNavParamHistory.add(params);
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/LevelDBPersistedTabDataStorageFactoryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/LevelDBPersistedTabDataStorageFactoryTest.java
index 2611d9a5..c715a63 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/LevelDBPersistedTabDataStorageFactoryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/LevelDBPersistedTabDataStorageFactoryTest.java
@@ -64,9 +64,11 @@
         LevelDBPersistedTabDataStorage.setSkipNativeAssertionsForTesting(true);
     }
 
+    @UiThreadTest
     @SmallTest
     @Test
     public void testFactoryMethod() {
+        Profile realProfile = Profile.getLastUsedRegularProfile();
         LevelDBPersistedTabDataStorageFactory factory = new LevelDBPersistedTabDataStorageFactory();
         Profile.setLastUsedProfileForTesting(mProfile1);
         LevelDBPersistedTabDataStorage profile1Storage = factory.create();
@@ -76,6 +78,8 @@
         LevelDBPersistedTabDataStorage profile1StorageAgain = factory.create();
         Assert.assertEquals(profile1Storage, profile1StorageAgain);
         Assert.assertNotEquals(profile1Storage, profile2Storage);
+        // Restore the original profile so the Activity can shut down correctly.
+        Profile.setLastUsedProfileForTesting(realProfile);
     }
 
     @UiThreadTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
index 12907e88..cf1a94e0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tasks/ReturnToChromeTest.java
@@ -597,7 +597,7 @@
         intent.addCategory(Intent.CATEGORY_LAUNCHER);
         mActivityTestRule.prepareUrlIntent(intent, url);
         Assert.assertFalse(mInflated.get());
-        mActivityTestRule.startActivityCompletely(intent);
+        mActivityTestRule.launchActivity(intent);
         if (mUseInstantStart) {
             CriteriaHelper.pollUiThread(mInflated::get);
         } else {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
index 33af8329..fc5fc02 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappNavigationTest.java
@@ -263,7 +263,7 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
         filter.addDataScheme("https");
         final ActivityMonitor monitor =
-                InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, false);
+                InstrumentationRegistry.getInstrumentation().addMonitor(filter, null, true);
 
         RevampedContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
                 null /* activity to check for focus after click */,
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6facf83b..8e92d67 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2277,7 +2277,6 @@
     "//net",
     "//net:extras",
     "//ppapi/buildflags",
-    "//ppapi/host",
     "//printing",
     "//printing/buildflags",
     "//rlz/buildflags",
@@ -5988,15 +5987,12 @@
       "plugins/reload_plugin_infobar_delegate.h",
       "renderer_host/pepper/chrome_browser_pepper_host_factory.cc",
       "renderer_host/pepper/chrome_browser_pepper_host_factory.h",
-      "renderer_host/pepper/device_id_fetcher.cc",
-      "renderer_host/pepper/device_id_fetcher.h",
       "renderer_host/pepper/pepper_isolated_file_system_message_filter.cc",
       "renderer_host/pepper/pepper_isolated_file_system_message_filter.h",
     ]
     deps += [
       "//components/pdf/browser",
       "//media:media_buildflags",
-      "//ppapi/buildflags",
       "//ppapi/host",
       "//ppapi/proxy:ipc",
       "//services/device/public/mojom",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 774391e..fdbf07f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3685,6 +3685,10 @@
      flag_descriptions::kImeAssistAutocorrectName,
      flag_descriptions::kImeAssistAutocorrectDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kAssistAutoCorrect)},
+    {"enable-cros-ime-assist-multi-word",
+     flag_descriptions::kImeAssistMultiWordName,
+     flag_descriptions::kImeAssistMultiWordDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kAssistMultiWord)},
     {"enable-cros-ime-assist-personal-info",
      flag_descriptions::kImeAssistPersonalInfoName,
      flag_descriptions::kImeAssistPersonalInfoDescription, kOsCrOS,
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index ee626e5..2754381 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -77,7 +77,7 @@
 
 const int kContextualSearchMaxSelection = 1000;
 const char kXssiEscape[] = ")]}'\n";
-const char kDiscourseContextHeaderPrefix[] = "X-Additional-Discourse-Context: ";
+const char kDiscourseContextHeaderName[] = "X-Additional-Discourse-Context";
 const char kDoPreventPreloadValue[] = "1";
 
 const int kResponseCodeUninitialized = -1;
@@ -162,8 +162,7 @@
 
   // Populates the discourse context and adds it to the HTTP header of the
   // search term resolution request.
-  resource_request->headers.AddHeadersFromString(
-      GetDiscourseContext(*context_));
+  resource_request->headers.CopyFrom(GetDiscourseContext(*context_));
 
   // Disable cookies for this request.
   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
@@ -396,7 +395,7 @@
                                  selection_end);
 }
 
-std::string ContextualSearchDelegate::GetDiscourseContext(
+const net::HttpRequestHeaders ContextualSearchDelegate::GetDiscourseContext(
     const ContextualSearchContext& context) {
   discourse_context::ClientDiscourseContext proto;
   discourse_context::Display* display = proto.add_display();
@@ -419,7 +418,10 @@
   // The server memoizer expects a web-safe encoding.
   std::replace(encoded_context.begin(), encoded_context.end(), '+', '-');
   std::replace(encoded_context.begin(), encoded_context.end(), '/', '_');
-  return kDiscourseContextHeaderPrefix + encoded_context;
+
+  net::HttpRequestHeaders headers;
+  headers.SetHeader(kDiscourseContextHeaderName, encoded_context);
+  return headers;
 }
 
 // Decodes the given response from the search term resolution request and sets
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.h b/chrome/browser/android/contextualsearch/contextual_search_delegate.h
index 4137d68e..5b21dca4 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.h
@@ -17,6 +17,7 @@
 #include "base/values.h"
 #include "chrome/browser/android/contextualsearch/contextual_search_context.h"
 #include "chrome/browser/android/contextualsearch/resolved_search_term.h"
+#include "net/http/http_request_headers.h"
 
 namespace content {
 class WebContents;
@@ -127,7 +128,8 @@
       uint32_t end_offset);
 
   // Populates and returns the discourse context.
-  std::string GetDiscourseContext(const ContextualSearchContext& context);
+  const net::HttpRequestHeaders GetDiscourseContext(
+      const ContextualSearchContext& context);
 
   // Builds a Resolved Search Term by decoding the given JSON string.
   std::unique_ptr<ResolvedSearchTerm> GetResolvedSearchTermFromJson(
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc
index bd93553a..b730d63 100644
--- a/chrome/browser/autofill/autofill_browsertest.cc
+++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -117,11 +117,6 @@
     // Don't want Keychain coming up on Mac.
     test::DisableSystemServices(browser()->profile()->GetPrefs());
 
-    // Load the MatchingPattern definitions.
-    base::RunLoop run_loop;
-    field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure());
-    run_loop.Run();
-
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index b20f0ab..d3513ccd 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -302,11 +302,6 @@
         &AutofillInteractiveTestBase::HandleTestURL, base::Unretained(this)));
     embedded_test_server()->StartAcceptingConnections();
 
-    // Load the MatchingPattern definitions.
-    base::RunLoop run_loop;
-    field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure());
-    run_loop.Run();
-
     // By default, all SSL cert checks are valid. Can be overriden in tests if
     // needed.
     cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
diff --git a/chrome/browser/autofill/form_structure_browsertest.cc b/chrome/browser/autofill/form_structure_browsertest.cc
index ed3c554..5debbc4 100644
--- a/chrome/browser/autofill/form_structure_browsertest.cc
+++ b/chrome/browser/autofill/form_structure_browsertest.cc
@@ -195,11 +195,6 @@
 void FormStructureBrowserTest::SetUpOnMainThread() {
   InProcessBrowserTest::SetUpOnMainThread();
 
-  // Load the MatchingPattern definitions.
-  base::RunLoop run_loop;
-  field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure());
-  run_loop.Run();
-
   embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
       &FormStructureBrowserTest::HandleRequest, base::Unretained(this)));
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 9c711e3..2b457739 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -345,7 +345,6 @@
 #include "net/ssl/ssl_cert_request_info.h"
 #include "pdf/buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "ppapi/host/ppapi_host.h"
 #include "printing/buildflags/buildflags.h"
 #include "sandbox/policy/features.h"
 #include "sandbox/policy/sandbox_type.h"
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc
index 5eaca153..90f90cf 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc
@@ -30,13 +30,6 @@
 
 }  // namespace
 
-// static
-AutoEnrollmentCheckScreen* AutoEnrollmentCheckScreen::Get(
-    ScreenManager* manager) {
-  return static_cast<AutoEnrollmentCheckScreen*>(
-      manager->GetScreen(AutoEnrollmentCheckScreenView::kScreenId));
-}
-
 AutoEnrollmentCheckScreen::AutoEnrollmentCheckScreen(
     AutoEnrollmentCheckScreenView* view,
     ErrorScreen* error_screen,
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h
index ba5c9ff..fb29d7b 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h
@@ -21,7 +21,6 @@
 namespace chromeos {
 
 class ErrorScreensHistogramHelper;
-class ScreenManager;
 
 // Handles the control flow after OOBE auto-update completes to wait for the
 // enterprise auto-enrollment check that happens as part of OOBE. This includes
@@ -33,13 +32,13 @@
       public BaseScreen,
       public NetworkPortalDetector::Observer {
  public:
+  using TView = AutoEnrollmentCheckScreenView;
+
   AutoEnrollmentCheckScreen(AutoEnrollmentCheckScreenView* view,
                             ErrorScreen* error_screen,
                             const base::RepeatingClosure& exit_callback);
   ~AutoEnrollmentCheckScreen() override;
 
-  static AutoEnrollmentCheckScreen* Get(ScreenManager* manager);
-
   // Clears the cached state causing the forced enrollment check to be retried.
   void ClearState();
 
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
index 32aa1a5..181523ef 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -112,8 +112,8 @@
   AutoEnrollmentCheckScreen* auto_enrollment_screen() {
     EXPECT_NE(WizardController::default_controller(), nullptr);
     AutoEnrollmentCheckScreen* auto_enrollment_screen =
-        AutoEnrollmentCheckScreen::Get(
-            WizardController::default_controller()->screen_manager());
+        WizardController::default_controller()
+            ->GetScreen<AutoEnrollmentCheckScreen>();
     EXPECT_NE(auto_enrollment_screen, nullptr);
     return auto_enrollment_screen;
   }
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 75b4c77..9df1100 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -832,8 +832,7 @@
 }
 
 void WizardController::ShowAutoEnrollmentCheckScreen() {
-  AutoEnrollmentCheckScreen* screen =
-      AutoEnrollmentCheckScreen::Get(screen_manager());
+  AutoEnrollmentCheckScreen* screen = GetScreen<AutoEnrollmentCheckScreen>();
   if (retry_auto_enrollment_check_)
     screen->ClearState();
   screen->set_auto_enrollment_controller(GetAutoEnrollmentController());
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
new file mode 100644
index 0000000..bef0996
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
@@ -0,0 +1,188 @@
+// // Copyright 2020 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 <memory>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_test_utils.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_buffer.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
+#include "url/origin.h"
+
+namespace policy {
+
+namespace {
+
+constexpr char kClipboardText[] = "Hello World";
+
+}  // namespace
+
+class DataTransferDlpBrowserTest : public PolicyTest {
+ public:
+  DataTransferDlpBrowserTest() = default;
+};
+
+IN_PROC_BROWSER_TEST_F(DataTransferDlpBrowserTest, EmptyPolicy) {
+  PolicyMap policies;
+  policies.Set(key::kDataLeakPreventionRulesList, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, /*value=*/base::nullopt,
+               nullptr);
+  UpdateProviderPolicy(policies);
+
+  {
+    ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
+    writer.WriteText(base::UTF8ToUTF16(kClipboardText));
+  }
+  ui::DataTransferEndpoint data_dst(
+      url::Origin::Create(GURL("https://google.com")));
+  base::string16 result;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst, &result);
+  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText), result);
+}
+
+IN_PROC_BROWSER_TEST_F(DataTransferDlpBrowserTest, RestrictedUrl) {
+  const std::string kUrl1 = "https://mail.google.com";
+  const std::string kUrl2 = "https://docs.google.com";
+  const std::string kUrl3 = "https://example.com";
+
+  base::Value rules(base::Value::Type::LIST);
+
+  base::Value src_urls1(base::Value::Type::LIST);
+  src_urls1.Append(kUrl1);
+  base::Value dst_urls1(base::Value::Type::LIST);
+  dst_urls1.Append("*");
+  base::Value restrictions1(base::Value::Type::LIST);
+  restrictions1.Append(dlp_test_util::CreateRestrictionWithLevel(
+      dlp::kClipboardRestriction, dlp::kBlockLevel));
+  rules.Append(dlp_test_util::CreateRule(
+      "rule #1", "Block Gmail", std::move(src_urls1), std::move(dst_urls1),
+      /*dst_components=*/base::Value(base::Value::Type::LIST),
+      std::move(restrictions1)));
+
+  base::Value src_urls2(base::Value::Type::LIST);
+  src_urls2.Append(kUrl1);
+  base::Value dst_urls2(base::Value::Type::LIST);
+  dst_urls2.Append(kUrl2);
+  base::Value restrictions2(base::Value::Type::LIST);
+  restrictions2.Append(dlp_test_util::CreateRestrictionWithLevel(
+      dlp::kClipboardRestriction, dlp::kAllowLevel));
+  rules.Append(dlp_test_util::CreateRule(
+      "rule #2", "Allow Gmail for work purposes", std::move(src_urls2),
+      std::move(dst_urls2),
+      /*dst_components=*/base::Value(base::Value::Type::LIST),
+      std::move(restrictions2)));
+
+  PolicyMap policies;
+  policies.Set(key::kDataLeakPreventionRulesList, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(rules),
+               nullptr);
+  UpdateProviderPolicy(policies);
+
+  {
+    ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste,
+                                     std::make_unique<ui::DataTransferEndpoint>(
+                                         url::Origin::Create(GURL(kUrl1))));
+    writer.WriteText(base::UTF8ToUTF16(kClipboardText));
+  }
+  ui::DataTransferEndpoint data_dst1(url::Origin::Create(GURL(kUrl1)));
+  base::string16 result1;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst1, &result1);
+  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText), result1);
+
+  ui::DataTransferEndpoint data_dst2(url::Origin::Create(GURL(kUrl2)));
+  base::string16 result2;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst2, &result2);
+  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText), result2);
+
+  ui::DataTransferEndpoint data_dst3(url::Origin::Create(GURL(kUrl3)));
+  base::string16 result3;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst3, &result3);
+  EXPECT_EQ(base::string16(), result3);
+
+  {
+    ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste,
+                                     std::make_unique<ui::DataTransferEndpoint>(
+                                         url::Origin::Create(GURL(kUrl3))));
+    writer.WriteText(base::UTF8ToUTF16(kClipboardText));
+  }
+  ui::DataTransferEndpoint data_dst4(url::Origin::Create(GURL(kUrl1)));
+  base::string16 result4;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst1, &result4);
+  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText), result4);
+}
+
+IN_PROC_BROWSER_TEST_F(DataTransferDlpBrowserTest, RestrictedComponent) {
+  const std::string kUrl1 = "https://mail.google.com";
+
+  base::Value rules(base::Value::Type::LIST);
+
+  base::Value src_urls(base::Value::Type::LIST);
+  src_urls.Append(kUrl1);
+  base::Value dst_components(base::Value::Type::LIST);
+  dst_components.Append(dlp::kArc);
+  dst_components.Append(dlp::kPluginVm);
+  base::Value restrictions(base::Value::Type::LIST);
+  restrictions.Append(dlp_test_util::CreateRestrictionWithLevel(
+      dlp::kClipboardRestriction, dlp::kBlockLevel));
+  rules.Append(dlp_test_util::CreateRule(
+      "rule #1", "Block Gmail", std::move(src_urls),
+      /*dst_urls=*/base::Value(base::Value::Type::LIST),
+      std::move(dst_components), std::move(restrictions)));
+
+  PolicyMap policies;
+  policies.Set(key::kDataLeakPreventionRulesList, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(rules),
+               nullptr);
+  UpdateProviderPolicy(policies);
+
+  {
+    ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste,
+                                     std::make_unique<ui::DataTransferEndpoint>(
+                                         url::Origin::Create(GURL(kUrl1))));
+    writer.WriteText(base::UTF8ToUTF16(kClipboardText));
+  }
+  ui::DataTransferEndpoint data_dst1(ui::EndpointType::kDefault);
+  base::string16 result1;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst1, &result1);
+  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText), result1);
+
+  // `notify_if_restricted` should be set false, otherwise the test would fail,
+  // because no guest os is actually running.
+  ui::DataTransferEndpoint data_dst2(ui::EndpointType::kArc,
+                                     /*notify_if_restricted=*/false);
+  base::string16 result2;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst2, &result2);
+  EXPECT_EQ(base::string16(), result2);
+
+  // `notify_if_restricted` should be set false, otherwise the test would fail,
+  // because no guest os is actually running.
+  ui::DataTransferEndpoint data_dst3(ui::EndpointType::kGuestOs,
+                                     /*notify_if_restricted=*/false);
+  base::string16 result3;
+  ui::Clipboard::GetForCurrentThread()->ReadText(
+      ui::ClipboardBuffer::kCopyPaste, &data_dst3, &result3);
+  EXPECT_EQ(base::string16(), result3);
+}
+
+// TODO(crbug.com/1139884): Add browsertests for the clipboard notifications.
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 8c5e520e..9cc52c47 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -491,6 +491,10 @@
   registry->RegisterBooleanPref(
       chromeos::prefs::kLauncherResultEverLaunched, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
+
+  registry->RegisterBooleanPref(
+      chromeos::prefs::kHasCameraAppMigratedToSWA, false,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
 }
 
 void Preferences::InitUserPrefs(sync_preferences::PrefServiceSyncable* prefs) {
@@ -1108,9 +1112,8 @@
   rate.repeat_interval_in_ms = xkb_auto_repeat_interval_pref_.GetValue();
   DCHECK(rate.initial_delay_in_ms > 0);
   DCHECK(rate.repeat_interval_in_ms > 0);
-  input_method::InputMethodManager::Get()
-      ->GetImeKeyboard()
-      ->SetAutoRepeatRate(rate);
+  input_method::InputMethodManager::Get()->GetImeKeyboard()->SetAutoRepeatRate(
+      rate);
 
   user_manager::known_user::SetIntegerPref(user_->GetAccountId(),
                                            ash::prefs::kXkbAutoRepeatDelay,
diff --git a/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc b/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
index bf9b674..1bbd1137 100644
--- a/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
+++ b/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
@@ -14,7 +14,7 @@
 std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForCameraSystemWebApp() {
   auto info = std::make_unique<WebApplicationInfo>();
   info->start_url = GURL(chromeos::kChromeUICameraAppMainURL);
-  info->scope = GURL(chromeos::kChromeUICameraAppURL);
+  info->scope = GURL(chromeos::kChromeUICameraAppScopeURL);
 
   info->title = l10n_util::GetStringUTF16(IDS_NAME);
   web_app::CreateIconInfoForSystemWebApp(
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
index 1d86988..f373950 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -12,6 +12,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "base/numerics/safe_conversions.h"
@@ -346,9 +347,13 @@
     return;
   }
 
+  // In case the Weakness Check feature is enabled start the check, and notify
+  // observers once done.
   if (base::FeatureList::IsEnabled(
           password_manager::features::kPasswordsWeaknessCheck)) {
-    insecure_credentials_manager_.StartWeakCheck();
+    insecure_credentials_manager_.StartWeakCheck(base::BindOnce(
+        &PasswordCheckDelegate::RecordAndNotifyAboutCompletedWeakPasswordCheck,
+        weak_ptr_factory_.GetWeakPtr()));
   }
 
   auto progress = base::MakeRefCounted<PasswordCheckProgress>();
@@ -378,13 +383,15 @@
 PasswordCheckDelegate::GetPasswordCheckStatus() const {
   api::passwords_private::PasswordCheckStatus result;
 
-  // Obtain the timestamp of the last completed check. This is 0.0 in case the
-  // check never completely ran before.
-  const double last_check_completed = profile_->GetPrefs()->GetDouble(
-      password_manager::prefs::kLastTimePasswordCheckCompleted);
-  if (last_check_completed) {
-    result.elapsed_time_since_last_check = std::make_unique<std::string>(
-        FormatElapsedTime(base::Time::FromDoubleT(last_check_completed)));
+  // Obtain the timestamp of the last completed password or weak check. This
+  // will be null in case no check has completely ran before.
+  base::Time last_check_completed =
+      std::max(base::Time::FromTimeT(profile_->GetPrefs()->GetDouble(
+                   password_manager::prefs::kLastTimePasswordCheckCompleted)),
+               last_completed_weak_check_);
+  if (!last_check_completed.is_null()) {
+    result.elapsed_time_since_last_check =
+        std::make_unique<std::string>(FormatElapsedTime(last_check_completed));
   }
 
   State state = bulk_leak_check_service_adapter_.GetBulkLeakCheckState();
@@ -455,18 +462,7 @@
   if (state == State::kIdle && std::exchange(is_check_running_, false)) {
     // When the service transitions from running into idle it has finished a
     // check.
-    profile_->GetPrefs()->SetDouble(
-        password_manager::prefs::kLastTimePasswordCheckCompleted,
-        base::Time::Now().ToDoubleT());
-
-    // In case the check run to completion delay the last Check Status update by
-    // a second. This avoids flickering of the UI if the full check ran from
-    // start to finish almost immediately.
-    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&PasswordCheckDelegate::NotifyPasswordCheckStatusChanged,
-                       weak_ptr_factory_.GetWeakPtr()),
-        base::TimeDelta::FromSeconds(1));
+    RecordAndNotifyAboutCompletedCompromisedPasswordCheck();
     return;
   }
 
@@ -514,6 +510,29 @@
   return insecure_credential;
 }
 
+void PasswordCheckDelegate::
+    RecordAndNotifyAboutCompletedCompromisedPasswordCheck() {
+  profile_->GetPrefs()->SetDouble(
+      password_manager::prefs::kLastTimePasswordCheckCompleted,
+      base::Time::Now().ToDoubleT());
+
+  // Delay the last Check Status update by a second. This avoids flickering of
+  // the UI if the full check ran from start to finish almost immediately.
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&PasswordCheckDelegate::NotifyPasswordCheckStatusChanged,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(1));
+}
+
+void PasswordCheckDelegate::RecordAndNotifyAboutCompletedWeakPasswordCheck() {
+  last_completed_weak_check_ = base::Time::Now();
+  // Note: In contrast to the compromised password check we do not does not
+  // artificially delay the response, Since this check is expected to complete
+  // quickly.
+  NotifyPasswordCheckStatusChanged();
+}
+
 void PasswordCheckDelegate::NotifyPasswordCheckStatusChanged() {
   if (auto* event_router =
           PasswordsPrivateEventRouterFactory::GetForProfile(profile_)) {
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.h b/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
index ab7abc2fc..257c99a 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
+#include "base/time/time.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_utils.h"
 #include "chrome/common/extensions/api/passwords_private.h"
@@ -122,6 +123,14 @@
   FindMatchingInsecureCredential(
       const api::passwords_private::InsecureCredential& credential) const;
 
+  // Invoked when a compromised password check completes. Records the current
+  // timestamp in `kLastTimePasswordCheckCompleted` pref.
+  void RecordAndNotifyAboutCompletedCompromisedPasswordCheck();
+
+  // Invoked when a weak password check completes. Records the current timestamp
+  // in `last_completed_weak_check_`.
+  void RecordAndNotifyAboutCompletedWeakPasswordCheck();
+
   // Tries to notify the PasswordsPrivateEventRouter that the password check
   // status has changed. Invoked after OnSavedPasswordsChanged and
   // OnStateChanged.
@@ -162,6 +171,9 @@
   // Remembers whether a password check is running right now.
   bool is_check_running_ = false;
 
+  // Store when the last weak check was completed.
+  base::Time last_completed_weak_check_;
+
   // A scoped observer for |saved_passwords_presenter_|.
   ScopedObserver<password_manager::SavedPasswordsPresenter,
                  password_manager::SavedPasswordsPresenter::Observer>
diff --git a/chrome/browser/extensions/api/storage/setting_sync_data.cc b/chrome/browser/extensions/api/storage/setting_sync_data.cc
index 4dce6ce..ce7dd47c 100644
--- a/chrome/browser/extensions/api/storage/setting_sync_data.cc
+++ b/chrome/browser/extensions/api/storage/setting_sync_data.cc
@@ -22,7 +22,7 @@
 }
 
 SettingSyncData::SettingSyncData(const syncer::SyncData& sync_data)
-    : change_type_(syncer::SyncChange::ACTION_INVALID) {
+    : change_type_(base::nullopt) {
   ExtractSyncData(sync_data);
 }
 
diff --git a/chrome/browser/extensions/api/storage/setting_sync_data.h b/chrome/browser/extensions/api/storage/setting_sync_data.h
index 6522f30..fca45377 100644
--- a/chrome/browser/extensions/api/storage/setting_sync_data.h
+++ b/chrome/browser/extensions/api/storage/setting_sync_data.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/values.h"
 #include "components/sync/model/sync_change.h"
 
@@ -25,7 +26,7 @@
   // Creates from a sync change.
   explicit SettingSyncData(const syncer::SyncChange& sync_change);
 
-  // Creates from sync data. |change_type| will be ACTION_INVALID.
+  // Creates from sync data. |change_type| will be base::nullopt.
   explicit SettingSyncData(const syncer::SyncData& sync_data);
 
   // Creates explicitly.
@@ -36,9 +37,10 @@
 
   ~SettingSyncData();
 
-  // May return ACTION_INVALID if this object represents sync data that isn't
+  // May return base::nullopt if this object represents sync data that isn't
   // associated with a sync operation.
-  syncer::SyncChange::SyncChangeType change_type() const {
+  const base::Optional<syncer::SyncChange::SyncChangeType>& change_type()
+      const {
     return change_type_;
   }
   const std::string& extension_id() const { return extension_id_; }
@@ -55,7 +57,7 @@
   // either an extension or app settings data type.
   void ExtractSyncData(const syncer::SyncData& sync_data);
 
-  syncer::SyncChange::SyncChangeType change_type_;
+  base::Optional<syncer::SyncChange::SyncChangeType> change_type_;
   std::string extension_id_;
   std::string key_;
   std::unique_ptr<base::Value> value_;
diff --git a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
index 9291132..3457a1f 100644
--- a/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
+++ b/chrome/browser/extensions/api/storage/syncable_settings_storage.cc
@@ -289,7 +289,9 @@
 
     syncer::SyncError error;
 
-    switch (sync_change->change_type()) {
+    DCHECK(sync_change->change_type().has_value());
+
+    switch (*sync_change->change_type()) {
       case syncer::SyncChange::ACTION_ADD:
         if (!current_value.get()) {
           error = OnSyncAdd(key, std::move(change_value), &changes);
@@ -324,9 +326,6 @@
               extension_id_ << "/" << key;
         }
         break;
-
-      default:
-        NOTREACHED();
     }
 
     if (error.IsSet()) {
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index f52ce98..07ba5a3 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -407,6 +407,9 @@
                           base::FeatureList::IsEnabled(
                               chromeos::features::kHandwritingGestureEditing)));
   features->AppendString(GenerateFeatureFlag(
+      "multiword",
+      base::FeatureList::IsEnabled(chromeos::features::kAssistMultiWord)));
+  features->AppendString(GenerateFeatureFlag(
       "floatingkeyboarddefault",
       base::FeatureList::IsEnabled(
           chromeos::features::kVirtualKeyboardFloatingDefault)));
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index cdfc420..d082efa8a 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -53,6 +53,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/keyboard/ui/grit/keyboard_resources.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/constants/chromeos_pref_names.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/site_instance.h"
@@ -396,7 +397,15 @@
 }
 
 void ComponentLoader::AddChromeCameraApp() {
-  if (base::FeatureList::IsEnabled(chromeos::features::kCameraSystemWebApp)) {
+  // TODO(crbug.com/1135280): Remove all the logic here once CCA is fully
+  // migrated to SWA.
+
+  // If users should use the SWA version of CCA and the status from the platform
+  // app version is already migrated, there is no need to install the platform
+  // version of CCA.
+  if (base::FeatureList::IsEnabled(chromeos::features::kCameraSystemWebApp) &&
+      profile_->GetPrefs()->GetBoolean(
+          chromeos::prefs::kHasCameraAppMigratedToSWA)) {
     return;
   }
 
diff --git a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
index d4fe18e1..a970c52d 100644
--- a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
+++ b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
@@ -71,18 +71,6 @@
 
 namespace {
 
-enum TestParam {
-  // Whether the extension under test is "allowlisted" (see
-  // GetExtensionsAllowlist in
-  // //extensions/browser/url_loader_factory_manager.cc).
-  kAllowlisted = 1 << 0,
-
-  // Whether network::features::
-  // kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess is
-  // enabled.
-  kDeriveOriginFromUrl = 1 << 2,
-};
-
 const char kCorsErrorWhenFetching[] = "error: TypeError: Failed to fetch";
 
 // The manifest.json used by tests uses |kExpectedKey| that will result in the
@@ -197,7 +185,7 @@
 
 class CorbAndCorsExtensionBrowserTest
     : public CorbAndCorsExtensionTestBase,
-      public ::testing::WithParamInterface<TestParam> {
+      public ::testing::WithParamInterface<bool> {
  public:
   using Base = CorbAndCorsExtensionTestBase;
 
@@ -206,17 +194,6 @@
     std::vector<base::test::ScopedFeatureList::FeatureAndParams>
         enabled_features;
 
-    if (DeriveOriginFromUrl()) {
-      enabled_features.emplace_back(
-          network::features::
-              kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess,
-          base::FieldTrialParams());
-    } else {
-      disabled_features.push_back(
-          network::features::
-              kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess);
-    }
-
     if (IsExtensionAllowlisted()) {
       base::FieldTrialParams field_trial_params;
       field_trial_params.emplace(
@@ -240,13 +217,7 @@
         &policy_provider_);
   }
 
-  bool IsExtensionAllowlisted() {
-    return (GetParam() & TestParam::kAllowlisted) != 0;
-  }
-
-  bool DeriveOriginFromUrl() {
-    return (GetParam() & TestParam::kDeriveOriginFromUrl) != 0;
-  }
+  bool IsExtensionAllowlisted() { return GetParam(); }
 
   const Extension* InstallExtension(
       GURL resource_to_fetch_from_declarative_content_script = GURL()) {
@@ -1288,7 +1259,7 @@
 };
 INSTANTIATE_TEST_SUITE_P(Allowlisted_AllowlistForCors,
                          TrustTokenExtensionBrowserTest,
-                         ::testing::Values(TestParam::kAllowlisted));
+                         ::testing::Values(true));
 IN_PROC_BROWSER_TEST_P(
     TrustTokenExtensionBrowserTest,
     FromProgrammaticContentScript_TrustTokenRedemptionAllowed) {
@@ -2296,23 +2267,16 @@
 
 INSTANTIATE_TEST_SUITE_P(Allowlisted,
                          CorbAndCorsExtensionBrowserTest,
-                         ::testing::Values(TestParam::kAllowlisted));
+                         ::testing::Values(true));
 INSTANTIATE_TEST_SUITE_P(NotAllowlisted,
                          CorbAndCorsExtensionBrowserTest,
-                         ::testing::Values(0));
+                         ::testing::Values(false));
 
 INSTANTIATE_TEST_SUITE_P(Allowlisted_LegacyOriginHeaderBehavior,
                          OriginHeaderExtensionBrowserTest,
-                         ::testing::Values(TestParam::kAllowlisted));
-INSTANTIATE_TEST_SUITE_P(Allowlisted_NewOriginHeaderBehavior,
-                         OriginHeaderExtensionBrowserTest,
-                         ::testing::Values(TestParam::kAllowlisted |
-                                           TestParam::kDeriveOriginFromUrl));
+                         ::testing::Values(true));
 INSTANTIATE_TEST_SUITE_P(NotAllowlisted_LegacyOriginHeaderBehavior,
                          OriginHeaderExtensionBrowserTest,
-                         ::testing::Values(0));
-INSTANTIATE_TEST_SUITE_P(NotAllowlisted_NewOriginHeaderBehavior,
-                         OriginHeaderExtensionBrowserTest,
-                         ::testing::Values(TestParam::kDeriveOriginFromUrl));
+                         ::testing::Values(false));
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_samesite_cookies_browsertest.cc b/chrome/browser/extensions/extension_samesite_cookies_browsertest.cc
index 6cc76d4..6cb45ce 100644
--- a/chrome/browser/extensions/extension_samesite_cookies_browsertest.cc
+++ b/chrome/browser/extensions/extension_samesite_cookies_browsertest.cc
@@ -362,8 +362,9 @@
 
 // Extension is site_for_cookies, initiator and requested URL are permitted,
 // initiator and requested URL are same-site => SameSite cookies are sent.
+// crbug.com/1153083: flaky on linux, win, and mac
 IN_PROC_BROWSER_TEST_P(ExtensionSameSiteCookiesTest,
-                       OnePermittedSameSiteFrame_Navigation) {
+                       DISABLED_OnePermittedSameSiteFrame_Navigation) {
   SetCookies(kPermittedHost);
   content::RenderFrameHost* main_frame = NavigateMainFrameToExtensionPage();
   content::RenderFrameHost* child_frame =
diff --git a/chrome/browser/extensions/fetch_apitest.cc b/chrome/browser/extensions/fetch_apitest.cc
index bfeac5a..b40a047 100644
--- a/chrome/browser/extensions/fetch_apitest.cc
+++ b/chrome/browser/extensions/fetch_apitest.cc
@@ -287,28 +287,7 @@
   EXPECT_EQ("basic", ExecuteScriptInBackgroundPage(extension->id(), script));
 }
 
-class ExtensionFetchPostOriginTest : public ExtensionFetchTest,
-                                     public testing::WithParamInterface<bool> {
- protected:
-  void SetUp() override {
-    if (GetParam()) {
-      scoped_feature_list_.InitAndEnableFeature(
-          network::features::
-              kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          network::features::
-              kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess);
-    }
-    ExtensionFetchTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_P(ExtensionFetchPostOriginTest,
-                       OriginOnPostWithPermissions) {
+IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, OriginOnPostWithPermissions) {
   TestExtensionDir dir;
   dir.WriteManifest(R"JSON(
      {
@@ -324,15 +303,12 @@
   GURL destination_url =
       embedded_test_server()->GetURL("example.com", "/echo-origin");
   std::string script = content::JsReplace(kFetchPostScript, destination_url);
-  std::string origin_string =
-      GetParam() ? url::Origin::Create(destination_url).Serialize()
-                 : url::Origin::Create(extension->url()).Serialize();
+  std::string origin_string = url::Origin::Create(extension->url()).Serialize();
   EXPECT_EQ(origin_string,
             ExecuteScriptInBackgroundPage(extension->id(), script));
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionFetchPostOriginTest,
-                       OriginOnPostWithoutPermissions) {
+IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, OriginOnPostWithoutPermissions) {
   TestExtensionDir dir;
   dir.WriteManifest(R"JSON(
      {
@@ -352,14 +328,6 @@
             ExecuteScriptInBackgroundPage(extension->id(), script));
 }
 
-INSTANTIATE_TEST_SUITE_P(UseExtensionOrigin,
-                         ExtensionFetchPostOriginTest,
-                         testing::Values(false));
-
-INSTANTIATE_TEST_SUITE_P(UseDestinationUrlOrigin,
-                         ExtensionFetchPostOriginTest,
-                         testing::Values(true));
-
 // An extension background script should be able to fetch resources contained in
 // the extension, and those resources should not be opaque.
 IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, ExtensionResourceShouldNotBeOpaque) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2d7886c..184be0b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1395,6 +1395,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "enable-cros-ime-assist-multi-word",
+    "owners": [ "curtismcmullan", "essential-inputs-team@google.com" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "enable-cros-ime-assist-personal-info",
     "owners": [ "jiwan", "essential-inputs-team@google.com" ],
     "expiry_milestone": 90
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 3cb19219..4fa6c8d9 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4281,6 +4281,11 @@
 const char kImeAssistAutocorrectDescription[] =
     "Enable assistive auto-correct features for native IME";
 
+const char kImeAssistMultiWordName[] =
+    "Enable assistive multi word suggestions";
+const char kImeAssistMultiWordDescription[] =
+    "Enable assistive multi word suggestions for native IME";
+
 const char kImeAssistPersonalInfoName[] = "Enable assistive personal info";
 const char kImeAssistPersonalInfoDescription[] =
     "Enable auto-complete suggestions on personal infomation for native IME.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 36e207b..667be75 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2498,6 +2498,9 @@
 extern const char kImeAssistAutocorrectName[];
 extern const char kImeAssistAutocorrectDescription[];
 
+extern const char kImeAssistMultiWordName[];
+extern const char kImeAssistMultiWordDescription[];
+
 extern const char kImeAssistPersonalInfoName[];
 extern const char kImeAssistPersonalInfoDescription[];
 
diff --git a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceTest.java b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceTest.java
index 0e65d8c..cb72f9c6 100644
--- a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceTest.java
+++ b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/services/PaintPreviewTabServiceTest.java
@@ -99,6 +99,8 @@
         activity.getWindow().setLocalFocus(true, true);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             InstrumentationRegistry.getInstrumentation().callActivityOnRestart(activity);
+            InstrumentationRegistry.getInstrumentation().callActivityOnStart(activity);
+            InstrumentationRegistry.getInstrumentation().callActivityOnResume(activity);
         });
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
diff --git a/chrome/browser/permissions/prediction_based_permission_ui_selector.cc b/chrome/browser/permissions/prediction_based_permission_ui_selector.cc
index 2cddc45..50b4d53 100644
--- a/chrome/browser/permissions/prediction_based_permission_ui_selector.cc
+++ b/chrome/browser/permissions/prediction_based_permission_ui_selector.cc
@@ -51,6 +51,8 @@
     return;
   }
 
+  last_request_grant_likelihood_ = base::nullopt;
+
   DCHECK(!request_);
   permissions::PredictionService* service =
       PredictionServiceFactory::GetForProfile(profile_);
@@ -67,6 +69,11 @@
   callback_.Reset();
 }
 
+base::Optional<permissions::PermissionUmaUtil::PredictionGrantLikelihood>
+PredictionBasedPermissionUiSelector::PredictedGrantLikelihoodForUKM() {
+  return last_request_grant_likelihood_;
+}
+
 permissions::PredictionRequestFeatures
 PredictionBasedPermissionUiSelector::BuildPredictionRequestFeatures(
     permissions::PermissionRequest* request) {
@@ -129,6 +136,9 @@
     return;
   }
 
+  last_request_grant_likelihood_ =
+      response->suggestion(0).grant_likelihood().discretized_likelihood();
+
   if (response->suggestion(0).grant_likelihood().discretized_likelihood() ==
       VeryUnlikely) {
     std::move(callback_).Run(Decision(
diff --git a/chrome/browser/permissions/prediction_based_permission_ui_selector.h b/chrome/browser/permissions/prediction_based_permission_ui_selector.h
index d8511911..20781d33 100644
--- a/chrome/browser/permissions/prediction_based_permission_ui_selector.h
+++ b/chrome/browser/permissions/prediction_based_permission_ui_selector.h
@@ -38,6 +38,9 @@
 
   void Cancel() override;
 
+  base::Optional<permissions::PermissionUmaUtil::PredictionGrantLikelihood>
+  PredictedGrantLikelihoodForUKM() override;
+
  private:
   permissions::PredictionRequestFeatures BuildPredictionRequestFeatures(
       permissions::PermissionRequest* request);
@@ -49,6 +52,9 @@
 
   Profile* profile_;
   std::unique_ptr<PredictionServiceRequest> request_;
+  base::Optional<permissions::PermissionUmaUtil::PredictionGrantLikelihood>
+      last_request_grant_likelihood_;
+
   DecisionMadeCallback callback_;
 };
 
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 1cfa576..8c4da83d 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -194,7 +194,6 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "chrome/browser/plugins/plugin_info_host_impl.h"
 #include "chrome/browser/plugins/plugins_resource_service.h"
-#include "chrome/browser/renderer_host/pepper/device_id_fetcher.h"
 #endif
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
@@ -491,6 +490,9 @@
 const char kSettingsLaunchedPasswordChecks[] =
     "profile.settings_launched_password_checks";
 
+// Deprecated 11/2020
+const char kDRMSalt[] = "settings.privacy.drm_salt";
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -568,6 +570,7 @@
 #endif
 
   registry->RegisterIntegerPref(kSettingsLaunchedPasswordChecks, 0);
+  registry->RegisterStringPref(kDRMSalt, "");
 }
 
 }  // namespace
@@ -866,7 +869,6 @@
 #endif
 
 #if BUILDFLAG(ENABLE_PLUGINS)
-  DeviceIDFetcher::RegisterProfilePrefs(registry);
   PluginInfoHostImpl::RegisterUserPrefs(registry);
 #endif
 
@@ -1172,4 +1174,7 @@
 
   // Added 11/2020
   profile_prefs->ClearPref(kSettingsLaunchedPasswordChecks);
+
+  // Added 11/2020
+  profile_prefs->ClearPref(kDRMSalt);
 }
diff --git a/chrome/browser/profiles/profile_metrics.h b/chrome/browser/profiles/profile_metrics.h
index 044deee..440fc1c 100644
--- a/chrome/browser/profiles/profile_metrics.h
+++ b/chrome/browser/profiles/profile_metrics.h
@@ -79,10 +79,10 @@
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
   enum ProfileSync {
-    SYNC_CUSTOMIZE = 0,       // User decided to customize sync
-    SYNC_CHOOSE,              // User chose what to sync
-    SYNC_ENCRYPT,             // User has chosen to encrypt all data
-    SYNC_PASSPHRASE,          // User is using a passphrase
+    SYNC_CUSTOMIZE = 0,           // User decided to customize sync
+    SYNC_CHOOSE,                  // User chose what to sync
+    SYNC_CREATED_NEW_PASSPHRASE,  // User created a passphrase to encrypt data
+    SYNC_ENTERED_EXISTING_PASSPHRASE,  // User entered an existing passphrase
     NUM_PROFILE_SYNC_METRICS
   };
 
diff --git a/chrome/browser/renderer_host/pepper/device_id_fetcher.cc b/chrome/browser/renderer_host/pepper/device_id_fetcher.cc
deleted file mode 100644
index ac59ea46..0000000
--- a/chrome/browser/renderer_host/pepper/device_id_fetcher.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2013 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 "chrome/browser/renderer_host/pepper/device_id_fetcher.h"
-
-#include "chrome/common/pref_names.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-
-// static
-void DeviceIDFetcher::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* prefs) {
-  prefs->RegisterBooleanPref(prefs::kEnableDRM, true);
-  prefs->RegisterStringPref(prefs::kDRMSalt, "");
-}
diff --git a/chrome/browser/renderer_host/pepper/device_id_fetcher.h b/chrome/browser/renderer_host/pepper/device_id_fetcher.h
deleted file mode 100644
index f5892c06c..0000000
--- a/chrome/browser/renderer_host/pepper/device_id_fetcher.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2013 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 CHROME_BROWSER_RENDERER_HOST_PEPPER_DEVICE_ID_FETCHER_H_
-#define CHROME_BROWSER_RENDERER_HOST_PEPPER_DEVICE_ID_FETCHER_H_
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-// This class has been followed out and now simply hosts a profile pref.
-// TODO(crbug.com/1152871): Remove or migrate those, too.
-class DeviceIDFetcher {
- public:
-  DeviceIDFetcher() = delete;
-
-  // Called to register the |kEnableDRM| and |kDRMSalt| preferences.
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* prefs);
-};
-
-#endif  // CHROME_BROWSER_RENDERER_HOST_PEPPER_DEVICE_ID_FETCHER_H_
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index 5db088b..d3d4e10a 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -43,6 +43,7 @@
     ":oobe_network",
     ":oobe_reset",
     ":oobe_screen_assistant_optin_flow",
+    ":oobe_screen_auto_enrollment_check",
     ":oobe_screen_enable_debugging",
     ":oobe_select",
     ":oobe_supervision_transition",
@@ -99,7 +100,7 @@
 js_library("active_directory_password_change") {
   deps = [
     "components/oobe_dialog:oobe_dialog",
-    "components/oobe_i18n_behavior:oobe_i18n_behavior"
+    "components/oobe_i18n_behavior:oobe_i18n_behavior",
   ]
 }
 
@@ -255,8 +256,8 @@
   deps = [
     ":web_view_helper",
     "components:login_screen_behavior",
-    "components/oobe_dialog:oobe_dialog",
     "components:oobe_dialog_host_behavior",
+    "components/oobe_dialog:oobe_dialog",
     "components/oobe_i18n_behavior:oobe_i18n_behavior",
   ]
 }
@@ -290,6 +291,13 @@
   ]
 }
 
+js_library("oobe_screen_auto_enrollment_check") {
+  deps = [
+    "components:login_screen_behavior",
+    "components/oobe_i18n_behavior:oobe_i18n_behavior",
+  ]
+}
+
 js_library("oobe_screen_enable_debugging") {
   deps = [
     "components:login_screen_behavior",
diff --git a/chrome/browser/resources/chromeos/login/md_login.html b/chrome/browser/resources/chromeos/login/md_login.html
index 045e89af..43df1b66 100644
--- a/chrome/browser/resources/chromeos/login/md_login.html
+++ b/chrome/browser/resources/chromeos/login/md_login.html
@@ -55,7 +55,6 @@
 <script defer src="chrome://oobe/test_api.js"></script>
 <link rel="stylesheet" href="api_keys_notice.css">
 <link rel="stylesheet" href="oobe_screen_autolaunch.css">
-<link rel="stylesheet" href="oobe_screen_auto_enrollment_check.css">
 <link rel="stylesheet" href="screen_error_message.css">
 
 <script src="chrome://oobe/keyboard_utils.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/oobe.html b/chrome/browser/resources/chromeos/login/oobe.html
index 36890e6..d0957100 100644
--- a/chrome/browser/resources/chromeos/login/oobe.html
+++ b/chrome/browser/resources/chromeos/login/oobe.html
@@ -60,7 +60,6 @@
 <link rel="stylesheet" href="api_keys_notice.css">
 
 <link rel="stylesheet" href="oobe_screen_autolaunch.css">
-<link rel="stylesheet" href="oobe_screen_auto_enrollment_check.css">
 
 <link rel="stylesheet" href="screen_error_message.css">
 
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js
index 2115305..ce4dd5cf 100644
--- a/chrome/browser/resources/chromeos/login/oobe.js
+++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -28,7 +28,6 @@
 // <include src="screen_multidevice_setup.js">
 
 // <include src="../../gaia_auth_host/authenticator.js">
-// <include src="oobe_screen_auto_enrollment_check.js">
 // <include src="multi_tap_detector.js">
 // <include src="web_view_helper.js">
 
@@ -41,7 +40,6 @@
        */
       initialize() {
         cr.ui.login.DisplayManager.initialize();
-        login.AutoEnrollmentCheckScreen.register();
         login.AutolaunchScreen.register();
         login.AccountPickerScreen.register();
         login.ErrorMessageScreen.register();
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.css b/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.css
deleted file mode 100644
index f718523..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.css
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Copyright 2014 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. */
-
-#auto-enrollment-check {
-  min-height: 395px;
-  padding: 70px 17px 21px;
-}
-
-#auto-enrollment-check #auto-enrollment-check-progress {
-  color: #9c9c9c;
-  display: flex;
-  justify-content: center;
-  margin-top: 130px;
-  min-height: 0;
-}
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.html b/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.html
index 3990cab..cbaf814a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.html
@@ -1,9 +1,33 @@
-<div class="step hidden" id="auto-enrollment-check" role="group"
-     i18n-values="aria-label:autoEnrollmentCheckScreenHeader" hidden>
-  <div class="step-contents">
-    <div id="auto-enrollment-check-progress" aria-live="polite">
-      <div class="throbber"></div>
-      <div i18n-content="autoEnrollmentCheckMessage"></div>
+<!-- Copyright 2020 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. -->
+
+<dom-module id="auto-enrollment-check-element">
+  <template>
+    <link rel="stylesheet" href="chrome://resources/css/throbber.css">
+    <link rel="stylesheet" href="../../../../../ui/login/oobe.css">
+    <style>
+      #auto-enrollment-check {
+        min-height: 395px;
+        padding: 70px 17px 21px;
+      }
+
+      #auto-enrollment-check #auto-enrollment-check-progress {
+        color: #9c9c9c;
+        display: flex;
+        justify-content: center;
+        margin-top: 130px;
+        min-height: 0;
+      }
+    </style>
+    <div id="auto-enrollment-check" role="group"
+        i18n-values="aria-label:autoEnrollmentCheckScreenHeader">
+      <div class="step-contents">
+        <div id="auto-enrollment-check-progress" aria-live="polite">
+          <div class="throbber"></div>
+          [[i18nDynamic(locale, 'autoEnrollmentCheckMessage')]]
+        </div>
+      </div>
     </div>
-  </div>
-</div>
+  </template>
+</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.js b/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.js
index 1065eb88..70eea170 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_auto_enrollment_check.js
@@ -6,7 +6,14 @@
  * @fileoverview Oobe Auto-enrollment check screen implementation.
  */
 
-login.createScreen(
-    'AutoEnrollmentCheckScreen', 'auto-enrollment-check', function() {
-      return {EXTERNAL_API: []};
+Polymer({
+  is: 'auto-enrollment-check-element',
+
+  behaviors: [OobeI18nBehavior, LoginScreenBehavior],
+
+  ready() {
+    this.initializeLoginScreen('AutoEnrollmentCheckScreen', {
+      resetAllowed: true,
     });
+  },
+});
diff --git a/chrome/browser/resources/chromeos/login/structure/components_oobe.html b/chrome/browser/resources/chromeos/login/structure/components_oobe.html
index 2618d65..1689a189 100644
--- a/chrome/browser/resources/chromeos/login/structure/components_oobe.html
+++ b/chrome/browser/resources/chromeos/login/structure/components_oobe.html
@@ -6,6 +6,7 @@
 
 <include src="../oobe_hid_detection.html">
 <include src="../oobe_i18n_dropdown.html">
+<include src="../oobe_screen_auto_enrollment_check.html">
 <include src="../oobe_screen_enable_debugging.html">
 <include src="../oobe_welcome_dialog.html">
 <include src="../oobe_welcome.html">
diff --git a/chrome/browser/resources/chromeos/login/structure/components_oobe.js b/chrome/browser/resources/chromeos/login/structure/components_oobe.js
index 0f246fd..1f4b99dd6 100644
--- a/chrome/browser/resources/chromeos/login/structure/components_oobe.js
+++ b/chrome/browser/resources/chromeos/login/structure/components_oobe.js
@@ -4,6 +4,7 @@
 
 // <include src="../oobe_hid_detection.js">
 // <include src="../oobe_i18n_dropdown.js">
+// <include src="../oobe_screen_auto_enrollment_check.js">
 // <include src="../oobe_screen_enable_debugging.js">
 // <include src="../oobe_welcome_dialog.js">
 // <include src="../oobe_welcome.js">
diff --git a/chrome/browser/resources/chromeos/login/structure/screens_oobe.html b/chrome/browser/resources/chromeos/login/structure/screens_oobe.html
index a6c4b83..17e8c2f 100644
--- a/chrome/browser/resources/chromeos/login/structure/screens_oobe.html
+++ b/chrome/browser/resources/chromeos/login/structure/screens_oobe.html
@@ -18,7 +18,8 @@
 </oobe-eula-element>
 <oobe-update-element id="oobe-update" class="step hidden" hidden>
 </oobe-update-element>
-<include src="../oobe_screen_auto_enrollment_check.html">
+<auto-enrollment-check-element id="auto-enrollment-check" class="step hidden" hidden>
+</auto-enrollment-check-element>
 <demo-preferences-element id="demo-preferences" class="step hidden" hidden>
 </demo-preferences-element>
 <enterprise-enrollment-element id="enterprise-enrollment" class="step hidden"
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.js b/chrome/browser/resources/settings/autofill_page/password_check.js
index f193afc..c8c52b1 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.js
+++ b/chrome/browser/resources/settings/autofill_page/password_check.js
@@ -530,8 +530,10 @@
    * @private
    */
   showsTimestamp_() {
-    return this.status.state === CheckState.IDLE &&
-        !!this.status.elapsedTimeSinceLastCheck;
+    return !!this.status.elapsedTimeSinceLastCheck &&
+        (this.status.state === CheckState.IDLE ||
+         (this.status.state === CheckState.SIGNED_OUT &&
+          this.passwordsWeaknessCheckEnabled));
   },
 
   /**
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index eb9e0ef99..a3f55743 100644
--- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -72,7 +72,6 @@
    *   extensionsRegistered: boolean,
    *   extensionsSynced: boolean,
    *   fullEncryptionBody: string,
-   *   passphrase: (string|undefined),
    *   passphraseRequired: boolean,
    *   passwordsRegistered: boolean,
    *   passwordsSynced: boolean,
@@ -81,7 +80,6 @@
    *   preferencesSynced: boolean,
    *   readingListRegistered: boolean,
    *   readingListSynced: boolean,
-   *   setNewPassphrase: (boolean|undefined),
    *   syncAllDataTypes: boolean,
    *   tabsRegistered: boolean,
    *   tabsSynced: boolean,
@@ -198,13 +196,20 @@
     setSyncDatatypes(syncPrefs) {}
 
     /**
-     * Sets the sync encryption options.
-     * @param {!settings.SyncPrefs} syncPrefs
-     * @return {!Promise<!settings.PageStatus>}
+     * Attempts to set up a new passphrase to encrypt Sync data.
+     * @param {string} passphrase
+     * @return {!Promise<boolean>} Whether the passphrase was successfully set.
+     * The call can fail, for example, if encrypting the data is disallowed.
      */
-    // TODO(crbug.com/1139060): Use a clear signature which doesn't rely on
-    // syncPrefs.
-    setSyncEncryption(syncPrefs) {}
+    setEncryptionPassphrase(passphrase) {}
+
+    /**
+     * Attempts to set the passphrase to decrypt Sync data.
+     * @param {string} passphrase
+     * @return {!Promise<boolean>} Whether the passphrase was successfully set.
+     * The call can fail, for example, if the passphrase is incorrect.
+     */
+    setDecryptionPassphrase(passphrase) {}
 
     /**
      * Start syncing with an account, specified by its email.
@@ -312,9 +317,13 @@
     }
 
     /** @override */
-    setSyncEncryption(syncPrefs) {
-      return cr.sendWithPromise(
-          'SyncSetupSetEncryption', JSON.stringify(syncPrefs));
+    setEncryptionPassphrase(passphrase) {
+      return cr.sendWithPromise('SyncSetupSetEncryptionPassphrase', passphrase);
+    }
+
+    /** @override */
+    setDecryptionPassphrase(passphrase) {
+      return cr.sendWithPromise('SyncSetupSetDecryptionPassphrase', passphrase);
     }
 
     /** @override */
diff --git a/chrome/browser/resources/settings/people_page/sync_encryption_options.js b/chrome/browser/resources/settings/people_page/sync_encryption_options.js
index 98d8f48..0708d6c 100644
--- a/chrome/browser/resources/settings/people_page/sync_encryption_options.js
+++ b/chrome/browser/resources/settings/people_page/sync_encryption_options.js
@@ -68,6 +68,13 @@
   },
 
   /**
+   * Whether there's a setEncryptionPassphrase() call pending response, in which
+   * case the component should wait before making a new call.
+   * @private {boolean}
+   */
+  isSettingEncryptionPassphrase_: false,
+
+  /**
    * Returns the encryption options CrRadioGroupElement.
    * @return {?CrRadioGroupElement}
    */
@@ -137,24 +144,26 @@
   saveNewPassphrase_() {
     assert(this.creatingNewPassphrase_);
     chrome.metricsPrivate.recordUserAction('Sync_SaveNewPassphraseClicked');
-    // Might happen within the transient time between the request to
-    // |setSyncEncryption| and receiving the response.
-    if (this.syncPrefs.setNewPassphrase) {
+
+    if (this.isSettingEncryptionPassphrase_) {
       return;
     }
+
     // If a new password has been entered but it is invalid, do not send the
     // sync state to the API.
     if (!this.validateCreatedPassphrases_()) {
       return;
     }
 
-    this.syncPrefs.setNewPassphrase = true;
-    this.syncPrefs.passphrase = this.passphrase_;
-
+    this.isSettingEncryptionPassphrase_ = true;
     settings.SyncBrowserProxyImpl.getInstance()
-        .setSyncEncryption(this.syncPrefs)
-        .then(pageStatus => {
-          this.fire('passphrase-changed', pageStatus);
+        .setEncryptionPassphrase(this.passphrase_)
+        .then(successfullySet => {
+          // TODO(crbug.com/1139060): Rename the event, there is no change if
+          // |successfullySet| is false. It should also mention 'encryption
+          // passphrase' in its name.
+          this.fire('passphrase-changed', {didChange: successfullySet});
+          this.isSettingEncryptionPassphrase_ = false;
         });
   },
 
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index 6469b5f..f31b881 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -491,22 +491,23 @@
       return;
     }
 
-    this.syncPrefs.setNewPassphrase = false;
+    this.browserProxy_.setDecryptionPassphrase(this.existingPassphrase_)
+        .then(
+            sucessfullySet => this.handlePageStatusChanged_(
+                sucessfullySet ? settings.PageStatus.DONE :
+                                 settings.PageStatus.PASSPHRASE_FAILED));
 
-    this.syncPrefs.passphrase = this.existingPassphrase_;
     this.existingPassphrase_ = '';
-
-    this.browserProxy_.setSyncEncryption(this.syncPrefs)
-        .then(this.handlePageStatusChanged_.bind(this));
   },
 
   /**
    * @private
-   * @param {!CustomEvent<!settings.PageStatus>} e
+   * @param {!CustomEvent<!{didChange: boolean}>} e
    */
   onPassphraseChanged_(e) {
     this.handlePageStatusChanged_(
-        /** @type {!settings.PageStatus} */ (e.detail));
+        e.detail.didChange ? settings.PageStatus.DONE :
+                             settings.PageStatus.PASSPHRASE_FAILED);
   },
 
   /**
diff --git a/chrome/browser/search_engines/template_url_service_sync_unittest.cc b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
index e2d2c10..27a851fb 100644
--- a/chrome/browser/search_engines/template_url_service_sync_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
@@ -143,7 +143,8 @@
 
   change_map_.erase(change_map_.begin(), change_map_.end());
   for (auto iter = change_list.begin(); iter != change_list.end(); ++iter)
-    change_map_[GetGUID(iter->sync_data())] = *iter;
+    change_map_.emplace(GetGUID(iter->sync_data()), *iter);
+
   return base::nullopt;
 }
 
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsActivityTestRule.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsActivityTestRule.java
index 250dbd2e..22bcb58 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsActivityTestRule.java
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsActivityTestRule.java
@@ -8,17 +8,15 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.lifecycle.Stage;
 
 import androidx.fragment.app.Fragment;
 
-import org.hamcrest.Matchers;
 import org.junit.Assert;
 
-import org.chromium.base.ActivityState;
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.test.util.Criteria;
-import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.BaseActivityTestRule;
+import org.chromium.base.test.util.ApplicationTestUtils;
+
 /**
  * Activity test rule that launch {@link SettingsActivity} in tests.
  *
@@ -28,7 +26,7 @@
  * @param <T> Fragment that will be attached to the SettingsActivity.
  */
 public class SettingsActivityTestRule<T extends Fragment>
-        extends ActivityTestRule<SettingsActivity> {
+        extends BaseActivityTestRule<SettingsActivity> {
     private final Class<T> mFragmentClass;
 
     /**
@@ -36,16 +34,7 @@
      * @param fragmentClass Fragment that will be attached after the activity starts.
      */
     public SettingsActivityTestRule(Class<T> fragmentClass) {
-        this(fragmentClass, false);
-    }
-
-    /**
-     * Create the settings activity test rule with an specific fragment class.
-     * @param fragmentClass Fragment that will be attached after the activity starts.
-     * @param initialTouchMode Whether in touch mode after the activity starts.
-     */
-    public SettingsActivityTestRule(Class<T> fragmentClass, boolean initialTouchMode) {
-        super(SettingsActivity.class, initialTouchMode, false);
+        super(SettingsActivity.class);
         mFragmentClass = fragmentClass;
     }
 
@@ -67,35 +56,9 @@
         SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
         Intent intent = settingsLauncher.createSettingsActivityIntent(
                 context, mFragmentClass.getName(), fragmentArgs);
-        SettingsActivity activity = super.launchActivity(intent);
-        Assert.assertNotNull(activity);
-
-        return activity;
-    }
-
-    /**
-     * We need to ensure that SettingsActivity gets destroyed in the TestRule because sometimes
-     * it uses the mock signin environment like fake AccountManagerFacade, if the activity starts
-     * with the stub then it also needs to finish with it. That's why we need to wait till the
-     * activity state becomes destroyed before tearing down the mock signin environment.
-     */
-    @Override
-    protected void afterActivityFinished() {
-        super.afterActivityFinished();
-        waitTillActivityIsDestroyed();
-    }
-
-    /**
-     * Block the execution till the SettingsActivity is destroyed.
-     */
-    public void waitTillActivityIsDestroyed() {
-        SettingsActivity activity = getActivity();
-        if (activity != null) {
-            CriteriaHelper.pollUiThread(() -> {
-                Criteria.checkThat(ApplicationStatus.getStateForActivity(activity),
-                        Matchers.is(ActivityState.DESTROYED));
-            });
-        }
+        launchActivity(intent);
+        ApplicationTestUtils.waitForActivityState(getActivity(), Stage.RESUMED);
+        return getActivity();
     }
 
     /**
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
index 887452b..57cfd3c 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
@@ -401,8 +401,6 @@
         dictionary_change->RemoveWord(word);
         break;
       case syncer::SyncChange::ACTION_UPDATE:
-        // Intentionally fall through.
-      case syncer::SyncChange::ACTION_INVALID:
         return syncer::ConvertToModelError(
             sync_error_handler_->CreateAndUploadError(
                 FROM_HERE, "Processing sync changes failed on change type " +
diff --git a/chrome/browser/supervised_user/supervised_user_allowlist_service.cc b/chrome/browser/supervised_user/supervised_user_allowlist_service.cc
index 2d8454a3..f03cf92 100644
--- a/chrome/browser/supervised_user/supervised_user_allowlist_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_allowlist_service.cc
@@ -258,10 +258,6 @@
         allowlists_removed = true;
         break;
       }
-      case syncer::SyncChange::ACTION_INVALID: {
-        NOTREACHED();
-        break;
-      }
     }
   }
 
diff --git a/chrome/browser/supervised_user/supervised_user_settings_service.cc b/chrome/browser/supervised_user/supervised_user_settings_service.cc
index 0460c14d..4682b00 100644
--- a/chrome/browser/supervised_user/supervised_user_settings_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_settings_service.cc
@@ -351,10 +351,6 @@
         dict->RemoveKey(key);
         break;
       }
-      case SyncChange::ACTION_INVALID: {
-        NOTREACHED();
-        break;
-      }
     }
   }
   store_->ReportValueChanged(kAtomicSettings,
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc
index 4a60728..1d2032c 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc
@@ -357,6 +357,7 @@
       prefs::kEnableReferrers,
       !base::FeatureList::IsEnabled(features::kNoReferrers));
   registry->RegisterBooleanPref(prefs::kEnableEncryptedMedia, true);
+  registry->RegisterBooleanPref(prefs::kEnableDRM, true);
   registry->RegisterBooleanPref(prefs::kScrollToTextFragmentEnabled, true);
 #if defined(OS_ANDROID)
   registry->RegisterDoublePref(prefs::kWebKitFontScaleFactor, 1.0);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 06693bc..0a3adea 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -303,13 +303,13 @@
   }
 
   bool should_leave_to_top_container = false;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
   // In immersive mode, the caption buttons container is reparented to the
   // TopContainerView and hence |rect| should not be claimed here.  See
-  // BrowserNonClientFrameViewAsh::OnImmersiveRevealStarted().
+  // BrowserNonClientFrameViewChromeOS::OnImmersiveRevealStarted().
   should_leave_to_top_container =
       browser_view_->immersive_mode_controller()->IsRevealed();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif
 
   if (!browser_view_->IsTabStripVisible()) {
     // Claim |rect| if it is above the top of the topmost client area view.
diff --git a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
new file mode 100644
index 0000000..0410261
--- /dev/null
+++ b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
@@ -0,0 +1,196 @@
+// Copyright 2020 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 "chrome/browser/ui/views/profiles/profile_picker_view.h"
+
+#include "base/check.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/lifetime/browser_shutdown.h"
+#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/views/profiles/profile_picker_test_base.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/keycodes/dom/dom_key.h"
+#include "ui/views/view.h"
+#include "ui/views/view_observer.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace {
+
+// Waits until a view is deleted.
+class ViewDeletedWaiter : public views::ViewObserver {
+ public:
+  explicit ViewDeletedWaiter(views::View* view) {
+    DCHECK(view);
+    observation_.Observe(view);
+  }
+  ~ViewDeletedWaiter() override = default;
+
+  // Waits until the view is deleted.
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  // ViewObserver:
+  void OnViewIsDeleting(views::View* observed_view) override {
+    run_loop_.Quit();
+  }
+
+  base::RunLoop run_loop_;
+  base::ScopedObservation<views::View, views::ViewObserver> observation_{this};
+};
+
+// Waits until the widget bounds change.
+class WidgetBoundsChangeWaiter : public views::WidgetObserver {
+ public:
+  explicit WidgetBoundsChangeWaiter(views::Widget* widget) {
+    DCHECK(widget);
+    observation_.Observe(widget);
+  }
+
+  // Waits until the widget bounds change.
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  // WidgetObserver:
+  void OnWidgetBoundsChanged(views::Widget* widget,
+                             const gfx::Rect& new_bounds) override {
+    run_loop_.Quit();
+  }
+
+  base::RunLoop run_loop_;
+  base::ScopedObservation<views::Widget, views::WidgetObserver> observation_{
+      this};
+};
+
+}  // namespace
+
+class ProfilePickerInteractiveUiTest : public ProfilePickerTestBase {
+ public:
+  ProfilePickerInteractiveUiTest() = default;
+  ~ProfilePickerInteractiveUiTest() override = default;
+
+  void SendCloseWindowKeyboardCommand() {
+    // Close window using keyboard.
+#if defined(OS_MAC)
+    // Use Cmd-W on Mac.
+    bool control = false;
+    bool shift = false;
+    bool command = true;
+#else
+    // Use Ctrl-Shift-W on other platforms.
+    bool control = true;
+    bool shift = true;
+    bool command = false;
+#endif
+    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
+        widget()->GetNativeWindow(), ui::VKEY_W, control, shift, /*alt=*/false,
+        command));
+  }
+
+  void WaitForPickerClosed() {
+    if (!ProfilePicker::IsOpen())
+      return;
+    ViewDeletedWaiter(view()).Wait();
+    ASSERT_FALSE(ProfilePicker::IsOpen());
+  }
+};
+
+// Checks that the main picker view can be closed with keyboard shortcut.
+IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, CloseWithKeyboard) {
+  // Open a new picker.
+  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
+  WaitForNewWebView();
+  WaitForFirstPaint(web_contents(), GURL("chrome://profile-picker"));
+  EXPECT_TRUE(ProfilePicker::IsOpen());
+  SendCloseWindowKeyboardCommand();
+  WaitForPickerClosed();
+  // Closing the picker does not exit Chrome.
+  EXPECT_FALSE(browser_shutdown::IsTryingToQuit());
+}
+
+#if defined(OS_MAC)
+// Checks that Chrome be closed with keyboard shortcut. Only MacOS has a
+// keyboard shortcut to exit Chrome.
+IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, ExitWithKeyboard) {
+  // Open a new picker.
+  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
+  WaitForNewWebView();
+  WaitForFirstPaint(web_contents(), GURL("chrome://profile-picker"));
+  EXPECT_TRUE(ProfilePicker::IsOpen());
+
+  content::WindowedNotificationObserver terminate_observer(
+      chrome::NOTIFICATION_APP_TERMINATING,
+      content::NotificationService::AllSources());
+  // Send Cmd-Q.
+  ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
+      widget()->GetNativeWindow(), ui::VKEY_Q, /*control=*/false,
+      /*shift=*/false, /*alt=*/false, /*command=*/true));
+  // Check that Chrome is quitting.
+  terminate_observer.Wait();
+  WaitForPickerClosed();
+  EXPECT_TRUE(browser_shutdown::IsTryingToQuit());
+}
+#endif
+
+// Checks that the main picker view can switch to full screen.
+IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, FullscreenWithKeyboard) {
+  // Open a new picker.
+  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
+  WaitForNewWebView();
+  WaitForFirstPaint(web_contents(), GURL("chrome://profile-picker"));
+  EXPECT_TRUE(ProfilePicker::IsOpen());
+
+  EXPECT_FALSE(widget()->IsFullscreen());
+  WidgetBoundsChangeWaiter bounds_waiter(widget());
+
+  // Toggle fullscreen with keyboard.
+#if defined(OS_MAC)
+  // Use Cmd-Ctrl-F on Mac.
+  bool control = true;
+  bool command = true;
+  ui::KeyboardCode key_code = ui::VKEY_F;
+#else
+  // Use F11 on other platforms.
+  bool control = false;
+  bool command = false;
+  ui::KeyboardCode key_code = ui::VKEY_F11;
+#endif
+  ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
+      widget()->GetNativeWindow(), key_code, control, /*shift=*/false,
+      /*alt=*/false, command));
+  // Fullscreen causes the bounds of the widget to change.
+  bounds_waiter.Wait();
+  EXPECT_TRUE(widget()->IsFullscreen());
+}
+
+// Checks that the signin web view is able to process keyboard events.
+IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest,
+                       CloseSigninWithKeyboard) {
+  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
+  WaitForNewWebView();
+
+  // Simulate a click on the signin button.
+  base::MockCallback<base::OnceClosure> switch_failure_callback;
+  EXPECT_CALL(switch_failure_callback, Run()).Times(0);
+  ProfilePicker::SwitchToSignIn(SK_ColorRED, switch_failure_callback.Get());
+
+  // Switch to the signin webview.
+  WaitForNewWebView();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+
+  // Close the picker with the keyboard.
+  EXPECT_TRUE(ProfilePicker::IsOpen());
+  SendCloseWindowKeyboardCommand();
+  WaitForPickerClosed();
+}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_test_base.cc b/chrome/browser/ui/views/profiles/profile_picker_test_base.cc
new file mode 100644
index 0000000..ac452a3
--- /dev/null
+++ b/chrome/browser/ui/views/profiles/profile_picker_test_base.cc
@@ -0,0 +1,122 @@
+// Copyright 2020 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 "chrome/browser/ui/views/profiles/profile_picker_test_base.h"
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/ui/profile_picker.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/webview/webview.h"
+#include "ui/views/view.h"
+#include "ui/views/view_observer.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Waits until a web view is added as a child view of the given view.
+class WebViewAddedWaiter : public views::ViewObserver {
+ public:
+  WebViewAddedWaiter(
+      views::View* top_view,
+      base::RepeatingCallback<views::WebView*()> current_web_view_getter)
+      : current_web_view_getter_(current_web_view_getter) {
+    observation_.Observe(top_view);
+  }
+  ~WebViewAddedWaiter() override = default;
+
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  // ViewObserver:
+  void OnChildViewAdded(views::View* observed_view,
+                        views::View* child) override {
+    if (child == current_web_view_getter_.Run()) {
+      ASSERT_TRUE(child);
+      run_loop_.Quit();
+    }
+  }
+
+  base::RunLoop run_loop_;
+  base::RepeatingCallback<views::WebView*()> current_web_view_getter_;
+  base::ScopedObservation<views::View, views::ViewObserver> observation_{this};
+};
+
+// Waits until a first non empty paint for given `url`.
+class FirstVisuallyNonEmptyPaintObserver : public content::WebContentsObserver {
+ public:
+  explicit FirstVisuallyNonEmptyPaintObserver(content::WebContents* contents,
+                                              const GURL& url)
+      : content::WebContentsObserver(contents), url_(url) {}
+
+  // Waits for the first paint.
+  void Wait() {
+    if (IsExitConditionSatisfied()) {
+      return;
+    }
+    run_loop_.Run();
+    EXPECT_TRUE(IsExitConditionSatisfied())
+        << web_contents()->GetVisibleURL() << " != " << url_;
+  }
+
+ private:
+  // WebContentsObserver:
+  void DidFirstVisuallyNonEmptyPaint() override {
+    if (web_contents()->GetVisibleURL() == url_)
+      run_loop_.Quit();
+  }
+
+  bool IsExitConditionSatisfied() {
+    return (web_contents()->GetVisibleURL() == url_ &&
+            web_contents()->CompletedFirstVisuallyNonEmptyPaint());
+  }
+
+  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
+  GURL url_;
+};
+
+}  // namespace
+
+ProfilePickerTestBase::ProfilePickerTestBase() {
+  feature_list_.InitAndEnableFeature(features::kNewProfilePicker);
+}
+
+ProfilePickerTestBase::~ProfilePickerTestBase() = default;
+
+views::View* ProfilePickerTestBase::view() {
+  return ProfilePicker::GetViewForTesting();
+}
+
+views::Widget* ProfilePickerTestBase::widget() {
+  return view() ? view()->GetWidget() : nullptr;
+}
+
+views::WebView* ProfilePickerTestBase::web_view() {
+  return ProfilePicker::GetWebViewForTesting();
+}
+
+void ProfilePickerTestBase::WaitForNewWebView() {
+  ASSERT_TRUE(view());
+  WebViewAddedWaiter(view(),
+                     base::BindRepeating(&ProfilePickerTestBase::web_view,
+                                         base::Unretained(this)))
+      .Wait();
+  EXPECT_TRUE(web_view());
+}
+
+void ProfilePickerTestBase::WaitForFirstPaint(content::WebContents* contents,
+                                              const GURL& url) {
+  DCHECK(contents);
+  FirstVisuallyNonEmptyPaintObserver(contents, url).Wait();
+}
+
+content::WebContents* ProfilePickerTestBase::web_contents() {
+  if (!web_view())
+    return nullptr;
+  return web_view()->GetWebContents();
+}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_test_base.h b/chrome/browser/ui/views/profiles/profile_picker_test_base.h
new file mode 100644
index 0000000..7176045
--- /dev/null
+++ b/chrome/browser/ui/views/profiles/profile_picker_test_base.h
@@ -0,0 +1,50 @@
+// Copyright 2020 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 CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_PICKER_TEST_BASE_H_
+#define CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_PICKER_TEST_BASE_H_
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/in_process_browser_test.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace views {
+class View;
+class WebView;
+class Widget;
+}  // namespace views
+
+class GURL;
+
+class ProfilePickerTestBase : public InProcessBrowserTest {
+ public:
+  ProfilePickerTestBase();
+  ~ProfilePickerTestBase() override;
+
+  // Returns the ProfilePickerView that is currently displayed.
+  views::View* view();
+
+  // Returns the widget associated with the profile picker.
+  views::Widget* widget();
+
+  // Returns the internal web view for the profile picker.
+  views::WebView* web_view();
+
+  // Waits until a new internal web view has been added to the main picker view.
+  void WaitForNewWebView();
+
+  // Waits until the web contents does the first non-empty paint for `url`.
+  void WaitForFirstPaint(content::WebContents* contents, const GURL& url);
+
+  // Gets the picker's web contents.
+  content::WebContents* web_contents();
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_PICKER_TEST_BASE_H_
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index 4e272524..3c0a000 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -10,8 +10,10 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
@@ -24,11 +26,13 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/views/accelerator_table.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h"
 #include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_ui.h"
 #include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
 #include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/google_chrome_strings.h"
@@ -51,6 +55,10 @@
 #include "ui/views/win/hwnd_util.h"
 #endif
 
+#if defined(OS_MAC)
+#include "chrome/browser/global_keyboard_shortcuts_mac.h"
+#endif
+
 namespace {
 
 ProfilePickerView* g_profile_picker_view = nullptr;
@@ -61,6 +69,10 @@
 constexpr base::TimeDelta kExtendedAccountInfoTimeout =
     base::TimeDelta::FromSeconds(10);
 
+constexpr int kSupportedAcceleratorCommands[] = {
+    IDC_CLOSE_TAB, IDC_CLOSE_WINDOW, IDC_EXIT, IDC_FULLSCREEN,
+    IDC_MINIMIZE_WINDOW};
+
 GURL CreateURLForEntryPoint(ProfilePicker::EntryPoint entry_point) {
   GURL base_url = GURL(chrome::kChromeUIProfilePickerUrl);
   switch (entry_point) {
@@ -144,6 +156,7 @@
   SetButtons(ui::DIALOG_BUTTON_NONE);
   SetTitle(IDS_PRODUCT_NAME);
   set_use_custom_frame(false);
+  ConfigureAccelerators();
   // TODO(crbug.com/1063856): Add |RecordDialogCreation|.
 }
 
@@ -201,16 +214,11 @@
 void ProfilePickerView::Init(ProfilePicker::EntryPoint entry_point,
                              Profile* system_profile) {
   DCHECK_EQ(state_, kInitializing);
-  auto web_view = std::make_unique<views::WebView>(system_profile);
-  web_view->GetWebContents()->SetDelegate(this);
+  CreateWebView(system_profile);
+  DCHECK(web_view_);
   // To record metrics using javascript, extensions are needed.
   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
-      web_view->GetWebContents());
-  // Set the member before adding to the hieararchy to make it easier for tests
-  // to detect that a new WebView has been created.
-  web_view_ = web_view.get();
-  AddChildView(std::move(web_view));
-  SetLayoutManager(std::make_unique<views::FillLayout>());
+      web_view_->GetWebContents());
 
   CreateDialogWidget(this, nullptr, nullptr);
 
@@ -309,13 +317,7 @@
   // Rebuild the view.
   // TODO(crbug.com/1126913): Add the simple toolbar with the back button.
   RemoveAllChildViews(true);
-  auto web_view = std::make_unique<views::WebView>(profile);
-  web_view->GetWebContents()->SetDelegate(this);
-  // Set the member before adding to the hieararchy to make it easier for tests
-  // to detect that a new WebView has been created.
-  web_view_ = web_view.get();
-  AddChildView(std::move(web_view));
-  SetLayoutManager(std::make_unique<views::FillLayout>());
+  CreateWebView(profile);
   web_view_->LoadInitialURL(GaiaUrls::GetInstance()->signin_chrome_sync_dice());
   web_view_->RequestFocus();
 }
@@ -359,6 +361,43 @@
   return minimum_size;
 }
 
+bool ProfilePickerView::AcceleratorPressed(const ui::Accelerator& accelerator) {
+  // Ignore presses of the Escape key. The profile picker may be Chrome's only
+  // top-level window, in which case we don't want presses of Esc to maybe quit
+  // the entire browser. This has higher priority than the default dialog Esc
+  // accelerator (which would otherwise close the window).
+  if (accelerator.key_code() == ui::VKEY_ESCAPE &&
+      accelerator.modifiers() == ui::EF_NONE) {
+    return true;
+  }
+
+  const auto& iter = accelerator_table_.find(accelerator);
+  DCHECK(iter != accelerator_table_.end());
+  int command_id = iter->second;
+  switch (command_id) {
+    case IDC_CLOSE_TAB:
+    case IDC_CLOSE_WINDOW:
+      // kEscKeyPressed is used although that shortcut is disabled (this is
+      // Ctrl-Shift-W instead).
+      GetWidget()->CloseWithReason(views::Widget::ClosedReason::kEscKeyPressed);
+      break;
+    case IDC_EXIT:
+      chrome::AttemptUserExit();
+      break;
+    case IDC_FULLSCREEN:
+      GetWidget()->SetFullscreen(!GetWidget()->IsFullscreen());
+      break;
+    case IDC_MINIMIZE_WINDOW:
+      GetWidget()->Minimize();
+      break;
+    default:
+      NOTREACHED() << "Unexpected command_id: " << command_id;
+      break;
+  }
+
+  return true;
+}
+
 bool ProfilePickerView::HandleContextMenu(
     content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
@@ -366,6 +405,15 @@
   return true;
 }
 
+bool ProfilePickerView::HandleKeyboardEvent(
+    content::WebContents* source,
+    const content::NativeWebKeyboardEvent& event) {
+  // Forward the keyboard event to AcceleratorPressed() through the
+  // FocusManager.
+  return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+      event, GetFocusManager());
+}
+
 void ProfilePickerView::AddNewContents(
     content::WebContents* source,
     std::unique_ptr<content::WebContents> new_contents,
@@ -487,6 +535,10 @@
   entry->SetIsEphemeral(false);
   entry->SetLocalProfileName(name_for_signed_in_profile_);
 
+  // Skip the FRE for this profile as it's replaced by profile creation flow.
+  signed_in_profile_being_created_->GetPrefs()->SetBoolean(
+      prefs::kHasSeenWelcomePage, true);
+
   // TODO(crbug.com/1126913): Change the callback of
   // profiles::OpenBrowserWindowForProfile() to be a OnceCallback as it is only
   // called once.
@@ -521,3 +573,44 @@
   DCHECK(browser);
   std::move(finish_flow_callback).Run(browser);
 }
+
+void ProfilePickerView::ConfigureAccelerators() {
+  // By default, dialog views close when pressing escape. Override this
+  // behavior as the profile picker should not close in that case.
+  AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
+
+  const std::vector<AcceleratorMapping> accelerator_list(GetAcceleratorList());
+  for (const auto& entry : accelerator_list) {
+    if (!base::Contains(kSupportedAcceleratorCommands, entry.command_id))
+      continue;
+    ui::Accelerator accelerator(entry.keycode, entry.modifiers);
+    accelerator_table_[accelerator] = entry.command_id;
+    AddAccelerator(accelerator);
+  }
+
+#if defined(OS_MAC)
+  // Check Mac-specific accelerators. Note: Chrome does not support dynamic or
+  // user-configured accelerators on Mac. Default static accelerators are used
+  // instead.
+  for (int command_id : kSupportedAcceleratorCommands) {
+    ui::Accelerator accelerator;
+    bool mac_accelerator_found =
+        GetDefaultMacAcceleratorForCommandId(command_id, &accelerator);
+    if (mac_accelerator_found) {
+      accelerator_table_[accelerator] = command_id;
+      AddAccelerator(accelerator);
+    }
+  }
+#endif  // OS_MAC
+}
+
+void ProfilePickerView::CreateWebView(Profile* profile) {
+  auto web_view = std::make_unique<views::WebView>(profile);
+  web_view->GetWebContents()->SetDelegate(this);
+  web_view->set_allow_accelerators(true);
+  // Set the member before adding to the hieararchy to make it easier for tests
+  // to detect that a new WebView has been created.
+  web_view_ = web_view.get();
+  AddChildView(std::move(web_view));
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h
index 7c470c2a..9141802 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -11,6 +11,7 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/window/dialog_delegate.h"
 
@@ -72,6 +73,7 @@
 
   // views::View;
   gfx::Size GetMinimumSize() const override;
+  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
   // content::WebContentsDelegate:
   bool HandleContextMenu(content::RenderFrameHost* render_frame_host,
@@ -83,6 +85,9 @@
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
                       bool* was_blocked) override;
+  bool HandleKeyboardEvent(
+      content::WebContents* source,
+      const content::NativeWebKeyboardEvent& event) override;
 
   // IdentityManager::Observer:
   void OnRefreshTokenUpdatedForAccount(
@@ -104,9 +109,22 @@
                        Profile* profile,
                        Profile::CreateStatus profile_create_status);
 
+  // Register basic keyboard accelerators such as closing the window (Alt-F4
+  // on Windows).
+  void ConfigureAccelerators();
+
+  // Creates and configures the internal web view, and adds it as a child view.
+  void CreateWebView(Profile* profile);
+
   ScopedKeepAlive keep_alive_;
   State state_ = State::kNotStarted;
 
+  // A mapping between accelerators and command IDs.
+  std::map<ui::Accelerator, int> accelerator_table_;
+
+  // Handler for unhandled key events from renderer.
+  views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
+
   // The current WebView object, owned by the view hierarchy.
   views::WebView* web_view_ = nullptr;
 
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index c22da81..f7f4af18 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/profiles/profile_picker_test_base.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -38,12 +39,12 @@
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
-#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/views/view_observer.h"
+#include "url/gurl.h"
 
 namespace {
 
@@ -69,64 +70,6 @@
   return account_info;
 }
 
-// Waits until a first non empty paint for given `url`.
-class FirstVisuallyNonEmptyPaintObserver : public content::WebContentsObserver {
- public:
-  explicit FirstVisuallyNonEmptyPaintObserver(content::WebContents* contents,
-                                              const GURL& url)
-      : content::WebContentsObserver(contents), url_(url) {}
-
-  void DidFirstVisuallyNonEmptyPaint() override {
-    if (web_contents()->GetVisibleURL() == url_)
-      run_loop_.Quit();
-  }
-
-  void Wait() {
-    if (IsExitConditionSatisfied()) {
-      return;
-    }
-    run_loop_.Run();
-    EXPECT_TRUE(IsExitConditionSatisfied())
-        << web_contents()->GetVisibleURL() << " != " << url_;
-  }
-
- private:
-  bool IsExitConditionSatisfied() {
-    return (web_contents()->GetVisibleURL() == url_ &&
-            web_contents()->CompletedFirstVisuallyNonEmptyPaint());
-  }
-
-  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
-  GURL url_;
-};
-
-class WebViewAddedWaiter : public views::ViewObserver {
- public:
-  WebViewAddedWaiter(
-      views::View* top_view,
-      base::RepeatingCallback<views::WebView*()> current_web_view_getter)
-      : current_web_view_getter_(current_web_view_getter) {
-    observation_.Observe(top_view);
-  }
-  ~WebViewAddedWaiter() override = default;
-
-  void Wait() { run_loop_.Run(); }
-
- private:
-  // ViewObserver:
-  void OnChildViewAdded(views::View* observed_view,
-                        views::View* child) override {
-    if (child == current_web_view_getter_.Run()) {
-      ASSERT_TRUE(child);
-      run_loop_.Quit();
-    }
-  }
-
-  base::RunLoop run_loop_;
-  base::RepeatingCallback<views::WebView*()> current_web_view_getter_;
-  base::ScopedObservation<views::View, views::ViewObserver> observation_{this};
-};
-
 class BrowserAddedWaiter : public BrowserListObserver {
  public:
   explicit BrowserAddedWaiter(size_t total_count) : total_count_(total_count) {
@@ -256,15 +199,14 @@
   base::RunLoop* run_loop_;
 };
 
-class ProfilePickerCreationFlowBrowserTest : public InProcessBrowserTest {
+class ProfilePickerCreationFlowBrowserTest : public ProfilePickerTestBase {
  public:
   ProfilePickerCreationFlowBrowserTest() {
-    feature_list_.InitWithFeatures(
-        {features::kProfilesUIRevamp, features::kNewProfilePicker}, {});
+    feature_list_.InitAndEnableFeature(features::kProfilesUIRevamp);
   }
 
   void SetUpInProcessBrowserTestFixture() override {
-    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+    ProfilePickerTestBase::SetUpInProcessBrowserTestFixture();
     create_services_subscription_ =
         BrowserContextDependencyManager::GetInstance()
             ->RegisterCreateServicesCallbackForTesting(
@@ -279,26 +221,6 @@
         context, base::BindRepeating(&FakeUserPolicySigninService::Build));
   }
 
-  views::View* view() { return ProfilePicker::GetViewForTesting(); }
-
-  views::WebView* web_view() { return ProfilePicker::GetWebViewForTesting(); }
-
-  void WaitForNewWebView() {
-    ASSERT_TRUE(view());
-    WebViewAddedWaiter(
-        view(),
-        base::BindRepeating(&ProfilePickerCreationFlowBrowserTest::web_view,
-                            base::Unretained(this)))
-        .Wait();
-    EXPECT_TRUE(web_view());
-  }
-
-  content::WebContents* web_contents() {
-    if (!web_view())
-      return nullptr;
-    return web_view()->GetWebContents();
-  }
-
  private:
   base::CallbackListSubscription create_services_subscription_;
   base::test::ScopedFeatureList feature_list_;
@@ -324,9 +246,8 @@
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
   WaitForNewWebView();
   EXPECT_TRUE(ProfilePicker::IsOpen());
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GURL("chrome://profile-picker/new-profile"))
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GURL("chrome://profile-picker/new-profile"));
 }
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
@@ -344,9 +265,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Add an account - simulate a successful Gaia sign-in.
   Profile* profile_being_created =
@@ -365,18 +285,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  FirstVisuallyNonEmptyPaintObserver(web_contents(),
-                                     GURL("chrome://sync-confirmation/"))
-      .Wait();
+  WaitForFirstPaint(web_contents(), GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "Yes, I'm in".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://newtab/"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
 
   // Check expectations when the profile creation flow is done.
   EXPECT_FALSE(ProfilePicker::IsOpen());
@@ -411,9 +327,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Disable sync by setting the device as managed in prefs.
   Profile* profile_being_created =
@@ -437,10 +352,8 @@
   // Wait for the sign-in to propagate to the flow, resulting in new browser
   // getting opened.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://newtab/"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
 
   EXPECT_FALSE(ProfilePicker::IsOpen());
 
@@ -478,9 +391,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Add an account - simulate a successful Gaia sign-in.
   Profile* profile_being_created =
@@ -497,18 +409,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  FirstVisuallyNonEmptyPaintObserver(web_contents(),
-                                     GURL("chrome://sync-confirmation/"))
-      .Wait();
+  WaitForFirstPaint(web_contents(), GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "Yes, I'm in".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::CONFIGURE_SYNC_FIRST);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://settings/syncSetup"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://settings/syncSetup"));
 
   // Check expectations when the profile creation flow is done.
   EXPECT_FALSE(ProfilePicker::IsOpen());
@@ -544,9 +452,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Simulate clicking on a link that opens in a new window.
   const GURL kURL("https://foo.google.com");
@@ -561,9 +468,8 @@
   // A new pppup browser is displayed (with the specified URL).
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
   EXPECT_EQ(new_browser->type(), Browser::TYPE_POPUP);
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(), kURL)
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    kURL);
 }
 
 // TODO(crbug.com/1144065): Flaky on multiple platforms.
@@ -606,9 +512,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Add an account - simulate a successful Gaia sign-in.
   Profile* profile_being_created =
@@ -625,10 +530,8 @@
 
   // Instead of sync confirmation, a browser is displayed (with a login error).
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://newtab/"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
 
   // Check expectations when the profile creation flow is done.
   EXPECT_FALSE(ProfilePicker::IsOpen());
@@ -659,9 +562,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   Profile* profile_being_created =
       static_cast<Profile*>(web_view()->GetBrowserContext());
@@ -680,18 +582,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  FirstVisuallyNonEmptyPaintObserver(web_contents(),
-                                     GURL("chrome://sync-confirmation/"))
-      .Wait();
+  WaitForFirstPaint(web_contents(), GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "Yes, I'm in".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://newtab/"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
 
   // Check expectations when the profile creation flow is done.
   EXPECT_FALSE(ProfilePicker::IsOpen());
@@ -740,9 +638,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Add an account - simulate a successful Gaia sign-in.
   Profile* profile_being_created =
@@ -775,10 +672,8 @@
   // The picker should be closed even before the enterprise confirmation but it
   // is closed asynchronously after opening the browser so after the NTP
   // renders, it is safe to check.
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://newtab/"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
   EXPECT_FALSE(ProfilePicker::IsOpen());
 
   // Now the sync consent screen is shown, simulate closing the UI with "Yes,
@@ -814,9 +709,8 @@
   // The DICE navigation happens in a new web view (for the profile being
   // created), wait for it.
   WaitForNewWebView();
-  FirstVisuallyNonEmptyPaintObserver(
-      web_contents(), GaiaUrls::GetInstance()->signin_chrome_sync_dice())
-      .Wait();
+  WaitForFirstPaint(web_contents(),
+                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Add an account - simulate a successful Gaia sign-in.
   Profile* profile_being_created =
@@ -849,10 +743,8 @@
   // "Configure sync".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::CONFIGURE_SYNC_FIRST);
-  FirstVisuallyNonEmptyPaintObserver(
-      new_browser->tab_strip_model()->GetActiveWebContents(),
-      GURL("chrome://settings/syncSetup"))
-      .Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://settings/syncSetup"));
 
   // Check expectations when the profile creation flow is done.
   EXPECT_FALSE(ProfilePicker::IsOpen());
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 66cfbd2b..b31cbad 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -84,8 +84,6 @@
   bool sync_everything;
   syncer::UserSelectableTypeSet selected_types;
   bool payments_integration_enabled;
-  std::string passphrase;
-  bool set_new_passphrase;
 };
 
 bool IsSyncSubpage(const GURL& current_url) {
@@ -93,9 +91,7 @@
 }
 
 SyncConfigInfo::SyncConfigInfo()
-    : sync_everything(false),
-      payments_integration_enabled(false),
-      set_new_passphrase(false) {}
+    : sync_everything(false), payments_integration_enabled(false) {}
 
 SyncConfigInfo::~SyncConfigInfo() {}
 
@@ -132,13 +128,6 @@
       config->selected_types.Put(type);
   }
 
-  // Passphrase settings.
-  if (result->GetString("passphrase", &config->passphrase) &&
-      !config->passphrase.empty() &&
-      !result->GetBoolean("setNewPassphrase", &config->set_new_passphrase)) {
-    DLOG(ERROR) << "GetConfiguration() not passed a set_new_passphrase value";
-    return false;
-  }
   return true;
 }
 
@@ -276,8 +265,12 @@
       base::BindRepeating(&PeopleHandler::HandleSetDatatypes,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "SyncSetupSetEncryption",
-      base::BindRepeating(&PeopleHandler::HandleSetEncryption,
+      "SyncSetupSetEncryptionPassphrase",
+      base::BindRepeating(&PeopleHandler::HandleSetEncryptionPassphrase,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "SyncSetupSetDecryptionPassphrase",
+      base::BindRepeating(&PeopleHandler::HandleSetDecryptionPassphrase,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "SyncSetupShowSetupUI",
@@ -527,79 +520,72 @@
 #endif
 }
 
-void PeopleHandler::HandleSetEncryption(const base::ListValue* args) {
-  SyncConfigInfo configuration;
-  const base::Value* callback_id = nullptr;
-  ParseConfigurationArguments(args, &configuration, &callback_id);
+void PeopleHandler::HandleSetEncryptionPassphrase(const base::ListValue* args) {
+  const base::Value& callback_id = args->GetList()[0];
 
-  // Start configuring the SyncService using the configuration passed to us from
-  // the JS layer.
-  syncer::SyncService* service = GetSyncService();
-
-  // If the sync engine has shutdown for some reason, just close the sync
-  // dialog.
-  if (!service || !service->IsEngineInitialized()) {
+  // Check the SyncService is up and running before retrieving SyncUserSettings,
+  // which contains the encryption-related APIs.
+  if (!GetSyncService() || !GetSyncService()->IsEngineInitialized()) {
+    // TODO(crbug.com/1139060): HandleSetDatatypes() also returns a success
+    // status in this case. Consider returning a failure in both methods. Maybe
+    // the CloseSyncSetup() call can also be removed.
     CloseSyncSetup();
-    ResolveJavascriptCallback(*callback_id, base::Value(kDonePageStatus));
+    ResolveJavascriptCallback(callback_id, base::Value(true));
     return;
   }
+  syncer::SyncUserSettings* sync_user_settings =
+      GetSyncService()->GetUserSettings();
 
-  if (service->GetUserSettings()->IsEncryptEverythingAllowed()) {
-    ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
+  const std::string& passphrase = args->GetList()[1].GetString();
+  bool successfully_set = false;
+  if (passphrase.empty()) {
+    successfully_set = false;
+  } else if (!sync_user_settings->IsEncryptEverythingAllowed()) {
+    successfully_set = false;
+  } else if (sync_user_settings->IsUsingSecondaryPassphrase()) {
+    // In case a passphrase is already being used, changing to a new one isn't
+    // currently supported (one must reset all the Sync data).
+    successfully_set = false;
+  } else if (sync_user_settings->IsPassphraseRequired() ||
+             sync_user_settings->IsTrustedVaultKeyRequired()) {
+    // Can't re-encrypt the data with |passphrase| if some of it hasn't even
+    // been decrypted yet due to a pending passphrase / trusted vault key.
+    successfully_set = false;
   } else {
-    // Don't allow "set new passphrase" if the SyncService doesn't allow it.
-    // The UI is hidden, but the user may have enabled it e.g. by fiddling with
-    // the web inspector.
-    configuration.set_new_passphrase = false;
+    sync_user_settings->SetEncryptionPassphrase(passphrase);
+    successfully_set = true;
+    ProfileMetrics::LogProfileSyncInfo(
+        ProfileMetrics::SYNC_CREATED_NEW_PASSPHRASE);
   }
+  ResolveJavascriptCallback(callback_id, base::Value(successfully_set));
+}
 
-  bool passphrase_failed = false;
-  if (!configuration.passphrase.empty()) {
-    // We call IsPassphraseRequired() here (instead of
-    // IsPassphraseRequiredForPreferredDataTypes()) because the user may try to
-    // enter a passphrase even though no encrypted data types are enabled.
-    if (service->GetUserSettings()->IsPassphraseRequired()) {
-      // If we have pending keys, try to decrypt them with the provided
-      // passphrase. We track if this succeeds or fails because a failed
-      // decryption should result in an error even if there aren't any encrypted
-      // data types.
-      passphrase_failed = !service->GetUserSettings()->SetDecryptionPassphrase(
-          configuration.passphrase);
-    } else if (service->GetUserSettings()->IsTrustedVaultKeyRequired()) {
-      // There are pending keys due to trusted vault keys being required, likely
-      // because something changed since the UI was displayed. A passphrase
-      // cannot be set in such circumstances.
-      passphrase_failed = true;
-    } else {
-      // OK, the user sent us a passphrase, but we don't have pending keys. So
-      // it either means that the pending keys were resolved somehow since the
-      // time the UI was displayed (re-encryption, pending passphrase change,
-      // etc) or the user wants to re-encrypt.
-      if (configuration.set_new_passphrase &&
-          !service->GetUserSettings()->IsUsingSecondaryPassphrase()) {
-        service->GetUserSettings()->SetEncryptionPassphrase(
-            configuration.passphrase);
-      }
+void PeopleHandler::HandleSetDecryptionPassphrase(const base::ListValue* args) {
+  const base::Value& callback_id = args->GetList()[0];
+
+  // Check the SyncService is up and running before retrieving SyncUserSettings,
+  // which contains the encryption-related APIs.
+  if (!GetSyncService() || !GetSyncService()->IsEngineInitialized()) {
+    // TODO(crbug.com/1139060): HandleSetDatatypes() also returns a success
+    // status in this case. Consider returning a failure in both methods. Maybe
+    // the CloseSyncSetup() call can also be removed.
+    CloseSyncSetup();
+    ResolveJavascriptCallback(callback_id, base::Value(true));
+    return;
+  }
+  syncer::SyncUserSettings* sync_user_settings =
+      GetSyncService()->GetUserSettings();
+
+  const std::string& passphrase = args->GetList()[1].GetString();
+  bool successfully_set = false;
+  if (!passphrase.empty() && sync_user_settings->IsPassphraseRequired()) {
+    successfully_set = sync_user_settings->SetDecryptionPassphrase(passphrase);
+    if (successfully_set) {
+      ProfileMetrics::LogProfileSyncInfo(
+          ProfileMetrics::SYNC_ENTERED_EXISTING_PASSPHRASE);
     }
   }
-
-  if (passphrase_failed ||
-      service->GetUserSettings()->IsPassphraseRequiredForPreferredDataTypes()) {
-    // If the user doesn't enter any passphrase, we won't call
-    // SetDecryptionPassphrase() (passphrase_failed == false), but we still
-    // want to display an error message to let the user know that their blank
-    // passphrase entry is not acceptable.
-
-    // TODO(tommycli): Switch this to RejectJavascriptCallback once the
-    // Sync page JavaScript has been further refactored.
-    ResolveJavascriptCallback(*callback_id,
-                              base::Value(kPassphraseFailedPageStatus));
-  } else {
-    ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus));
-  }
-
-  if (!configuration.set_new_passphrase && !configuration.passphrase.empty())
-    ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
+  ResolveJavascriptCallback(callback_id, base::Value(successfully_set));
 }
 
 void PeopleHandler::HandleShowSyncSetupUI(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index b6013d93..c50237f 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -87,14 +87,16 @@
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncEverything);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncAllManually);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, NonRegisteredType);
-  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestPassphraseStillRequired);
+  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, EnterCorrectExistingPassphrase);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncIndividualTypes);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
-                           EnterExistingFrozenImplicitPassword);
-  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, SetNewCustomPassphrase);
+                           SuccessfullyCreateCustomPassphrase);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, EnterWrongExistingPassphrase);
-  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, EnterBlankExistingPassphrase);
-  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TurnOnEncryptAllDisallowed);
+  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, CannotCreateBlankPassphrase);
+  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
+                           CannotCreatePassphraseIfEncryptEverythingDisallowed);
+  FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
+                           CannotOverwritePassphraseWithNewOne);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
                            UnrecoverableErrorInitializingSync);
   FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, GaiaErrorInitializingSync);
@@ -152,7 +154,8 @@
   void HandleGetProfileInfo(const base::ListValue* args);
   void OnDidClosePage(const base::ListValue* args);
   void HandleSetDatatypes(const base::ListValue* args);
-  void HandleSetEncryption(const base::ListValue* args);
+  void HandleSetEncryptionPassphrase(const base::ListValue* args);
+  void HandleSetDecryptionPassphrase(const base::ListValue* args);
   void HandleShowSyncSetupUI(const base::ListValue* args);
   void HandleSyncPrefsDispatch(const base::ListValue* args);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index ccc0fcaf..1700d9e7 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -78,26 +78,12 @@
   CHOOSE_WHAT_TO_SYNC
 };
 
-enum EncryptAllConfig {
-  ENCRYPT_ALL_DATA,
-  ENCRYPT_PASSWORDS
-};
-
 // Create a json-format string with the key/value pairs appropriate for a call
-// to HandleSetEncryption(). If |extra_values| is non-null, then the values from
-// the passed dictionary are added to the json.
-std::string GetConfiguration(const base::DictionaryValue* extra_values,
-                             SyncAllDataConfig sync_all,
-                             syncer::UserSelectableTypeSet types,
-                             const std::string& passphrase,
-                             EncryptAllConfig encrypt_all) {
+// to HandleSetDatatypes().
+std::string GetConfiguration(SyncAllDataConfig sync_all,
+                             syncer::UserSelectableTypeSet types) {
   base::DictionaryValue result;
-  if (extra_values)
-    result.MergeDictionary(extra_values);
   result.SetBoolean("syncAllDataTypes", sync_all == SYNC_ALL_DATA);
-  result.SetBoolean("encryptAllData", encrypt_all == ENCRYPT_ALL_DATA);
-  if (!passphrase.empty())
-    result.SetString("passphrase", passphrase);
   // Add all of our data types.
   result.SetBoolean("appsSynced", types.Has(syncer::UserSelectableType::kApps));
   result.SetBoolean("autofillSynced",
@@ -314,6 +300,21 @@
     EXPECT_EQ(expected_status, status);
   }
 
+  // Expects a call to ResolveJavascriptCallback() with |should_succeed| as its
+  // argument.
+  void ExpectSetPassphraseSuccess(bool should_succeed) {
+    EXPECT_EQ(1u, web_ui_.call_data().size());
+    const auto& data = *web_ui_.call_data()[0];
+    EXPECT_EQ("cr.webUIResponse", data.function_name());
+    EXPECT_TRUE(data.arg2()->is_bool());
+    EXPECT_TRUE(data.arg2()->GetBool())
+        << "Callback should be resolved with a boolean indicating the success, "
+           "never rejected.";
+
+    EXPECT_TRUE(data.arg3()->is_bool());
+    EXPECT_EQ(should_succeed, data.arg3()->GetBool());
+  }
+
   const base::DictionaryValue* ExpectSyncPrefsChanged() {
     const content::TestWebUI::CallData& data1 = *web_ui_.call_data().back();
     EXPECT_EQ("cr.webUIListenerCallback", data1.function_name());
@@ -638,8 +639,7 @@
 TEST_F(PeopleHandlerTest, TestSyncEverything) {
   SigninUser();
   CreatePeopleHandler();
-  std::string args = GetConfiguration(nullptr, SYNC_ALL_DATA, GetAllTypes(),
-                                      std::string(), ENCRYPT_PASSWORDS);
+  std::string args = GetConfiguration(SYNC_ALL_DATA, GetAllTypes());
   base::ListValue list_args;
   list_args.AppendString(kTestCallbackId);
   list_args.AppendString(args);
@@ -656,147 +656,119 @@
   ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
 }
 
-TEST_F(PeopleHandlerTest, TestPassphraseStillRequired) {
+TEST_F(PeopleHandlerTest, EnterCorrectExistingPassphrase) {
   SigninUser();
   CreatePeopleHandler();
-  std::string args = GetConfiguration(nullptr, SYNC_ALL_DATA, GetAllTypes(),
-                                      std::string(), ENCRYPT_PASSWORDS);
-  base::ListValue list_args;
-  list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
-  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsPassphraseRequiredForPreferredDataTypes())
-      .WillByDefault(Return(true));
+  SetupInitializedSyncService();
+
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired())
       .WillByDefault(Return(true));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsUsingSecondaryPassphrase())
-      .WillByDefault(Return(false));
-  SetupInitializedSyncService();
-  SetDefaultExpectationsForConfigPage();
-
-  handler_->HandleSetEncryption(&list_args);
-  // We should navigate back to the configure page since we need a passphrase.
-  ExpectPageStatusResponse(PeopleHandler::kPassphraseFailedPageStatus);
-}
-
-TEST_F(PeopleHandlerTest, EnterExistingFrozenImplicitPassword) {
-  SigninUser();
-  CreatePeopleHandler();
-  base::DictionaryValue dict;
-  dict.SetBoolean("setNewPassphrase", false);
-  std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
-                                      "oldGaiaPassphrase", ENCRYPT_PASSWORDS);
-  base::ListValue list_args;
-  list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
-  // Act as if an encryption passphrase is required the first time, then never
-  // again after that.
-  EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
-              IsPassphraseRequired())
-      .WillOnce(Return(true));
-  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsPassphraseRequiredForPreferredDataTypes())
+          IsTrustedVaultKeyRequired())
       .WillByDefault(Return(false));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
           IsUsingSecondaryPassphrase())
-      .WillByDefault(Return(false));
-  SetupInitializedSyncService();
-  EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
-              SetDecryptionPassphrase("oldGaiaPassphrase"))
-      .WillOnce(Return(true));
-
-  handler_->HandleSetEncryption(&list_args);
-  ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
-}
-
-TEST_F(PeopleHandlerTest, SetNewCustomPassphrase) {
-  SigninUser();
-  CreatePeopleHandler();
-  base::DictionaryValue dict;
-  dict.SetBoolean("setNewPassphrase", true);
-  std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
-                                      "custom_passphrase", ENCRYPT_ALL_DATA);
-  base::ListValue list_args;
-  list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
+      .WillByDefault(Return(true));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
           IsEncryptEverythingAllowed())
       .WillByDefault(Return(true));
-  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsPassphraseRequiredForPreferredDataTypes())
-      .WillByDefault(Return(false));
+
+  EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
+              SetDecryptionPassphrase("correct_passphrase"))
+      .WillOnce(Return(true));
+
+  base::ListValue list_args;
+  list_args.AppendString(kTestCallbackId);
+  list_args.AppendString("correct_passphrase");
+  handler_->HandleSetDecryptionPassphrase(&list_args);
+
+  ExpectSetPassphraseSuccess(true);
+}
+
+TEST_F(PeopleHandlerTest, SuccessfullyCreateCustomPassphrase) {
+  SigninUser();
+  CreatePeopleHandler();
+  SetupInitializedSyncService();
+
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired())
       .WillByDefault(Return(false));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsTrustedVaultKeyRequired())
+      .WillByDefault(Return(false));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
           IsUsingSecondaryPassphrase())
       .WillByDefault(Return(false));
-  SetupInitializedSyncService();
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsEncryptEverythingAllowed())
+      .WillByDefault(Return(true));
+
   EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
               SetEncryptionPassphrase("custom_passphrase"));
 
-  handler_->HandleSetEncryption(&list_args);
-  ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+  base::ListValue list_args;
+  list_args.AppendString(kTestCallbackId);
+  list_args.AppendString("custom_passphrase");
+  handler_->HandleSetEncryptionPassphrase(&list_args);
+
+  ExpectSetPassphraseSuccess(true);
 }
 
 TEST_F(PeopleHandlerTest, EnterWrongExistingPassphrase) {
   SigninUser();
   CreatePeopleHandler();
-  base::DictionaryValue dict;
-  dict.SetBoolean("setNewPassphrase", false);
-  std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
-                                      "invalid_passphrase", ENCRYPT_ALL_DATA);
-  base::ListValue list_args;
-  list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
-  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsPassphraseRequiredForPreferredDataTypes())
-      .WillByDefault(Return(true));
+  SetupInitializedSyncService();
+
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired())
       .WillByDefault(Return(true));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsUsingSecondaryPassphrase())
+          IsTrustedVaultKeyRequired())
       .WillByDefault(Return(false));
-  SetupInitializedSyncService();
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsUsingSecondaryPassphrase())
+      .WillByDefault(Return(true));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsEncryptEverythingAllowed())
+      .WillByDefault(Return(true));
+
   EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
               SetDecryptionPassphrase("invalid_passphrase"))
       .WillOnce(Return(false));
 
-  SetDefaultExpectationsForConfigPage();
-
-  handler_->HandleSetEncryption(&list_args);
-  // We should navigate back to the configure page since we need a passphrase.
-  ExpectPageStatusResponse(PeopleHandler::kPassphraseFailedPageStatus);
-}
-
-TEST_F(PeopleHandlerTest, EnterBlankExistingPassphrase) {
-  SigninUser();
-  CreatePeopleHandler();
-  base::DictionaryValue dict;
-  dict.SetBoolean("setNewPassphrase", false);
-  std::string args = GetConfiguration(&dict,
-                                      SYNC_ALL_DATA,
-                                      GetAllTypes(),
-                                      "",
-                                      ENCRYPT_PASSWORDS);
   base::ListValue list_args;
   list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
-  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsPassphraseRequiredForPreferredDataTypes())
-      .WillByDefault(Return(true));
+  list_args.AppendString("invalid_passphrase");
+  handler_->HandleSetDecryptionPassphrase(&list_args);
+
+  ExpectSetPassphraseSuccess(false);
+}
+
+TEST_F(PeopleHandlerTest, CannotCreateBlankPassphrase) {
+  SigninUser();
+  CreatePeopleHandler();
+  SetupInitializedSyncService();
+
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired())
-      .WillByDefault(Return(true));
+      .WillByDefault(Return(false));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsTrustedVaultKeyRequired())
+      .WillByDefault(Return(false));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
           IsUsingSecondaryPassphrase())
       .WillByDefault(Return(false));
-  SetupInitializedSyncService();
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsEncryptEverythingAllowed())
+      .WillByDefault(Return(true));
 
-  SetDefaultExpectationsForConfigPage();
+  EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
+              SetEncryptionPassphrase)
+      .Times(0);
 
-  handler_->HandleSetEncryption(&list_args);
-  // We should navigate back to the configure page since we need a passphrase.
-  ExpectPageStatusResponse(PeopleHandler::kPassphraseFailedPageStatus);
+  base::ListValue list_args;
+  list_args.AppendString(kTestCallbackId);
+  list_args.AppendString("");
+  handler_->HandleSetEncryptionPassphrase(&list_args);
+
+  ExpectSetPassphraseSuccess(false);
 }
 
 // Walks through each user selectable type, and tries to sync just that single
@@ -808,9 +780,7 @@
   for (syncer::UserSelectableType type : GetAllTypes()) {
     syncer::UserSelectableTypeSet type_to_set;
     type_to_set.Put(type);
-    std::string args =
-        GetConfiguration(nullptr, CHOOSE_WHAT_TO_SYNC, type_to_set,
-                         std::string(), ENCRYPT_PASSWORDS);
+    std::string args = GetConfiguration(CHOOSE_WHAT_TO_SYNC, type_to_set);
     base::ListValue list_args;
     list_args.AppendString(kTestCallbackId);
     list_args.AppendString(args);
@@ -833,9 +803,7 @@
   SigninUser();
   CreatePeopleHandler();
   SetDefaultExpectationsForConfigPage();
-  std::string args =
-      GetConfiguration(nullptr, CHOOSE_WHAT_TO_SYNC, GetAllTypes(),
-                       std::string(), ENCRYPT_PASSWORDS);
+  std::string args = GetConfiguration(CHOOSE_WHAT_TO_SYNC, GetAllTypes());
   base::ListValue list_args;
   list_args.AppendString(kTestCallbackId);
   list_args.AppendString(args);
@@ -867,9 +835,7 @@
 
   // Simulate "Sync everything" being turned off, but all individual
   // toggles left on.
-  std::string config =
-      GetConfiguration(/*extra_values=*/nullptr, CHOOSE_WHAT_TO_SYNC,
-                       GetAllTypes(), std::string(), ENCRYPT_PASSWORDS);
+  std::string config = GetConfiguration(CHOOSE_WHAT_TO_SYNC, GetAllTypes());
   base::ListValue list_args;
   list_args.AppendString(kTestCallbackId);
   list_args.AppendString(config);
@@ -1078,34 +1044,62 @@
   CheckBool(dictionary, "encryptAllDataAllowed", false);
 }
 
-TEST_F(PeopleHandlerTest, TurnOnEncryptAllDisallowed) {
+TEST_F(PeopleHandlerTest, CannotCreatePassphraseIfEncryptEverythingDisallowed) {
   SigninUser();
   CreatePeopleHandler();
-  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
-          IsPassphraseRequiredForPreferredDataTypes())
-      .WillByDefault(Return(false));
+  SetupInitializedSyncService();
+
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired())
       .WillByDefault(Return(false));
-  SetupInitializedSyncService();
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsTrustedVaultKeyRequired())
+      .WillByDefault(Return(false));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsUsingSecondaryPassphrase())
+      .WillByDefault(Return(false));
   ON_CALL(*mock_sync_service_->GetMockUserSettings(),
           IsEncryptEverythingAllowed())
       .WillByDefault(Return(false));
 
-  base::DictionaryValue dict;
-  dict.SetBoolean("setNewPassphrase", true);
-  std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
-                                      "password", ENCRYPT_ALL_DATA);
-  base::ListValue list_args;
-  list_args.AppendString(kTestCallbackId);
-  list_args.AppendString(args);
-
   EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
-              SetEncryptionPassphrase(_))
+              SetEncryptionPassphrase)
       .Times(0);
 
-  handler_->HandleSetEncryption(&list_args);
+  base::ListValue list_args;
+  list_args.AppendString(kTestCallbackId);
+  list_args.AppendString("passphrase123");
+  handler_->HandleSetEncryptionPassphrase(&list_args);
 
-  ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+  ExpectSetPassphraseSuccess(false);
+}
+
+TEST_F(PeopleHandlerTest, CannotOverwritePassphraseWithNewOne) {
+  SigninUser();
+  CreatePeopleHandler();
+  SetupInitializedSyncService();
+
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired())
+      .WillByDefault(Return(false));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsTrustedVaultKeyRequired())
+      .WillByDefault(Return(false));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsUsingSecondaryPassphrase())
+      .WillByDefault(Return(true));
+  ON_CALL(*mock_sync_service_->GetMockUserSettings(),
+          IsEncryptEverythingAllowed())
+      .WillByDefault(Return(true));
+
+  EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
+              SetEncryptionPassphrase)
+      .Times(0);
+
+  base::ListValue list_args;
+  list_args.AppendString(kTestCallbackId);
+  list_args.AppendString("passphrase123");
+  handler_->HandleSetEncryptionPassphrase(&list_args);
+
+  ExpectSetPassphraseSuccess(false);
 }
 
 TEST_F(PeopleHandlerTest, DashboardClearWhileSettingsOpen_ConfirmSoon) {
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 36cd911..55f09a6f 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -87,6 +87,7 @@
     "//components/services/app_service/public/cpp:app_url_handling",
     "//components/services/app_service/public/cpp:protocol_handling",
     "//components/sync",
+    "//components/user_manager",
     "//content/public/browser",
     "//services/metrics/public/cpp:ukm_builders",
     "//skia",
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index b883366..38ba6ff 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -35,6 +35,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/url_data_source.h"
@@ -62,6 +63,7 @@
 #include "chromeos/components/help_app_ui/url_constants.h"
 #include "chromeos/components/media_app_ui/url_constants.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/constants/chromeos_pref_names.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "extensions/common/constants.h"
@@ -103,7 +105,8 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-base::flat_map<SystemAppType, SystemAppInfo> CreateSystemWebApps() {
+base::flat_map<SystemAppType, SystemAppInfo> CreateSystemWebApps(
+    Profile* profile) {
   base::flat_map<SystemAppType, SystemAppInfo> infos;
 // TODO(calamity): Split this into per-platform functions.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -117,8 +120,11 @@
         SystemAppInfo(
             "Camera", GURL("chrome://camera-app/views/main.html"),
             base::BindRepeating(&CreateWebAppInfoForCameraSystemWebApp)));
-    infos.at(SystemAppType::CAMERA).uninstall_and_replace = {
-        extension_misc::kCameraAppId};
+    if (!profile->GetPrefs()->GetBoolean(
+            chromeos::prefs::kHasCameraAppMigratedToSWA)) {
+      infos.at(SystemAppType::CAMERA).uninstall_and_replace = {
+          extension_misc::kCameraAppId};
+    }
     // We need "FileHandling" to use File Handling API to set launch directory.
     // And we need "NativeFileSystem2" to use Native File System API.
     infos.at(SystemAppType::CAMERA).enabled_origin_trials =
@@ -339,7 +345,8 @@
       return true;
     case SystemAppType::CAMERA:
       return base::FeatureList::IsEnabled(
-          chromeos::features::kCameraSystemWebApp);
+                 chromeos::features::kCameraSystemWebApp) &&
+             !user_manager::UserManager::Get()->IsLoggedInAsGuest();
     case SystemAppType::TERMINAL:
       return true;
     case SystemAppType::MEDIA:
@@ -385,7 +392,7 @@
 
     // Populate with real system apps if the test asks for it.
     if (base::FeatureList::IsEnabled(features::kEnableAllSystemWebApps))
-      system_app_infos_ = CreateSystemWebApps();
+      system_app_infos_ = CreateSystemWebApps(profile_);
 
     return;
   }
@@ -398,7 +405,7 @@
   update_policy_ = UpdatePolicy::kAlwaysUpdate;
 #endif
 
-  system_app_infos_ = CreateSystemWebApps();
+  system_app_infos_ = CreateSystemWebApps(profile_);
 }
 
 SystemWebAppManager::~SystemWebAppManager() = default;
@@ -491,7 +498,7 @@
 
 void SystemWebAppManager::InstallSystemAppsForTesting() {
   on_apps_synchronized_.reset(new base::OneShotEvent());
-  system_app_infos_ = CreateSystemWebApps();
+  system_app_infos_ = CreateSystemWebApps(profile_);
   Start();
 
   // Wait for the System Web Apps to install.
@@ -804,6 +811,13 @@
     on_apps_synchronized_->Signal();
     OnAppsPolicyChanged();
   }
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  bool is_camera_app_installed =
+      system_app_infos_.find(SystemAppType::CAMERA) != system_app_infos_.end();
+  profile_->GetPrefs()->SetBoolean(chromeos::prefs::kHasCameraAppMigratedToSWA,
+                                   is_camera_app_installed);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 bool SystemWebAppManager::ShouldForceInstallApps() const {
diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc
index d249c55..7bfc923 100644
--- a/chrome/browser/webshare/share_service_impl.cc
+++ b/chrome/browser/webshare/share_service_impl.cc
@@ -177,7 +177,8 @@
 #elif defined(OS_WIN)
   auto share_operation = std::make_unique<webshare::ShareOperation>(
       title, text, share_url, std::move(files), web_contents);
-  share_operation->Run(base::BindOnce(
+  auto* const share_operation_ptr = share_operation.get();
+  share_operation_ptr->Run(base::BindOnce(
       [](std::unique_ptr<webshare::ShareOperation> share_operation,
          ShareCallback callback,
          blink::mojom::ShareError result) { std::move(callback).Run(result); },
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 2730933..3e9e892 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1606348564-f9ad4c8126c0686f48279a254d0d8ff9696d692f.profdata
+chrome-linux-master-1606391943-c49ce66f4f03ab73428a9c9ccbc70b089767b5c2.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index db33405..d5b80cf3 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1606348564-46558a518073e70dc3e85439edab882c7abb26f5.profdata
+chrome-mac-master-1606391943-6f31c76b4f9e93d1de26d5f38d730905e1d3f7a7.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index db14604..1b89595 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1606066622-6c4da226bf03284a24fb3e251f76a24f1e8c9c69.profdata
+chrome-win32-master-1606359294-a78100f1c02315e3a2df0ee09ef21dd127ea538b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 9b9063e..f88a255 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1606316157-945260897cb0815b47df3e1edecdf8b6cf6cb896.profdata
+chrome-win64-master-1606380888-a2312ebb2792a371a28e16249fe53f8f1a9dbcea.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 28df0b0..bc11079b 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2633,8 +2633,6 @@
 // re-created.
 const char kAppShortcutsArch[] = "apps.shortcuts_arch";
 
-// A string pref for storing the salt used to compute the pepper device ID.
-const char kDRMSalt[] = "settings.privacy.drm_salt";
 // A boolean pref that enables the (private) pepper GetDeviceID() call and
 // enables the use of remote attestation for content protection.
 const char kEnableDRM[] = "settings.privacy.drm_enabled";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index c3f3597..f8eb7798 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -885,7 +885,6 @@
 extern const char kAppShortcutsVersion[];
 extern const char kAppShortcutsArch[];
 
-extern const char kDRMSalt[];
 extern const char kEnableDRM[];
 
 extern const char kWatchdogExtensionActive[];
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 03d9d4c..01fc562 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -197,7 +197,6 @@
     "//mojo/public/cpp/bindings",
     "//net",
     "//ppapi/buildflags",
-    "//ppapi/shared_impl",
     "//printing/buildflags",
     "//services/metrics/public/cpp:metrics_cpp",
     "//services/metrics/public/cpp:ukm_builders",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index b0b945a..79159efa 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -128,7 +128,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/net_errors.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "ppapi/shared_impl/ppapi_switches.h"
 #include "printing/buildflags/buildflags.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
@@ -196,6 +195,7 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "chrome/common/plugin_utils.h"
 #include "chrome/renderer/plugins/chrome_plugin_placeholder.h"
+#include "ppapi/shared_impl/ppapi_switches.h"  // nogncheck crbug.com/1125897
 #else
 #include "components/plugins/renderer/plugin_placeholder.h"
 #endif
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b8c9b01..569cddb 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2679,6 +2679,7 @@
         "../browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc",
         "../browser/chromeos/policy/display_resolution_handler_browsertest.cc",
         "../browser/chromeos/policy/display_rotation_default_handler_browsertest.cc",
+        "../browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc",
         "../browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc",
         "../browser/chromeos/policy/dlp/dlp_rules_manager_browsertest.cc",
         "../browser/chromeos/policy/dlp/dlp_rules_manager_test_utils.cc",
@@ -3182,8 +3183,7 @@
           [ "../browser/site_isolation/spellcheck_per_process_browsertest.cc" ]
     }
 
-    if (is_win || is_mac ||
-        ((is_linux || is_chromeos_lacros) && !is_chromeos_lacros)) {
+    if (is_win || is_mac || (is_linux && !is_chromeos_lacros)) {
       sources +=
           [ "../browser/ui/views/profiles/profile_picker_view_browsertest.cc" ]
     }
@@ -6319,6 +6319,12 @@
         "../browser/password_manager/password_manager_signin_intercept_test_helper.h",
       ]
     }
+    if (is_win || is_mac || (is_linux && !is_chromeos_lacros)) {
+      sources += [
+        "../browser/ui/views/profiles/profile_picker_test_base.cc",
+        "../browser/ui/views/profiles/profile_picker_test_base.h",
+      ]
+    }
   }
 
   proto_library("test_proto") {
@@ -6474,37 +6480,6 @@
       "ppapi/ppapi_interactive_browsertest.cc",
     ]
 
-    if (is_mac) {
-      sources += [
-        "../browser/apps/platform_apps/app_shim_interactive_uitest_mac.mm",
-        "../browser/apps/platform_apps/app_shim_quit_interactive_uitest_mac.mm",
-        "../browser/global_keyboard_shortcuts_mac_browsertest.mm",
-        "../browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm",
-        "../browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.mm",
-        "../browser/ui/cocoa/status_bubble_mac_interactive_uitest.mm",
-        "../browser/ui/cocoa/tab_contents/web_contents_view_mac_interactive_uitest.mm",
-        "base/interactive_test_utils_mac.mm",
-      ]
-    }
-
-    if (is_win) {
-      sources += [
-        "../browser/ui/send_mouse_move_uitest_win.cc",
-        "../browser/ui/views/accessibility/navigation_accessibility_uitest_win.cc",
-        "base/always_on_top_window_killer_win.cc",
-        "base/always_on_top_window_killer_win.h",
-        "base/interactive_test_utils_win.cc",
-        "base/save_desktop_snapshot_win.cc",
-        "base/save_desktop_snapshot_win.h",
-        "base/window_contents_as_string_win.cc",
-        "base/window_contents_as_string_win.h",
-      ]
-
-      if (use_aura) {
-        sources += [ "../browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc" ]
-      }
-    }
-
     configs += [ "//build/config:precompiled_headers" ]
     if ((is_linux || is_chromeos) && !is_component_build) {
       configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
@@ -6695,11 +6670,6 @@
       }
     }
 
-    if (is_mac) {
-      sources +=
-          [ "../browser/notifications/notification_interactive_uitest_mac.mm" ]
-    }
-
     if (is_chromeos_ash) {
       deps += [
         "//chrome/browser/media/router:test_support",
@@ -6726,10 +6696,24 @@
         "../browser/notifications/notification_platform_bridge_win_interactive_uitest.cc",
         "../browser/notifications/win/fake_itoastnotifier.cc",
         "../browser/notifications/win/fake_itoastnotifier.h",
+        "../browser/ui/send_mouse_move_uitest_win.cc",
+        "../browser/ui/views/accessibility/navigation_accessibility_uitest_win.cc",
         "../browser/ui/views/accessibility/uia_accessibility_event_waiter.cc",
         "../browser/ui/views/accessibility/uia_accessibility_event_waiter.h",
         "../browser/ui/views/touch_events_interactive_uitest_win.cc",
+        "base/always_on_top_window_killer_win.cc",
+        "base/always_on_top_window_killer_win.h",
+        "base/interactive_test_utils_win.cc",
+        "base/save_desktop_snapshot_win.cc",
+        "base/save_desktop_snapshot_win.h",
+        "base/window_contents_as_string_win.cc",
+        "base/window_contents_as_string_win.h",
       ]
+
+      if (use_aura) {
+        sources += [ "../browser/ui/views/autofill/autofill_accessibility_win_browsertest.cc" ]
+      }
+
       deps += [
         "//chrome:other_version",
         "//chrome/app:chrome_dll_resources",
@@ -6750,7 +6734,18 @@
     }
 
     if (is_mac) {
-      sources += [ "../browser/ui/find_bar/find_bar_platform_helper_mac_interactive_uitest.mm" ]
+      sources += [
+        "../browser/apps/platform_apps/app_shim_interactive_uitest_mac.mm",
+        "../browser/apps/platform_apps/app_shim_quit_interactive_uitest_mac.mm",
+        "../browser/global_keyboard_shortcuts_mac_browsertest.mm",
+        "../browser/notifications/notification_interactive_uitest_mac.mm",
+        "../browser/spellchecker/spellcheck_mac_view_interactive_uitest.mm",
+        "../browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.mm",
+        "../browser/ui/cocoa/status_bubble_mac_interactive_uitest.mm",
+        "../browser/ui/cocoa/tab_contents/web_contents_view_mac_interactive_uitest.mm",
+        "../browser/ui/find_bar/find_bar_platform_helper_mac_interactive_uitest.mm",
+        "base/interactive_test_utils_mac.mm",
+      ]
 
       sources -= [
         # TODO(crbug.com/1026820): Re-enable this test when history backend failures are addressed.
@@ -6813,6 +6808,12 @@
       sources +=
           [ "../browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc" ]
     }
+
+    if (is_win || is_mac || (is_linux && !is_chromeos_lacros)) {
+      sources += [
+        "../browser/ui/views/profiles/profile_picker_interactive_uitest.cc",
+      ]
+    }
   }
 }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
index be13b4b..ec2abf63 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
@@ -5,30 +5,31 @@
 package org.chromium.chrome.test;
 
 import android.app.Activity;
-import android.app.Instrumentation;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.internal.runner.listener.InstrumentationResultPrinter;
-import android.support.test.rule.ActivityTestRule;
 import android.view.Menu;
 
+import androidx.annotation.NonNull;
+
 import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.CommandLine;
-import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.ScalableTimeout;
+import org.chromium.chrome.browser.DeferredStartupHandler;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -41,7 +42,9 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport;
 import org.chromium.chrome.test.util.ChromeApplicationTestUtils;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.NewTabPageTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.infobars.InfoBar;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
@@ -59,14 +62,13 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Custom  {@link ActivityTestRule} for test using  {@link ChromeActivity}.
  *
  * @param <T> The {@link Activity} class under test.
  */
-public class ChromeActivityTestRule<T extends ChromeActivity> extends ActivityTestRule<T> {
+public class ChromeActivityTestRule<T extends ChromeActivity> extends BaseActivityTestRule<T> {
     private static final String TAG = "ChromeATR";
 
     // The number of ms to wait for the rendering activity to be started.
@@ -75,20 +77,13 @@
     private static final long OMNIBOX_FIND_SUGGESTION_TIMEOUT_MS = 10 * 1000;
 
     private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
-    private Class<T> mChromeActivityClass;
-    private T mSetActivity;
     private String mCurrentTestName;
 
     @Rule
     private EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
 
     protected ChromeActivityTestRule(Class<T> activityClass) {
-        this(activityClass, false);
-    }
-
-    protected ChromeActivityTestRule(Class<T> activityClass, boolean initialTouchMode) {
-        super(activityClass, initialTouchMode, false);
-        mChromeActivityClass = activityClass;
+        super(activityClass);
     }
 
     @Override
@@ -135,13 +130,11 @@
         return ACTIVITY_START_TIMEOUT_MS;
     }
 
-    // TODO(yolandyan): remove this once startActivityCompletely is refactored out of
-    // ChromeActivityTestRule
+    // This has to be here or getActivity will return a T that extends Activity, not a T that
+    // extends ChromeActivity.
     @Override
+    @SuppressWarnings("RedundantOverride")
     public T getActivity() {
-        if (mSetActivity != null) {
-            return mSetActivity;
-        }
         return super.getActivity();
     }
 
@@ -152,6 +145,14 @@
     }
 
     /**
+     * TODO(https://crbug.com/1146574): This only exists here because legacy ActivityTestRule
+     * inherited from UiThreadTestRule. This function should be removed.
+     */
+    public void runOnUiThread(Runnable r) {
+        ThreadUtils.runOnUiThreadBlocking(r);
+    }
+
+    /**
      * @return The {@link AppMenuCoordinator} for the activity.
      */
     public AppMenuCoordinator getAppMenuCoordinator() {
@@ -196,49 +197,37 @@
     }
 
     /**
-     * Invokes {@link Instrumentation#startActivitySync(Intent)} and sets the
-     * test case's activity to the result. See the documentation for
-     * {@link Instrumentation#startActivitySync(Intent)} on the timing of the
-     * return, but generally speaking the activity's "onCreate" has completed
-     * and the activity's main looper has become idle.
-     *
-     * TODO(yolandyan): very similar to ActivityTestRule#launchActivity(Intent),
-     * yet small differences remains (e.g. launchActivity() uses FLAG_ACTIVITY_NEW_TASK while
-     * startActivityCompletely doesn't), need to refactor and use only launchActivity
-     * after the JUnit4 migration
+     * Similar to #launchActivity(Intent), but waits for the Activity tab to be initialized.
      */
     public void startActivityCompletely(Intent intent) {
-        Features.ensureCommandLineIsUpToDate();
+        DeferredStartupHandler.setExpectingActivityStartupForTesting();
+        launchActivity(intent);
+        waitForActivityNativeInitializationComplete();
 
-        final CallbackHelper activityCallback = new CallbackHelper();
-        final AtomicReference<T> activityRef = new AtomicReference<>();
-        ActivityStateListener stateListener = new ActivityStateListener() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public void onActivityStateChange(Activity activity, int newState) {
-                if (newState == ActivityState.RESUMED) {
-                    if (!mChromeActivityClass.isAssignableFrom(activity.getClass())) return;
+        CriteriaHelper.pollUiThread(
+                () -> getActivity().getActivityTab() != null, "Tab never selected/initialized.");
+        Tab tab = getActivity().getActivityTab();
 
-                    activityRef.set((T) activity);
-                    activityCallback.notifyCalled();
-                    ApplicationStatus.unregisterActivityStateListener(this);
-                }
-            }
-        };
-        ApplicationStatus.registerStateListenerForAllActivities(stateListener);
+        ChromeTabUtils.waitForTabPageLoaded(tab, (String) null);
 
-        try {
-            InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
-            activityCallback.waitForCallback("Activity did not start as expected", 0);
-            T activity = activityRef.get();
-            Assert.assertNotNull("Activity reference is null.", activity);
-            setActivity(activity);
-            Log.d(TAG, "startActivityCompletely <<");
-        } catch (TimeoutException e) {
-            throw new RuntimeException(e);
-        } finally {
-            ApplicationStatus.unregisterActivityStateListener(stateListener);
+        if (tab != null && UrlUtilities.isNTPUrl(ChromeTabUtils.getUrlStringOnUiThread(tab))
+                && !getActivity().isInOverviewMode()) {
+            NewTabPageTestUtils.waitForNtpLoaded(tab);
         }
+
+        Assert.assertTrue("Deferred startup never completed. Did you try to start an Activity "
+                        + "that was already started?",
+                DeferredStartupHandler.waitForDeferredStartupCompleteForTesting(
+                        ScalableTimeout.scaleTimeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL)));
+
+        Assert.assertNotNull(tab);
+        Assert.assertNotNull(tab.getView());
+    }
+
+    @Override
+    public void launchActivity(@NonNull Intent startIntent) {
+        Features.ensureCommandLineIsUpToDate();
+        super.launchActivity(startIntent);
     }
 
     /**
@@ -472,10 +461,6 @@
         return getActivity().getWindowAndroid().getKeyboardDelegate();
     }
 
-    public void setActivity(T chromeActivity) {
-        mSetActivity = chromeActivity;
-    }
-
     /**
      * Waits for an Activity of the given class to be started.
      * @return The Activity.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeTabbedActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeTabbedActivityTestRule.java
index cc708d7..65e683d2 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeTabbedActivityTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeTabbedActivityTestRule.java
@@ -20,11 +20,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.DeferredStartupHandler;
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
@@ -36,7 +33,6 @@
 import org.chromium.chrome.test.util.MenuUtils;
 import org.chromium.chrome.test.util.NewTabPageTestUtils;
 import org.chromium.chrome.test.util.WaitForFocusHelper;
-import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.concurrent.TimeoutException;
@@ -113,31 +109,7 @@
      */
     public void startMainActivityFromIntent(Intent intent, String url) {
         prepareUrlIntent(intent, url);
-
-        DeferredStartupHandler.setExpectingActivityStartupForTesting();
         startActivityCompletely(intent);
-        waitForActivityNativeInitializationComplete();
-
-        CriteriaHelper.pollUiThread(
-                () -> getActivity().getActivityTab() != null, "Tab never selected/initialized.");
-        Tab tab = getActivity().getActivityTab();
-
-        ChromeTabUtils.waitForTabPageLoaded(tab, (String) null);
-
-        if (tab != null && UrlUtilities.isNTPUrl(ChromeTabUtils.getUrlStringOnUiThread(tab))
-                && !getActivity().isInOverviewMode()) {
-            NewTabPageTestUtils.waitForNtpLoaded(tab);
-        }
-
-        Assert.assertTrue("Deferred startup never completed. Did you try to start an Activity "
-                        + "that was already started?",
-                DeferredStartupHandler.waitForDeferredStartupCompleteForTesting(
-                        ScalableTimeout.scaleTimeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL)));
-
-        Assert.assertNotNull(tab);
-        Assert.assertNotNull(tab.getView());
-
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
     /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/batch/BlankCTATabInitialStateRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/batch/BlankCTATabInitialStateRule.java
index e401694..dba985d0 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/batch/BlankCTATabInitialStateRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/batch/BlankCTATabInitialStateRule.java
@@ -39,6 +39,7 @@
         super();
         mActivityTestRule = activityTestRule;
         mClearAllTabState = clearAllTabState;
+        mActivityTestRule.setFinishActivity(false);
     }
 
     @Override
diff --git a/chrome/test/data/webui/settings/password_check_test.js b/chrome/test/data/webui/settings/password_check_test.js
index 4e7cc9a3..a530a8b 100644
--- a/chrome/test/data/webui/settings/password_check_test.js
+++ b/chrome/test/data/webui/settings/password_check_test.js
@@ -982,6 +982,27 @@
     expectFalse(isElementVisible(section.$.subtitle));
   });
 
+  // When the user is signed out but has run a weak check a timestamp should be
+  // shown.
+  test('showWeakCheckTimestampWhenSignedOut', async function() {
+    loadTimeData.overrideValues({passwordsWeaknessCheck: true});
+    passwordManager.data.checkStatus = makePasswordCheckStatus(
+        /*state=*/ PasswordCheckState.SIGNED_OUT,
+        /*checked=*/ 0,
+        /*remaining=*/ 0,
+        /*lastCheck=*/ 'Just now');
+
+    const section = createCheckPasswordSection();
+    await passwordManager.whenCalled('getPasswordCheckStatus');
+    flush();
+    const titleRow = section.$.titleRow;
+    const subtitle = section.$.subtitle;
+    assertTrue(isElementVisible(titleRow));
+    assertTrue(isElementVisible(subtitle));
+    expectEquals(
+        section.i18n('checkedPasswords') + ' • Just now', titleRow.innerText);
+  });
+
   // If |passwordsWeaknessCheck| is true, user is signed out and has
   // compromised credentials that were found in the past, shows "Checked
   // passwords" and correct label in the top of comromised passwords section.
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.js b/chrome/test/data/webui/settings/people_page_sync_page_test.js
index 624cd363..913638f 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.js
@@ -337,7 +337,7 @@
     assertTrue(passphraseConfirmationInput.invalid);
   });
 
-  test('CreatingPassphraseValidPassphrase', function() {
+  test('CreatingPassphraseValidPassphrase', async function() {
     encryptWithPassphrase.click();
     flush();
 
@@ -350,34 +350,31 @@
         encryptionElement.$$('#passphraseConfirmationInput');
     passphraseInput.value = 'foo';
     passphraseConfirmationInput.value = 'foo';
+    browserProxy.encryptionPassphraseSuccess = true;
     saveNewPassphrase.click();
 
-    function verifySetSyncEncryption(args) {
-      assertTrue(args.setNewPassphrase);
-      assertEquals('foo', args.passphrase);
+    const passphrase = await browserProxy.whenCalled('setEncryptionPassphrase');
 
-      // Fake backend response.
-      const newPrefs = getSyncAllPrefs();
-      newPrefs.fullEncryptionBody = 'Encrypted with custom passphrase';
-      newPrefs.encryptAllData = true;
-      webUIListenerCallback('sync-prefs-changed', newPrefs);
+    assertEquals('foo', passphrase);
 
-      flush();
+    // Fake backend response.
+    const newPrefs = getSyncAllPrefs();
+    newPrefs.fullEncryptionBody = 'Encrypted with custom passphrase';
+    newPrefs.encryptAllData = true;
+    webUIListenerCallback('sync-prefs-changed', newPrefs);
 
-      return waitBeforeNextRender(syncPage).then(() => {
-        // Need to re-retrieve this, as a different show passphrase radio
-        // button is shown once |syncPrefs.fullEncryptionBody| is non-empty.
-        encryptWithPassphrase = encryptionElement.$$(
-            'cr-radio-button[name="encrypt-with-passphrase"]');
+    flush();
 
-        // Assert that the radio boxes are disabled after encryption enabled.
-        assertTrue(encryptionRadioGroup.disabled);
-        assertEquals(-1, encryptWithGoogle.$.button.tabIndex);
-        assertEquals(-1, encryptWithPassphrase.$.button.tabIndex);
-      });
-    }
-    return browserProxy.whenCalled('setSyncEncryption')
-        .then(verifySetSyncEncryption);
+    await waitBeforeNextRender(syncPage);
+    // Need to re-retrieve this, as a different show passphrase radio
+    // button is shown once |syncPrefs.fullEncryptionBody| is non-empty.
+    encryptWithPassphrase =
+        encryptionElement.$$('cr-radio-button[name="encrypt-with-passphrase"]');
+
+    // Assert that the radio boxes are disabled after encryption enabled.
+    assertTrue(encryptionRadioGroup.disabled);
+    assertEquals(-1, encryptWithGoogle.$.button.tabIndex);
+    assertEquals(-1, encryptWithPassphrase.$.button.tabIndex);
   });
 
   test('RadioBoxesHiddenWhenPassphraseRequired', function() {
@@ -415,7 +412,7 @@
         assertFalse(submitExistingPassphrase.disabled);
       });
 
-  test('EnterExistingWrongPassphrase', function() {
+  test('EnterExistingWrongPassphrase', async function() {
     const prefs = getSyncAllPrefs();
     prefs.encryptAllData = true;
     prefs.passphraseRequired = true;
@@ -425,21 +422,19 @@
     const existingPassphraseInput = syncPage.$$('#existingPassphraseInput');
     assertTrue(!!existingPassphraseInput);
     existingPassphraseInput.value = 'wrong';
-    browserProxy.encryptionResponse = PageStatus.PASSPHRASE_FAILED;
+    browserProxy.decryptionPassphraseSuccess = false;
 
     const submitExistingPassphrase = syncPage.$$('#submitExistingPassphrase');
     assertTrue(!!submitExistingPassphrase);
     submitExistingPassphrase.click();
 
-    return browserProxy.whenCalled('setSyncEncryption').then(function(args) {
-      assertFalse(args.setNewPassphrase);
-      assertEquals('wrong', args.passphrase);
-      assertTrue(args.passphraseRequired);
-      assertTrue(existingPassphraseInput.invalid);
-    });
+    const passphrase = await browserProxy.whenCalled('setDecryptionPassphrase');
+
+    assertEquals('wrong', passphrase);
+    assertTrue(existingPassphraseInput.invalid);
   });
 
-  test('EnterExistingCorrectPassphrase', function() {
+  test('EnterExistingCorrectPassphrase', async function() {
     const prefs = getSyncAllPrefs();
     prefs.encryptAllData = true;
     prefs.passphraseRequired = true;
@@ -449,29 +444,27 @@
     const existingPassphraseInput = syncPage.$$('#existingPassphraseInput');
     assertTrue(!!existingPassphraseInput);
     existingPassphraseInput.value = 'right';
-    browserProxy.encryptionResponse = PageStatus.CONFIGURE;
+    browserProxy.decryptionPassphraseSuccess = true;
 
     const submitExistingPassphrase = syncPage.$$('#submitExistingPassphrase');
     assertTrue(!!submitExistingPassphrase);
     submitExistingPassphrase.click();
 
-    return browserProxy.whenCalled('setSyncEncryption').then(function(args) {
-      assertFalse(args.setNewPassphrase);
-      assertEquals('right', args.passphrase);
-      assertTrue(args.passphraseRequired);
+    const passphrase = await browserProxy.whenCalled('setDecryptionPassphrase');
 
-      // Fake backend response.
-      const newPrefs = getSyncAllPrefs();
-      newPrefs.encryptAllData = true;
-      webUIListenerCallback('sync-prefs-changed', newPrefs);
+    assertEquals('right', passphrase);
 
-      flush();
+    // Fake backend response.
+    const newPrefs = getSyncAllPrefs();
+    newPrefs.encryptAllData = true;
+    webUIListenerCallback('sync-prefs-changed', newPrefs);
 
-      // Verify that the encryption radio boxes are shown but disabled.
-      assertTrue(encryptionRadioGroup.disabled);
-      assertEquals(-1, encryptWithGoogle.$.button.tabIndex);
-      assertEquals(-1, encryptWithPassphrase.$.button.tabIndex);
-    });
+    flush();
+
+    // Verify that the encryption radio boxes are shown but disabled.
+    assertTrue(encryptionRadioGroup.disabled);
+    assertEquals(-1, encryptWithGoogle.$.button.tabIndex);
+    assertEquals(-1, encryptWithPassphrase.$.button.tabIndex);
   });
 
   test('SyncAdvancedRow', function() {
diff --git a/chrome/test/data/webui/settings/test_sync_browser_proxy.js b/chrome/test/data/webui/settings/test_sync_browser_proxy.js
index 5537f5f..567f747 100644
--- a/chrome/test/data/webui/settings/test_sync_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_sync_browser_proxy.js
@@ -19,7 +19,8 @@
       'getSyncStatus',
       'incrementPromoImpressionCount',
       'setSyncDatatypes',
-      'setSyncEncryption',
+      'setEncryptionPassphrase',
+      'setDecryptionPassphrase',
       'signOut',
       'pauseSync',
       'sendSyncPrefsChanged',
@@ -37,8 +38,10 @@
     this.impressionCount_ = 0;
 
     // Settable fake data.
-    /** @type {!settings.PageStatus} */
-    this.encryptionResponse = settings.PageStatus.CONFIGURE;
+    /** @type {boolean} */
+    this.encryptionPassphraseSuccess = false;
+    /** @type {boolean} */
+    this.decryptionPassphraseSuccess = false;
     /** @type {!Array<!settings.StoredAccount>} */
     this.storedAccounts = [];
     /** @type {!settings.SyncStatus} */
@@ -111,9 +114,15 @@
   }
 
   /** @override */
-  setSyncEncryption(syncPrefs) {
-    this.methodCalled('setSyncEncryption', syncPrefs);
-    return Promise.resolve(this.encryptionResponse);
+  setEncryptionPassphrase(passphrase) {
+    this.methodCalled('setEncryptionPassphrase', passphrase);
+    return Promise.resolve(this.encryptionPassphraseSuccess);
+  }
+
+  /** @override */
+  setDecryptionPassphrase(passphrase) {
+    this.methodCalled('setDecryptionPassphrase', passphrase);
+    return Promise.resolve(this.decryptionPassphraseSuccess);
   }
 
   /** @override */
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index 26f75e1..9fdc15b 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -183,7 +183,7 @@
   Uninstall();
 }
 
-TEST_F(IntegrationTest, UnregisterUninstalledApp) {
+TEST_F(IntegrationTest, DISABLED_UnregisterUninstalledApp) {
   RegisterTestApp();
   ExpectInstalled();
   ExpectActiveVersion(UPDATER_VERSION_STRING);
diff --git a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
index 830e9f1b..46a3a80c 100644
--- a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
+++ b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
@@ -68,7 +68,8 @@
       <structure name="IDR_CAMERA_SOUND_JS" file="js/sound.js" type="chrome_html" />
       <structure name="IDR_CAMERA_STATE_JS" file="js/state.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TEST_BRIDGE_JS" file="js/test_bridge.js" type="chrome_html" />
-      <structure name="IDR_CAMERA_TEST_HTML" file="views/test.html" type="chrome_html" />
+      <structure name="IDR_CAMERA_TEST_HTML" file="test/test.html" type="chrome_html" />
+      <structure name="IDR_CAMERA_TEST_LEGACY_HTML" file="views/test.html" type="chrome_html" />
       <structure name="IDR_CAMERA_TIMERTICK_JS" file="js/views/camera/timertick.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TOAST_JS" file="js/toast.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TOOLTIP_JS" file="js/tooltip.js" type="chrome_html" />
diff --git a/chromeos/components/camera_app_ui/resources/js/dom.js b/chromeos/components/camera_app_ui/resources/js/dom.js
index 6caa43f..c6bb118 100644
--- a/chromeos/components/camera_app_ui/resources/js/dom.js
+++ b/chromeos/components/camera_app_ui/resources/js/dom.js
@@ -12,7 +12,7 @@
  * type.
  * @param {!Node} target
  * @param {string} selector
- * @param {function(new: T, ...)} type A user-defined constructor.
+ * @param {function(new: T, ...)} type The expected element type.
  * @return {T}
  * @template T
  */
@@ -25,7 +25,7 @@
  * their type to be specific type.
  * @param {!Node} target
  * @param {string} selector
- * @param {function(new: T, ...)} type A user-defined constructor.
+ * @param {function(new: T, ...)} type The expected element type.
  * @return {!NodeList<T>}
  * @template T
  */
@@ -40,7 +40,7 @@
 /**
  * Gets an element in document matching css selector and checks its type.
  * @param {string} selector
- * @param {function(new: T, ...)} type A user-defined constructor.
+ * @param {function(new: T, ...)} type The expected element type.
  * @return {T}
  * @template T
  */
@@ -52,7 +52,7 @@
  * Gets all elements in document matching css selector and asserts their type to
  * be specific type.
  * @param {string} selector
- * @param {function(new: T, ...)} type A user-defined constructor.
+ * @param {function(new: T, ...)} type The expected element type.
  * @return {!NodeList<T>}
  * @template T
  */
@@ -60,4 +60,16 @@
   return getAllFrom(document, selector, type);
 }
 
+/**
+ * Creates a typed element.
+ * @param {string} tag The HTML tag of the element to be created.
+ * @param {function(new: T, ...)} type The expected element type.
+ * @return {!T}
+ * @template T
+ */
+export function create(tag, type) {
+  const el = document.createElement(tag);
+  return assertInstanceof(el, type);
+}
+
 /* eslint-enable valid-jsdoc */
diff --git a/chromeos/components/camera_app_ui/resources/js/main.js b/chromeos/components/camera_app_ui/resources/js/main.js
index 1486db3b..879daee 100644
--- a/chromeos/components/camera_app_ui/resources/js/main.js
+++ b/chromeos/components/camera_app_ui/resources/js/main.js
@@ -266,8 +266,7 @@
 
     const preloadImages = (async () => {
       const loadImage = (url) => new Promise((resolve, reject) => {
-        const link =
-            /** @type {!HTMLLinkElement} */ (document.createElement('link'));
+        const link = dom.create('link', HTMLLinkElement);
         link.rel = 'preload';
         link.as = 'image';
         link.href = url;
diff --git a/chromeos/components/camera_app_ui/resources/js/util.js b/chromeos/components/camera_app_ui/resources/js/util.js
index b4c2a89..92693ac 100644
--- a/chromeos/components/camera_app_ui/resources/js/util.js
+++ b/chromeos/components/camera_app_ui/resources/js/util.js
@@ -24,8 +24,7 @@
  *     Returns canvas element and the context for 2D drawing.
  */
 export function newDrawingCanvas({width, height}) {
-  const canvas =
-      assertInstanceof(document.createElement('canvas'), HTMLCanvasElement);
+  const canvas = dom.create('canvas', HTMLCanvasElement);
   canvas.width = width;
   canvas.height = height;
   const ctx =
@@ -169,8 +168,7 @@
     if (orientation.rotation === 0 && !orientation.flip) {
       onSuccess(blob);
     } else {
-      const original =
-          assertInstanceof(document.createElement('img'), HTMLImageElement);
+      const original = dom.create('img', HTMLImageElement);
       original.onload = function() {
         drawPhoto(original, orientation, onSuccess, onFailure);
       };
@@ -361,9 +359,8 @@
  * @return {!Promise<!Blob>} Promise for the result.
  */
 export async function scalePicture(url, isVideo, width, height = undefined) {
-  const element =
-      /** @type {(!HTMLImageElement|!HTMLVideoElement)} */ (
-          document.createElement(isVideo ? 'video' : 'img'));
+  const element = isVideo ? dom.create('video', HTMLVideoElement) :
+                            dom.create('img', HTMLImageElement);
   if (isVideo) {
     element.preload = 'auto';
   }
@@ -486,8 +483,7 @@
  */
 export async function createUntrustedJSModule(scriptUrl, origin) {
   const untrustedPageReady = new WaitableEvent();
-  const iFrame =
-      /** @type {!HTMLIFrameElement} */ (document.createElement('iframe'));
+  const iFrame = dom.create('iframe', HTMLIFrameElement);
   iFrame.addEventListener('load', () => untrustedPageReady.signal());
   iFrame.setAttribute('src', `${origin}/views/untrusted_script_loader.html`);
   iFrame.hidden = true;
diff --git a/chromeos/components/camera_app_ui/resources/test/test.html b/chromeos/components/camera_app_ui/resources/test/test.html
new file mode 100644
index 0000000..82c75d87
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/test/test.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<!-- Copyright 2020 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. -->
+<html>
+  <head>
+  </head>
+</html>
diff --git a/chromeos/components/camera_app_ui/url_constants.cc b/chromeos/components/camera_app_ui/url_constants.cc
index 6e1a8b9..b131281 100644
--- a/chromeos/components/camera_app_ui/url_constants.cc
+++ b/chromeos/components/camera_app_ui/url_constants.cc
@@ -8,6 +8,7 @@
 
 const char kChromeUICameraAppHost[] = "camera-app";
 const char kChromeUICameraAppMainURL[] = "chrome://camera-app/views/main.html";
+const char kChromeUICameraAppScopeURL[] = "chrome://camera-app/views";
 const char kChromeUICameraAppURL[] = "chrome://camera-app/";
 const char kChromeUIUntrustedCameraAppURL[] = "chrome-untrusted://camera-app/";
 
diff --git a/chromeos/components/camera_app_ui/url_constants.h b/chromeos/components/camera_app_ui/url_constants.h
index 4a81ad5e..95b78c7 100644
--- a/chromeos/components/camera_app_ui/url_constants.h
+++ b/chromeos/components/camera_app_ui/url_constants.h
@@ -9,6 +9,7 @@
 
 extern const char kChromeUICameraAppHost[];
 extern const char kChromeUICameraAppMainURL[];
+extern const char kChromeUICameraAppScopeURL[];
 extern const char kChromeUICameraAppURL[];
 extern const char kChromeUIUntrustedCameraAppURL[];
 
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index c0f56da..59fc41a 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -100,6 +100,10 @@
 const base::Feature kAssistAutoCorrect{"AssistAutoCorrect",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether to enable assistive multi word suggestions.
+const base::Feature kAssistMultiWord{"AssistMultiWord",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to enable assistive personal information.
 const base::Feature kAssistPersonalInfo{"AssistPersonalInfo",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 834d1ee4..b9cd168 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -62,6 +62,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kAssistAutoCorrect;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kAssistMultiWord;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kAssistPersonalInfo;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kAssistPersonalInfoAddress;
diff --git a/chromeos/constants/chromeos_pref_names.cc b/chromeos/constants/chromeos_pref_names.cc
index 1d44149..c92dd814 100644
--- a/chromeos/constants/chromeos_pref_names.cc
+++ b/chromeos/constants/chromeos_pref_names.cc
@@ -124,5 +124,9 @@
 // the Chrome OS launcher.
 const char kLauncherResultEverLaunched[] = "launcher.result_ever_launched";
 
+// Whether the status of the platform app version of camera app is migrated to
+// SWA.
+const char kHasCameraAppMigratedToSWA[] = "camera.has_migrated_to_swa";
+
 }  // namespace prefs
 }  // namespace chromeos
diff --git a/chromeos/constants/chromeos_pref_names.h b/chromeos/constants/chromeos_pref_names.h
index f5447d5c..09a586c 100644
--- a/chromeos/constants/chromeos_pref_names.h
+++ b/chromeos/constants/chromeos_pref_names.h
@@ -54,6 +54,8 @@
 extern const char kSuggestedContentEnabled[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kLauncherResultEverLaunched[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kHasCameraAppMigratedToSWA[];
 
 }  // namespace prefs
 }  // namespace chromeos
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 88ac294c..ff53f0f 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-89-4324.9-1606128713-benchmark-89.0.4335.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-89-4324.9-1606128713-benchmark-89.0.4336.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index f333af2f..e3916aa 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-89-4324.9-1606129869-benchmark-89.0.4335.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-89-4324.9-1606129869-benchmark-89.0.4336.0-r1-redacted.afdo.xz
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index dd0a03a..83a970cc 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -1403,8 +1403,8 @@
   WebFormElement form = cleared_element.Form();
   if (form.IsNull()) {
     // Process password field clearing for fields outside the <form> tag.
-    GetPasswordManagerDriver()->PasswordFormCleared(
-        *GetFormDataFromUnownedInputElements());
+    if (auto unowned_form_data = GetFormDataFromUnownedInputElements())
+      GetPasswordManagerDriver()->PasswordFormCleared(*unowned_form_data);
     return;
   }
   // Process field clearing for a form under a <form> tag.
diff --git a/components/autofill/core/browser/autofill_download_manager_unittest.cc b/components/autofill/core/browser/autofill_download_manager_unittest.cc
index c78f7da..419e5cd 100644
--- a/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -1946,7 +1946,7 @@
 
   AutofillDownloadManager download_manager(driver_.get(), this);
   FormStructure form_structure(form);
-  form_structure.set_page_language(LanguageCode("fr"));
+  form_structure.set_original_page_language(LanguageCode("fr"));
 
   pref_service_->SetBoolean(
       RandomizedEncoder::kUrlKeyedAnonymizedDataCollectionEnabled, true);
@@ -1983,7 +1983,8 @@
     ASSERT_TRUE(request.ParseFromString(payloads_.front()));
     ASSERT_TRUE(request.has_upload());
     const AutofillUploadContents& upload = request.upload();
-    EXPECT_EQ(upload.language(), form_structure.page_language().value());
+    EXPECT_EQ(upload.language(),
+              form_structure.original_page_language().value());
     ASSERT_TRUE(upload.has_randomized_form_metadata());
     EXPECT_TRUE(upload.randomized_form_metadata().has_id());
     EXPECT_TRUE(upload.randomized_form_metadata().has_name());
diff --git a/components/autofill/core/browser/autofill_handler.cc b/components/autofill/core/browser/autofill_handler.cc
index f4082434..914fb25 100644
--- a/components/autofill/core/browser/autofill_handler.cc
+++ b/components/autofill/core/browser/autofill_handler.cc
@@ -40,9 +40,14 @@
   return nullptr;
 }
 
-// Returns true if |live_form| does not match |cached_form|.
+// Returns true if |live_form| does not match |cached_form|, assuming that
+// |live_form|'s language is |live_form_language|.
 bool CachedFormNeedsUpdate(const FormData& live_form,
+                           const LanguageCode& live_form_language,
                            const FormStructure& cached_form) {
+  if (cached_form.original_page_language() != live_form_language)
+    return true;
+
   if (live_form.fields.size() != cached_form.field_count())
     return true;
 
@@ -201,6 +206,7 @@
   driver_->SendFormDataToRenderer(query_id, action, data);
 }
 
+// Returns true if |live_form| does not match |cached_form|.
 bool AutofillHandler::GetCachedFormAndField(const FormData& form,
                                             const FormFieldData& field,
                                             FormStructure** form_structure,
@@ -210,7 +216,7 @@
       FindCachedFormByRendererId(form.unique_renderer_id);
   if (cached_form) {
     DCHECK(cached_form);
-    if (!CachedFormNeedsUpdate(form, *cached_form)) {
+    if (!CachedFormNeedsUpdate(form, GetPageLanguage(), *cached_form)) {
       // There is no data to return if there are no auto-fillable fields.
       if (!cached_form->autofill_count())
         return false;
@@ -288,7 +294,7 @@
       value_from_dynamic_change_form_ = true;
   }
 
-  form_structure->set_page_language(GetPageLanguage());
+  form_structure->set_original_page_language(GetPageLanguage());
 
   form_structure->DetermineHeuristicTypes(log_manager_);
 
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 1015ea9..bb716b1 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -797,7 +797,7 @@
     copied_credit_cards.push_back(*card);
 
   // Annotate the form with the source language of the page.
-  form_structure->set_page_language(LanguageCode(GetPageLanguage()));
+  form_structure->set_original_page_language(GetPageLanguage());
 
   // Attach the Randomized Encoder.
   form_structure->set_randomized_encoder(
@@ -2796,9 +2796,9 @@
 LanguageCode AutofillManager::GetPageLanguage() const {
   DCHECK(client_);
   const translate::LanguageState* language_state = client_->GetLanguageState();
-  if (language_state)
-    return LanguageCode(language_state->original_language());
-  return LanguageCode();
+  if (!language_state)
+    return LanguageCode();
+  return LanguageCode(language_state->original_language());
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 2f0eae83..82af7ed6 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -390,6 +390,8 @@
   FormData* pending_form_data() { return pending_form_data_.get(); }
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, PageLanguageGetsCorrectlySet);
+
   // Keeps track of the filling context for a form, used to make refill attemps.
   struct FillingContext {
     // |optional_profile| or |optional_credit_card| must be non-null.
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index 2cd3b31..5f29a9f 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -8455,17 +8455,25 @@
 }
 
 TEST_F(AutofillManagerTest, PageLanguageGetsCorrectlySet) {
-  const char* kTestLanguage = "zh";
   FormData form;
   test::CreateTestAddressFormData(&form);
 
-  // Set up language state mock.
-  autofill_client_.GetLanguageState()->SetOriginalLanguage(kTestLanguage);
+  autofill_client_.GetLanguageState()->SetOriginalLanguage("und");
 
-  FormStructure* parsed_form = autofill_manager_->ParseFormForTest(form);
+  autofill_manager_->OnFormsSeen({form}, base::TimeTicks());
+  FormStructure* parsed_form =
+      autofill_manager_->FindCachedFormByRendererId(form.unique_renderer_id);
 
   ASSERT_TRUE(parsed_form);
-  ASSERT_EQ(LanguageCode(kTestLanguage), parsed_form->page_language());
+  ASSERT_EQ(LanguageCode("und"), parsed_form->original_page_language());
+
+  autofill_client_.GetLanguageState()->SetOriginalLanguage("zh");
+
+  autofill_manager_->OnFormsSeen({form}, base::TimeTicks());
+  parsed_form =
+      autofill_manager_->FindCachedFormByRendererId(form.unique_renderer_id);
+
+  ASSERT_EQ(LanguageCode("zh"), parsed_form->original_page_language());
 }
 
 // AutofillManagerTest with kAutofillDisabledMixedForms feature enabled.
diff --git a/components/autofill/core/browser/autofill_profile_sync_util.cc b/components/autofill/core/browser/autofill_profile_sync_util.cc
index 267ad01..f82bcdf 100644
--- a/components/autofill/core/browser/autofill_profile_sync_util.cc
+++ b/components/autofill/core/browser/autofill_profile_sync_util.cc
@@ -48,6 +48,8 @@
       return VerificationStatus::kObserved;
     case sync_pb::AutofillProfileSpecifics_VerificationStatus_USER_VERIFIED:
       return VerificationStatus::kUserVerified;
+    case sync_pb::AutofillProfileSpecifics_VerificationStatus_SERVER_PARSED:
+      return VerificationStatus::kServerParsed;
   }
 }
 
@@ -67,6 +69,8 @@
       return sync_pb::AutofillProfileSpecifics_VerificationStatus_OBSERVED;
     case (VerificationStatus::kUserVerified):
       return sync_pb::AutofillProfileSpecifics_VerificationStatus_USER_VERIFIED;
+    case (VerificationStatus::kServerParsed):
+      return sync_pb::AutofillProfileSpecifics_VerificationStatus_SERVER_PARSED;
   }
 }
 
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index d74c8d2..72de7229 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -451,10 +451,14 @@
   }
 
   for (ServerFieldType type : types) {
-    if (GetVerificationStatus(type) < profile.GetVerificationStatus(type))
+    if (structured_address::IsLessSignificantVerificationStatus(
+            GetVerificationStatus(type), profile.GetVerificationStatus(type))) {
       return -1;
-    if (GetVerificationStatus(type) > profile.GetVerificationStatus(type))
+    }
+    if (structured_address::IsLessSignificantVerificationStatus(
+            profile.GetVerificationStatus(type), GetVerificationStatus(type))) {
       return 1;
+    }
   }
 
   // TODO(crbug.com/1130194): Remove feature check once structured addresses are
@@ -475,11 +479,17 @@
         return comparison;
     }
 
-    for (ServerFieldType type : new_types) {
-      if (GetVerificationStatus(type) < profile.GetVerificationStatus(type))
+    for (ServerFieldType type : types) {
+      if (structured_address::IsLessSignificantVerificationStatus(
+              GetVerificationStatus(type),
+              profile.GetVerificationStatus(type))) {
         return -1;
-      if (GetVerificationStatus(type) > profile.GetVerificationStatus(type))
+      }
+      if (structured_address::IsLessSignificantVerificationStatus(
+              profile.GetVerificationStatus(type),
+              GetVerificationStatus(type))) {
         return 1;
+      }
     }
   }
 
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address.cc b/components/autofill/core/browser/data_model/autofill_structured_address.cc
index 9bdc8a9..49232c5 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address.cc
@@ -135,7 +135,8 @@
 bool StreetAddress::HasNewerValuePrecendenceInMerging(
     const AddressComponent& newer_component) const {
   // If the newer component has a better verification status, use the newer one.
-  if (GetVerificationStatus() < newer_component.GetVerificationStatus())
+  if (IsLessSignificantVerificationStatus(
+          GetVerificationStatus(), newer_component.GetVerificationStatus()))
     return true;
 
   // If the verification statuses are the same, do not use the newer component
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
index 3d13303..c32c74b 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
@@ -27,6 +27,28 @@
 
 namespace structured_address {
 
+bool IsLessSignificantVerificationStatus(VerificationStatus left,
+                                         VerificationStatus right) {
+  // Both the KUserVerified and kObserved are larger then kServerParsed although
+  // the underlying integer suggests differently.
+  if (left == VerificationStatus::kServerParsed &&
+      (right == VerificationStatus::kObserved ||
+       right == VerificationStatus::kUserVerified)) {
+    return true;
+  }
+
+  if (right == VerificationStatus::kServerParsed &&
+      (left == VerificationStatus::kObserved ||
+       left == VerificationStatus::kUserVerified)) {
+    return false;
+  }
+
+  // In all other cases, it is sufficient to compare the underlying integer
+  // values.
+  return static_cast<std::underlying_type_t<VerificationStatus>>(left) <
+         static_cast<std::underlying_type_t<VerificationStatus>>(right);
+}
+
 AddressComponent::AddressComponent(ServerFieldType storage_type,
                                    AddressComponent* parent,
                                    std::vector<AddressComponent*> subcomponents,
@@ -701,7 +723,7 @@
 void AddressComponent::MergeVerificationStatuses(
     const AddressComponent& newer_component) {
   if (IsValueAssigned() && (GetValue() == newer_component.GetValue()) &&
-      (GetVerificationStatus() < newer_component.GetVerificationStatus())) {
+      HasNewerValuePrecendenceInMerging(newer_component)) {
     value_verification_status_ = newer_component.GetVerificationStatus();
   }
 
@@ -811,7 +833,7 @@
 
   // If the normalized values are the same, optimize the verification status.
   if ((merge_mode_ & kUseBetterOrNewerForSameValue) && (value == value_newer)) {
-    if (newer_component.GetVerificationStatus() >= GetVerificationStatus()) {
+    if (HasNewerValuePrecendenceInMerging(newer_component)) {
       *this = newer_component;
     }
     return true;
@@ -904,7 +926,8 @@
 
 bool AddressComponent::HasNewerValuePrecendenceInMerging(
     const AddressComponent& newer_component) const {
-  return newer_component.GetVerificationStatus() >= GetVerificationStatus();
+  return !IsLessSignificantVerificationStatus(
+      newer_component.GetVerificationStatus(), GetVerificationStatus());
 }
 
 bool AddressComponent::MergeTokenEquivalentComponent(
@@ -1113,6 +1136,7 @@
     case VerificationStatus::kNoStatus:
     case VerificationStatus::kParsed:
     case VerificationStatus::kFormatted:
+    case VerificationStatus::kServerParsed:
       break;
     case VerificationStatus::kObserved:
       result += 1;
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.h b/components/autofill/core/browser/data_model/autofill_structured_address_component.h
index 9cfc4d1..913bebf2 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.h
@@ -37,8 +37,15 @@
   kObserved = 3,
   // The user used the autofill settings to verify and store this token.
   kUserVerified = 4,
+  // The token was parsed by the server.
+  kServerParsed = 5,
 };
 
+// Returns true if |left| has a less significant verification status compared to
+// |right|.
+bool IsLessSignificantVerificationStatus(VerificationStatus left,
+                                         VerificationStatus right);
+
 // The merge mode defines if and how two components are merged.
 enum MergeMode {
   // If one component has an empty value, use the non-empty one.
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
index b0c79ee..41034c8d 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component_unittest.cc
@@ -1663,5 +1663,26 @@
   VerifyTestValues(&older, older_values);
 }
 
+// Test the comparison of different Verification statuses.
+TEST(AutofillStructuredAddressAddressComponent,
+     TestIsLessSignificantVerificationStatus) {
+  EXPECT_TRUE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kParsed, VerificationStatus::kFormatted));
+  EXPECT_TRUE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kParsed, VerificationStatus::kServerParsed));
+  EXPECT_TRUE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kServerParsed, VerificationStatus::kObserved));
+  EXPECT_TRUE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kServerParsed, VerificationStatus::kUserVerified));
+  EXPECT_FALSE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kServerParsed, VerificationStatus::kFormatted));
+  EXPECT_FALSE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kServerParsed, VerificationStatus::kParsed));
+  EXPECT_FALSE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kObserved, VerificationStatus::kServerParsed));
+  EXPECT_FALSE(IsLessSignificantVerificationStatus(
+      VerificationStatus::kUserVerified, VerificationStatus::kServerParsed));
+}
+
 }  // namespace structured_address
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index d2fa11d..ac7434b 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -658,7 +658,7 @@
   // prediction routines.
   if (ShouldRunHeuristics()) {
     const FieldCandidatesMap field_type_map = FormField::ParseFormFields(
-        fields_, page_language_, is_form_tag_, log_manager);
+        fields_, original_page_language_, is_form_tag_, log_manager);
     for (const auto& field : fields_) {
       const auto iter = field_type_map.find(field->unique_renderer_id);
       if (iter != field_type_map.end()) {
@@ -711,8 +711,8 @@
   upload->set_data_present(EncodeFieldTypes(available_field_types));
   upload->set_passwords_revealed(passwords_were_revealed_);
   upload->set_has_form_tag(is_form_tag_);
-  if (!page_language_->empty() && randomized_encoder_ != nullptr) {
-    upload->set_language(page_language_.value());
+  if (!original_page_language_->empty() && randomized_encoder_ != nullptr) {
+    upload->set_language(original_page_language_.value());
   }
 
   auto triggering_event = (submission_event_ != SubmissionIndicatorEvent::NONE)
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index f4e54f9..cee4187 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -366,10 +366,12 @@
 
   void set_is_rich_query_enabled(bool v) { is_rich_query_enabled_ = v; }
 
-  const LanguageCode& page_language() const { return page_language_; }
+  const LanguageCode& original_page_language() const {
+    return original_page_language_;
+  }
 
-  void set_page_language(LanguageCode language) {
-    page_language_ = std::move(language);
+  void set_original_page_language(LanguageCode language) {
+    original_page_language_ = std::move(language);
   }
 
   bool value_from_dynamic_change_form() const {
@@ -584,9 +586,9 @@
   static base::string16 FindLongestCommonPrefix(
       const std::vector<base::string16>& strings);
 
-  // The language detected for this form's page, prior to any translations
+  // The language detected for this form's page, before any translations
   // performed by Chrome.
-  LanguageCode page_language_;
+  LanguageCode original_page_language_;
 
   // The id attribute of the form.
   base::string16 id_attribute_;
diff --git a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc
index 0dd1e6c..28fbe66 100644
--- a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc
+++ b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser.cc
@@ -5,12 +5,14 @@
 #include "components/autofill/core/browser/pattern_provider/pattern_configuration_parser.h"
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/values.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/pattern_provider/pattern_provider.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/language_code.h"
 #include "components/grit/components_resources.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -76,18 +78,17 @@
   return true;
 }
 
-// Callback which is used once the JSON is parsed.
-// |overwrite_equal_version| should be true when loading a remote
-// configuration. If the configuration versions are equal or
-// both unspecified (i.e. set to 0) this prioritizes the remote
+// Callback which is used once the JSON is parsed. If the configuration versions
+// are equal or both unspecified (i.e. set to 0) this prioritizes the remote
 // configuration over the local one.
-void OnJsonParsed(bool overwrite_equal_version,
-                  base::OnceClosure done_callback,
-                  data_decoder::DataDecoder::ValueOrError result) {
-  // Skip any processing in case of an error.
+void OnJsonParsed(data_decoder::DataDecoder::ValueOrError result) {
+  if (!base::FeatureList::IsEnabled(features::kAutofillUseRemotePatterns)) {
+    DVLOG(1) << "Remote patterns are disabled.";
+    return;
+  }
+
   if (!result.value) {
     DVLOG(1) << "Failed to parse PatternProvider configuration JSON string.";
-    std::move(done_callback).Run();
     return;
   }
 
@@ -97,15 +98,12 @@
 
   if (patterns && version.IsValid()) {
     DVLOG(1) << "Successfully parsed PatternProvider configuration.";
-
     PatternProvider& pattern_provider = PatternProvider::GetInstance();
     pattern_provider.SetPatterns(std::move(patterns.value()),
-                                 std::move(version), overwrite_equal_version);
+                                 std::move(version));
   } else {
     DVLOG(1) << "Failed to parse PatternProvider configuration JSON object.";
   }
-
-  std::move(done_callback).Run();
 }
 
 }  // namespace
@@ -169,35 +167,8 @@
 }
 
 void PopulateFromJsonString(std::string json_string) {
-  data_decoder::DataDecoder::ParseJsonIsolated(
-      std::move(json_string),
-      base::BindOnce(&OnJsonParsed, true, base::DoNothing::Once()));
-}
-
-void PopulateFromResourceBundle(base::OnceClosure done_callback) {
-  if (!ui::ResourceBundle::HasSharedInstance()) {
-    VLOG(1) << "Resource Bundle unavailable to load Autofill Matching Pattern "
-               "definitions.";
-    std::move(done_callback).Run();
-    return;
-  }
-
-  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-
-  // Load the string from the Resource Bundle on a worker thread, then
-  // securely parse the JSON in a separate process and call |OnJsonParsed|
-  // with the result.
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(&ui::ResourceBundle::LoadDataResourceString,
-                     base::Unretained(&bundle), IDR_AUTOFILL_REGEX_JSON),
-      base::BindOnce(
-          [](base::OnceClosure done_callback, std::string resource_string) {
-            data_decoder::DataDecoder::ParseJsonIsolated(
-                std::move(resource_string),
-                base::BindOnce(&OnJsonParsed, false, std::move(done_callback)));
-          },
-          std::move(done_callback)));
+  data_decoder::DataDecoder::ParseJsonIsolated(std::move(json_string),
+                                               base::BindOnce(&OnJsonParsed));
 }
 
 base::Optional<PatternProvider::Map>
diff --git a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc
index 17765439..de78bc2 100644
--- a/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc
+++ b/components/autofill/core/browser/pattern_provider/pattern_configuration_parser_unittest.cc
@@ -23,7 +23,7 @@
 // parsed to the map structure used by |PatternProvider| as
 // expected, given the input is valid.
 TEST(PatternConfigurationParserTest, WellFormedParsedCorrectly) {
-  std::string JSON_message = R"(
+  std::string json_message = R"(
     {
       "version": "1.0",
       "FULL_NAME": {
@@ -61,14 +61,14 @@
         ]
       }
     })";
-  base::Optional<base::Value> JSON_object =
-      base::JSONReader::Read(JSON_message);
+  base::Optional<base::Value> json_object =
+      base::JSONReader::Read(json_message);
 
-  ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
+  ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string.";
 
-  base::Version version = ExtractVersionFromJsonObject(JSON_object.value());
+  base::Version version = ExtractVersionFromJsonObject(json_object.value());
   base::Optional<PatternProvider::Map> optional_patterns =
-      GetConfigurationFromJsonObject(JSON_object.value());
+      GetConfigurationFromJsonObject(json_object.value());
 
   ASSERT_TRUE(version.IsValid());
   ASSERT_TRUE(optional_patterns);
@@ -101,7 +101,7 @@
 // Test that the parser does not return anything if some |MatchingPattern|
 // object is missing a property.
 TEST(PatternConfigurationParserTest, MalformedMissingProperty) {
-  std::string JSON_message = R"(
+  std::string json_message = R"(
     {
       "version": "1.0",
       "FULL_NAME": {
@@ -126,13 +126,13 @@
         ]
       }
     })";
-  base::Optional<base::Value> JSON_object =
-      base::JSONReader::Read(JSON_message);
+  base::Optional<base::Value> json_object =
+      base::JSONReader::Read(json_message);
 
-  ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
+  ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string.";
 
   base::Optional<PatternProvider::Map> optional_patterns =
-      GetConfigurationFromJsonObject(JSON_object.value());
+      GetConfigurationFromJsonObject(json_object.value());
 
   ASSERT_FALSE(optional_patterns);
 }
@@ -140,7 +140,7 @@
 // Test that the parser correctly sets the default version if
 // it is not present in the configuration.
 TEST(PatternConfigurationParserTest, MalformedMissingVersion) {
-  std::string JSON_message = R"(
+  std::string json_message = R"(
     {
       "FULL_NAME": {
         "en": [
@@ -154,12 +154,12 @@
         ]
       }
     })";
-  base::Optional<base::Value> JSON_object =
-      base::JSONReader::Read(JSON_message);
+  base::Optional<base::Value> json_object =
+      base::JSONReader::Read(json_message);
 
-  ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
+  ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string.";
 
-  base::Version version = ExtractVersionFromJsonObject(JSON_object.value());
+  base::Version version = ExtractVersionFromJsonObject(json_object.value());
 
   ASSERT_EQ(base::Version("0"), version);
 }
@@ -167,7 +167,7 @@
 // Test that the parser does not return anything if the inner key points
 // to a single object instead of a list.
 TEST(PatternConfigurationParserTest, MalformedNotList) {
-  std::string JSON_message = R"(
+  std::string json_message = R"(
     {
       "FULL_NAME": {
         "en": {
@@ -179,13 +179,13 @@
         }
       }
     })";
-  base::Optional<base::Value> JSON_object =
-      base::JSONReader::Read(JSON_message);
+  base::Optional<base::Value> json_object =
+      base::JSONReader::Read(json_message);
 
-  ASSERT_TRUE(JSON_object) << "Incorrectly formatted JSON string.";
+  ASSERT_TRUE(json_object) << "Incorrectly formatted JSON string.";
 
   base::Optional<PatternProvider::Map> optional_patterns =
-      GetConfigurationFromJsonObject(JSON_object.value());
+      GetConfigurationFromJsonObject(json_object.value());
 
   ASSERT_FALSE(optional_patterns);
 }
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider.cc b/components/autofill/core/browser/pattern_provider/pattern_provider.cc
index 73fbc08..f7d56f5 100644
--- a/components/autofill/core/browser/pattern_provider/pattern_provider.cc
+++ b/components/autofill/core/browser/pattern_provider/pattern_provider.cc
@@ -71,7 +71,7 @@
   static base::NoDestructor<PatternProvider> instance;
   static bool initialized = false;
   if (!initialized) {
-    instance->SetPatterns(CreateDefaultRegexPatterns(), base::Version(), true);
+    instance->SetPatterns(CreateDefaultRegexPatterns(), base::Version());
     initialized = true;
   }
   return *instance;
@@ -81,15 +81,12 @@
 PatternProvider::~PatternProvider() = default;
 
 void PatternProvider::SetPatterns(PatternProvider::Map patterns,
-                                  const base::Version version,
-                                  const bool overwrite_equal_version) {
+                                  const base::Version& version) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!pattern_version_.IsValid() ||
-      (version.IsValid() && pattern_version_ < version) ||
-      (version.IsValid() && pattern_version_ == version &&
-       overwrite_equal_version)) {
-    patterns_ = patterns;
+      (version.IsValid() && pattern_version_ <= version)) {
+    patterns_ = std::move(patterns);
     pattern_version_ = version;
     EnrichPatternsWithEnVersion(&patterns_);
     SortPatternsByScore(&patterns_);
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider.h b/components/autofill/core/browser/pattern_provider/pattern_provider.h
index 3ae65738..767ad35 100644
--- a/components/autofill/core/browser/pattern_provider/pattern_provider.h
+++ b/components/autofill/core/browser/pattern_provider/pattern_provider.h
@@ -27,7 +27,6 @@
  public:
   // The outer keys are field types or other pattern names. The inner keys are
   // page languages in lower case.
-  // TODO(crbug/1142413): decide on uppercase or lowercase.
   using Map = std::map<std::string,
                        std::map<LanguageCode, std::vector<MatchingPattern>>>;
 
@@ -35,9 +34,7 @@
   static PatternProvider& GetInstance();
 
   // Setter for loading patterns from external storage.
-  void SetPatterns(const Map patterns,
-                   const base::Version version,
-                   const bool overwrite_equal_version);
+  void SetPatterns(const Map patterns, const base::Version& version);
 
   // Find the patterns for a given ServerFieldType and for a given
   // |page_language|.
diff --git a/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc b/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc
index 56b3990..9152d3c 100644
--- a/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc
+++ b/components/autofill/core/browser/pattern_provider/pattern_provider_unittest.cc
@@ -18,9 +18,11 @@
 #include "components/autofill/core/browser/pattern_provider/pattern_provider.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/language_code.h"
+#include "components/grit/components_resources.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/resource/resource_bundle.h"
 
 namespace autofill {
 
@@ -65,10 +67,44 @@
         patterns[AutofillType::ServerFieldTypeToString(COMPANY_NAME)];
     company_patterns[kLanguageDe] = de_patterns;
     company_patterns[kLanguageEn] = en_patterns;
-    SetPatterns(patterns, base::Version(), true);
+    SetPatterns(std::move(patterns), base::Version());
   }
 };
 
+// Called when the JSON bundle has been parsed, and sets the PatternProvider's
+// patterns.
+void OnJsonParsed(base::OnceClosure done_callback,
+                  data_decoder::DataDecoder::ValueOrError result) {
+  base::Version version =
+      field_type_parsing::ExtractVersionFromJsonObject(result.value.value());
+  base::Optional<PatternProvider::Map> patterns =
+      field_type_parsing::GetConfigurationFromJsonObject(result.value.value());
+  ASSERT_TRUE(patterns);
+  ASSERT_TRUE(version.IsValid());
+  PatternProvider& pattern_provider = PatternProvider::GetInstance();
+  pattern_provider.SetPatterns(std::move(patterns.value()), std::move(version));
+  std::move(done_callback).Run();
+}
+
+// Loads the string from the Resource Bundle on a worker thread.
+void LoadPatternsFromResourceBundle() {
+  ASSERT_TRUE(ui::ResourceBundle::HasSharedInstance());
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  base::RunLoop run_loop;
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&ui::ResourceBundle::LoadDataResourceString,
+                     base::Unretained(&bundle), IDR_AUTOFILL_REGEX_JSON),
+      base::BindOnce(
+          [](base::OnceClosure done_callback, std::string resource_string) {
+            data_decoder::DataDecoder::ParseJsonIsolated(
+                std::move(resource_string),
+                base::BindOnce(&OnJsonParsed, std::move(done_callback)));
+          },
+          run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
 }  // namespace
 
 bool operator==(const MatchingPattern& mp1, const MatchingPattern& mp2) {
@@ -115,9 +151,7 @@
   ASSERT_NE(default_patterns, PatternProvider::GetInstance().patterns_);
 
   // Load the JSON explicitly from the file.
-  base::RunLoop run_loop;
-  field_type_parsing::PopulateFromResourceBundle(run_loop.QuitClosure());
-  run_loop.Run();
+  LoadPatternsFromResourceBundle();
 
   auto json_version = PatternProvider::GetInstance().pattern_version_;
   auto json_patterns = PatternProvider::GetInstance().patterns_;
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index e09998c..50fcc72 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -3924,18 +3924,21 @@
   TestAutofillClock test_clock;
   test_clock.SetNow(kArbitraryTime);
 
+  auto Check = [](const AutofillDataModel& data_model, size_t use_count,
+                  base::Time use_date, base::Time modification_date) {
+    EXPECT_EQ(use_count, data_model.use_count());
+    EXPECT_EQ(use_date, data_model.use_date());
+    EXPECT_EQ(modification_date, data_model.modification_date());
+  };
+
   AutofillProfile profile(test::GetFullProfile());
-  EXPECT_EQ(1U, profile.use_count());
-  EXPECT_EQ(kArbitraryTime, profile.use_date());
-  EXPECT_EQ(kArbitraryTime, profile.modification_date());
+  Check(profile, 1u, kArbitraryTime, kArbitraryTime);
   AddProfileToPersonalDataManager(profile);
 
   CreditCard credit_card(base::GenerateGUID(), test::kEmptyOrigin);
   test::SetCreditCardInfo(&credit_card, "John Dillinger",
                           "4234567890123456" /* Visa */, "01", "2999", "1");
-  EXPECT_EQ(1U, credit_card.use_count());
-  EXPECT_EQ(kArbitraryTime, credit_card.use_date());
-  EXPECT_EQ(kArbitraryTime, credit_card.modification_date());
+  Check(credit_card, 1u, kArbitraryTime, kArbitraryTime);
   personal_data_->AddCreditCard(credit_card);
 
   // Make sure everything is set up correctly.
@@ -3950,40 +3953,43 @@
       personal_data_->GetProfileByGUID(profile.guid());
   ASSERT_TRUE(added_profile);
   EXPECT_EQ(*added_profile, profile);
-  EXPECT_EQ(1U, added_profile->use_count());
-  EXPECT_EQ(kArbitraryTime, added_profile->use_date());
-  EXPECT_EQ(kArbitraryTime, added_profile->modification_date());
-
-  base::RunLoop run_loop;
-  EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks())
-      .WillOnce(QuitMessageLoop(&run_loop));
-  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1);
-
-  personal_data_->RecordUseOf(profile);
-
-  run_loop.Run();
+  Check(*added_profile, 1u, kArbitraryTime, kArbitraryTime);
 
   CreditCard* added_card =
       personal_data_->GetCreditCardByGUID(credit_card.guid());
   ASSERT_TRUE(added_card);
   EXPECT_EQ(*added_card, credit_card);
-  EXPECT_EQ(1U, added_card->use_count());
-  EXPECT_EQ(kArbitraryTime, added_card->use_date());
-  EXPECT_EQ(kArbitraryTime, added_card->modification_date());
-  personal_data_->RecordUseOf(credit_card);
+  Check(*added_card, 1u, kArbitraryTime, kArbitraryTime);
 
-  // Verify usage stats are updated.
+  // Use |profile|, then verify usage stats.
+  base::RunLoop profile_run_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks())
+      .WillOnce(QuitMessageLoop(&profile_run_loop));
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1);
+  personal_data_->RecordUseOf(profile);
+  profile_run_loop.Run();
+
   added_profile = personal_data_->GetProfileByGUID(profile.guid());
-  ASSERT_TRUE(added_profile);
-  EXPECT_EQ(2U, added_profile->use_count());
-  EXPECT_EQ(kSomeLaterTime, added_profile->use_date());
-  EXPECT_EQ(kArbitraryTime, added_profile->modification_date());
-
   added_card = personal_data_->GetCreditCardByGUID(credit_card.guid());
+  ASSERT_TRUE(added_profile);
   ASSERT_TRUE(added_card);
-  EXPECT_EQ(2U, added_card->use_count());
-  EXPECT_EQ(kSomeLaterTime, added_card->use_date());
-  EXPECT_EQ(kArbitraryTime, added_card->modification_date());
+  Check(*added_profile, 2u, kSomeLaterTime, kArbitraryTime);
+  Check(*added_card, 1u, kArbitraryTime, kArbitraryTime);
+
+  // Use |credit_card|, then verify usage stats.
+  base::RunLoop credit_card_run_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataFinishedProfileTasks())
+      .WillOnce(QuitMessageLoop(&credit_card_run_loop));
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1);
+  personal_data_->RecordUseOf(credit_card);
+  credit_card_run_loop.Run();
+
+  added_profile = personal_data_->GetProfileByGUID(profile.guid());
+  added_card = personal_data_->GetCreditCardByGUID(credit_card.guid());
+  ASSERT_TRUE(added_profile);
+  ASSERT_TRUE(added_card);
+  Check(*added_profile, 2u, kSomeLaterTime, kArbitraryTime);
+  Check(*added_card, 2u, kSomeLaterTime, kArbitraryTime);
 }
 
 TEST_F(PersonalDataManagerTest, ClearAllServerData) {
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 5a5c6a33..1899168 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -161,12 +161,12 @@
 const base::Feature kAutofillNameSectionsWithRendererIds{
     "AutofillNameSectionsWithRendererIds", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// When enabled, autofill suggestions are displayed in the keyboard accessory
+// When enabled, Autofill suggestions are displayed in the keyboard accessory
 // instead of the regular popup.
 const base::Feature kAutofillKeyboardAccessory{
     "AutofillKeyboardAccessory", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// When enabled, autofill will use new logic to strip both prefixes
+// When enabled, Autofill will use new logic to strip both prefixes
 // and suffixes when setting FormStructure::parseable_name_
 extern const base::Feature kAutofillLabelAffixRemoval{
     "AutofillLabelAffixRemoval", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -180,7 +180,7 @@
 const base::Feature kAutofillOffNoServerData{"AutofillOffNoServerData",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-// If feature is enabled, autofill will be disabled for mixed forms (forms on
+// If feature is enabled, Autofill will be disabled for mixed forms (forms on
 // HTTPS sites that submit over HTTP).
 const base::Feature kAutofillPreventMixedFormsFilling{
     "AutofillPreventMixedFormsFilling", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -285,6 +285,11 @@
     "AutofillUsePageLanguageToSelectFieldParsingPatterns",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// When enabled, Autofill will load remote patterns via the component updater.
+// TODO(crbug/1121990): Remove once launched.
+extern const base::Feature kAutofillUseRemotePatterns{
+    "AutofillUseRemotePatterns", base::FEATURE_DISABLED_BY_DEFAULT};
+
 #if defined(OS_ANDROID)
 // Controls whether the Autofill manual fallback for Addresses and Payments is
 // present on Android.
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 304bfd0..010fbda5 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -71,6 +71,7 @@
 extern const base::Feature kAutofillUseImprovedLabelDisambiguation;
 extern const base::Feature kAutofillUseNewSectioningMethod;
 extern const base::Feature kAutofillUsePageLanguageToSelectFieldParsingPatterns;
+extern const base::Feature kAutofillUseRemotePatterns;
 
 #if defined(OS_ANDROID)
 extern const base::Feature kAutofillManualFallbackAndroid;
diff --git a/components/autofill/core/common/language_code.h b/components/autofill/core/common/language_code.h
index eb7a6789..ae6570fe 100644
--- a/components/autofill/core/common/language_code.h
+++ b/components/autofill/core/common/language_code.h
@@ -9,15 +9,18 @@
 #include <string>
 #include <utility>
 
+#include "base/check.h"
 #include "base/ranges/algorithm.h"
 #include "base/types/strong_alias.h"
 
 namespace autofill {
 
 // Following the implicit conventions in //components/translate, a LanguageCode
-// in  is a lowercase alphabetic string of length up to 3, or "zh-CN", or
-// "zh-TW". A non-exhaustive list of common values is
+// is a lowercase alphabetic string of length up to 3, or "zh-CN", or "zh-TW". A
+// non-exhaustive list of common values is
 // translate::kDefaultSupportedLanguages.
+// C++ small string optimization keeps these objects lightweight so that copying
+// should not be a worry.
 class LanguageCode
     : public base::StrongAlias<class LanguageCodeTag, std::string> {
  private:
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java
index 62b3ff7..72a6336 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java
@@ -11,7 +11,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
+import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
@@ -100,10 +100,10 @@
      * @param intent The intent to be handled by the embedder.
      * @param referrerUrl The referrer for the current navigation.
      * @param fallbackUrl The fallback URL to load if the intent cannot be handled by the embedder.
-     * @return The OverrideUrlLoadingResultType for the action taken by the embedder.
+     * @return The OverrideUrlLoadingResult for the action taken by the embedder.
      */
-    @OverrideUrlLoadingResultType
-    int handleIncognitoIntentTargetingSelf(Intent intent, String referrerUrl, String fallbackUrl);
+    OverrideUrlLoadingResult handleIncognitoIntentTargetingSelf(
+            Intent intent, String referrerUrl, String fallbackUrl);
 
     /**
      * Loads a URL as specified by |loadUrlParams| if possible. May fail in exceptional conditions
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index a89afce..86feda6 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -204,6 +204,38 @@
     }
 
     /**
+     * Packages information about the result of a check of whether we should override URL loading.
+     */
+    public static class OverrideUrlLoadingResult {
+        @OverrideUrlLoadingResultType
+        int mResultType;
+
+        OverrideUrlLoadingResult(@OverrideUrlLoadingResultType int resultType) {
+            mResultType = resultType;
+        }
+
+        public @OverrideUrlLoadingResultType int getResultType() {
+            return mResultType;
+        }
+
+        public static OverrideUrlLoadingResult forAsyncAction() {
+            return new OverrideUrlLoadingResult(
+                    OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION);
+        }
+        public static OverrideUrlLoadingResult forNoOverride() {
+            return new OverrideUrlLoadingResult(OverrideUrlLoadingResultType.NO_OVERRIDE);
+        }
+        public static OverrideUrlLoadingResult forClobberingTab() {
+            return new OverrideUrlLoadingResult(
+                    OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB);
+        }
+        public static OverrideUrlLoadingResult forExternalIntent() {
+            return new OverrideUrlLoadingResult(
+                    OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT);
+        }
+    }
+
+    /**
      * Constructs a new instance of {@link ExternalNavigationHandler}, using the injected
      * {@link ExternalNavigationDelegate}.
      */
@@ -217,8 +249,7 @@
      * @return Whether the URL generated an intent, caused a navigation in
      *         current tab, or wasn't handled at all.
      */
-    public @OverrideUrlLoadingResultType int shouldOverrideUrlLoading(
-            ExternalNavigationParams params) {
+    public OverrideUrlLoadingResult shouldOverrideUrlLoading(ExternalNavigationParams params) {
         if (DEBUG) Log.i(TAG, "shouldOverrideUrlLoading called on " + params.getUrl());
         Intent targetIntent;
         // Perform generic parsing of the URI to turn it into an Intent.
@@ -226,7 +257,7 @@
             targetIntent = Intent.parseUri(params.getUrl(), Intent.URI_INTENT_SCHEME);
         } catch (Exception ex) {
             Log.w(TAG, "Bad URI %s", params.getUrl(), ex);
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         String browserFallbackUrl =
@@ -241,14 +272,13 @@
         MutableBoolean canLaunchExternalFallbackResult = new MutableBoolean();
 
         long time = SystemClock.elapsedRealtime();
-        @OverrideUrlLoadingResultType
-        int result = shouldOverrideUrlLoadingInternal(
+        OverrideUrlLoadingResult result = shouldOverrideUrlLoadingInternal(
                 params, targetIntent, browserFallbackUrl, canLaunchExternalFallbackResult);
         assert canLaunchExternalFallbackResult.get() != null;
         RecordHistogram.recordTimesHistogram(
                 "Android.StrictMode.OverrideUrlLoadingTime", SystemClock.elapsedRealtime() - time);
 
-        if (result != OverrideUrlLoadingResultType.NO_OVERRIDE) {
+        if (result.getResultType() != OverrideUrlLoadingResultType.NO_OVERRIDE) {
             int pageTransitionCore = params.getPageTransition() & PageTransition.CORE_MASK;
             boolean isFormSubmit = pageTransitionCore == PageTransition.FORM_SUBMIT;
             boolean isRedirectFromFormSubmit = isFormSubmit && params.isRedirect();
@@ -257,7 +287,8 @@
                         "Android.Intent.LaunchExternalAppFormSubmitHasUserGesture",
                         params.hasUserGesture());
             }
-        } else if (result == OverrideUrlLoadingResultType.NO_OVERRIDE && browserFallbackUrl != null
+        } else if (result.getResultType() == OverrideUrlLoadingResultType.NO_OVERRIDE
+                && browserFallbackUrl != null
                 && (params.getRedirectHandler() == null
                         // For instance, if this is a chained fallback URL, we ignore it.
                         || !params.getRedirectHandler().shouldNotOverrideUrlLoading())) {
@@ -268,7 +299,7 @@
         return result;
     }
 
-    private @OverrideUrlLoadingResultType int handleFallbackUrl(ExternalNavigationParams params,
+    private OverrideUrlLoadingResult handleFallbackUrl(ExternalNavigationParams params,
             Intent targetIntent, String browserFallbackUrl, boolean canLaunchExternalFallback) {
         if (mDelegate.isIntentToInstantApp(targetIntent)) {
             RecordHistogram.recordEnumeratedHistogram("Android.InstantApps.DirectInstantAppsIntent",
@@ -286,7 +317,7 @@
                 List<ResolveInfo> resolvingInfos = queryIntentActivities(intent);
                 if (!isAlreadyInTargetWebApk(resolvingInfos, params)
                         && launchWebApkIfSoleIntentHandler(resolvingInfos, intent)) {
-                    return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+                    return OverrideUrlLoadingResult.forExternalIntent();
                 }
             } catch (Exception e) {
                 if (DEBUG) Log.i(TAG, "Could not parse fallback url as intent");
@@ -307,7 +338,7 @@
         // http://crbug.com/364522.
         if (!params.isMainFrame()) {
             if (DEBUG) Log.i(TAG, "Don't support fallback url in subframes");
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         // NOTE: any further redirection from fall-back URL should not override URL loading.
@@ -321,9 +352,9 @@
         return clobberCurrentTab(browserFallbackUrl, params.getReferrerUrl());
     }
 
-    private void printDebugShouldOverrideUrlLoadingResultType(int result) {
+    private void printDebugShouldOverrideUrlLoadingResultType(OverrideUrlLoadingResult result) {
         String resultString;
-        switch (result) {
+        switch (result.getResultType()) {
             case OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT:
                 resultString = "OVERRIDE_WITH_EXTERNAL_INTENT";
                 break;
@@ -471,7 +502,7 @@
      *         intent.)
      */
     @VisibleForTesting
-    protected @OverrideUrlLoadingResultType int clobberCurrentTab(String url, String referrerUrl) {
+    protected OverrideUrlLoadingResult clobberCurrentTab(String url, String referrerUrl) {
         int transitionType = PageTransition.LINK;
         final LoadUrlParams loadUrlParams = new LoadUrlParams(url, transitionType);
         if (!TextUtils.isEmpty(referrerUrl)) {
@@ -488,7 +519,7 @@
                     mDelegate.loadUrlIfPossible(loadUrlParams);
                 }
             });
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB;
+            return OverrideUrlLoadingResult.forClobberingTab();
         } else {
             assert false : "clobberCurrentTab was called with an empty tab.";
             Uri uri = Uri.parse(url);
@@ -498,7 +529,7 @@
             intent.addCategory(Intent.CATEGORY_BROWSABLE);
             intent.setPackage(packageName);
             startActivity(intent, false, mDelegate);
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            return OverrideUrlLoadingResult.forExternalIntent();
         }
     }
 
@@ -779,17 +810,17 @@
      * If the intent can't be resolved, we should fall back to the browserFallbackUrl, or try to
      * find the app on the market if no fallback is provided.
      */
-    private int handleUnresolvableIntent(
+    private OverrideUrlLoadingResult handleUnresolvableIntent(
             ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl) {
         // Fallback URL will be handled by the caller of shouldOverrideUrlLoadingInternal.
-        if (browserFallbackUrl != null) return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        if (browserFallbackUrl != null) return OverrideUrlLoadingResult.forNoOverride();
         if (targetIntent.getPackage() != null) return handleWithMarketIntent(params, targetIntent);
 
         if (DEBUG) Log.i(TAG, "Could not find an external activity to use");
-        return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        return OverrideUrlLoadingResult.forNoOverride();
     }
 
-    private @OverrideUrlLoadingResultType int handleWithMarketIntent(
+    private OverrideUrlLoadingResult handleWithMarketIntent(
             ExternalNavigationParams params, Intent intent) {
         String marketReferrer = IntentUtils.safeGetStringExtra(intent, EXTRA_MARKET_REFERRER);
         if (TextUtils.isEmpty(marketReferrer)) {
@@ -851,9 +882,9 @@
      * This is the catch-all path for any intent that the app can handle that doesn't have a
      * specialized external app handling it.
      */
-    private @OverrideUrlLoadingResultType int fallBackToHandlingInApp() {
+    private OverrideUrlLoadingResult fallBackToHandlingInApp() {
         if (DEBUG) Log.i(TAG, "No specialized handler for URL");
-        return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        return OverrideUrlLoadingResult.forNoOverride();
     }
 
     /**
@@ -950,7 +981,7 @@
                 params.isRendererInitiated(), params.getInitiatorOrigin());
     }
 
-    private @OverrideUrlLoadingResultType int handleExternalIncognitoIntent(Intent targetIntent,
+    private OverrideUrlLoadingResult handleExternalIncognitoIntent(Intent targetIntent,
             ExternalNavigationParams params, String browserFallbackUrl,
             boolean shouldProxyForInstantApps) {
         // This intent may leave this app. Warn the user that incognito does not carry over
@@ -959,10 +990,10 @@
                     params.shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent(),
                     shouldProxyForInstantApps)) {
             if (DEBUG) Log.i(TAG, "Incognito navigation out");
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION;
+            return OverrideUrlLoadingResult.forAsyncAction();
         }
         if (DEBUG) Log.i(TAG, "Failed to show incognito alert dialog.");
-        return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        return OverrideUrlLoadingResult.forNoOverride();
     }
 
     /**
@@ -1129,7 +1160,7 @@
                 || blockExternalNavFromBackgroundTab(params) || ignoreBackForwardNav(params);
     }
 
-    private @OverrideUrlLoadingResultType int shouldOverrideUrlLoadingInternal(
+    private OverrideUrlLoadingResult shouldOverrideUrlLoadingInternal(
             ExternalNavigationParams params, Intent targetIntent,
             @Nullable String browserFallbackUrl, MutableBoolean canLaunchExternalFallbackResult) {
         sanitizeQueryIntentActivitiesIntent(targetIntent);
@@ -1137,29 +1168,29 @@
         canLaunchExternalFallbackResult.set(false);
 
         if (shouldBlockAllExternalAppLaunches(params)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (handleWithAutofillAssistant(params, targetIntent, browserFallbackUrl)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         boolean isExternalProtocol = !UrlUtilities.isAcceptedScheme(params.getUrl());
 
         if (isInternalPdfDownload(isExternalProtocol, params)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         // This check should happen for reloads, navigations, etc..., which is why
         // it occurs before the subsequent blocks.
         if (startFileIntentIfNecessary(params, targetIntent)) {
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION;
+            return OverrideUrlLoadingResult.forAsyncAction();
         }
 
         // This should come after file intents, but before any returns of
         // OVERRIDE_WITH_EXTERNAL_INTENT.
         if (externalIntentRequestsDisabledForUrl(params)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         int pageTransitionCore = params.getPageTransition() & PageTransition.CORE_MASK;
@@ -1178,42 +1209,42 @@
                 (isLink && isFromIntent && params.isRedirect()) || isOnEffectiveIntentRedirect;
 
         if (handleCCTRedirectsToInstantApps(params, isExternalProtocol, incomingIntentRedirect)) {
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            return OverrideUrlLoadingResult.forExternalIntent();
         } else if (redirectShouldStayInApp(params, isExternalProtocol, targetIntent)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (!preferToShowIntentPicker(params, pageTransitionCore, isExternalProtocol, isFormSubmit,
                     linkNotFromIntent, incomingIntentRedirect)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
-        if (isLinkFromChromeInternalPage(params)) return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        if (isLinkFromChromeInternalPage(params)) return OverrideUrlLoadingResult.forNoOverride();
 
         if (handleWtaiMcProtocol(params)) {
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            return OverrideUrlLoadingResult.forExternalIntent();
         }
         // TODO: handle other WTAI schemes.
-        if (isUnhandledWtaiProtocol(params)) return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        if (isUnhandledWtaiProtocol(params)) return OverrideUrlLoadingResult.forNoOverride();
 
         boolean hasIntentScheme = params.getUrl().startsWith(UrlConstants.INTENT_URL_SHORT_PREFIX)
                 || params.getUrl().startsWith(UrlConstants.APP_INTENT_URL_SHORT_PREFIX);
         if (hasInternalScheme(params, targetIntent, hasIntentScheme)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (hasContentScheme(params, targetIntent, hasIntentScheme)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (hasFileSchemeInIntentURI(targetIntent, hasIntentScheme)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
-        if (isYoutubePairingCode(params)) return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        if (isYoutubePairingCode(params)) return OverrideUrlLoadingResult.forNoOverride();
 
         if (shouldStayInIncognito(params, isExternalProtocol)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (!maybeSetSmsPackage(targetIntent)) maybeRecordPhoneIntentMetrics(targetIntent);
@@ -1236,7 +1267,7 @@
         if (!isExternalProtocol && !hasSpecializedHandler) {
             if (fallBackToHandlingWithInstantApp(
                         params, incomingIntentRedirect, linkNotFromIntent)) {
-                return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+                return OverrideUrlLoadingResult.forExternalIntent();
             }
             return fallBackToHandlingInApp();
         }
@@ -1246,14 +1277,14 @@
 
         if (shouldStayWithinHost(
                     params, isLink, isFormSubmit, resolvingInfos, isExternalProtocol)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         boolean isDirectInstantAppsIntent =
                 isExternalProtocol && mDelegate.isIntentToInstantApp(targetIntent);
         boolean shouldProxyForInstantApps = isDirectInstantAppsIntent && isSerpReferrer();
         if (preventDirectInstantAppsIntent(isDirectInstantAppsIntent, shouldProxyForInstantApps)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         prepareExternalIntent(targetIntent, params, resolvingInfos, shouldProxyForInstantApps);
@@ -1279,18 +1310,18 @@
 
         if (shouldKeepIntentRedirectInApp(
                     params, incomingIntentRedirect, resolvingInfos, isExternalProtocol)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (isAlreadyInTargetWebApk(resolvingInfos, params)) {
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         } else if (launchWebApkIfSoleIntentHandler(resolvingInfos, targetIntent)) {
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            return OverrideUrlLoadingResult.forExternalIntent();
         }
         if (launchExternalIntent(targetIntent, shouldProxyForInstantApps)) {
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            return OverrideUrlLoadingResult.forExternalIntent();
         }
-        return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        return OverrideUrlLoadingResult.forNoOverride();
     }
 
     /**
@@ -1312,7 +1343,7 @@
      * @return OVERRIDE_WITH_EXTERNAL_INTENT when we successfully started market activity,
      *         NO_OVERRIDE otherwise.
      */
-    private @OverrideUrlLoadingResultType int sendIntentToMarket(
+    private OverrideUrlLoadingResult sendIntentToMarket(
             String packageName, String marketReferrer, ExternalNavigationParams params) {
         Uri marketUri =
                 new Uri.Builder()
@@ -1332,7 +1363,7 @@
         if (!deviceCanHandleIntent(intent)) {
             // Exit early if the Play Store isn't available. (https://crbug.com/820709)
             if (DEBUG) Log.i(TAG, "Play Store not installed.");
-            return OverrideUrlLoadingResultType.NO_OVERRIDE;
+            return OverrideUrlLoadingResult.forNoOverride();
         }
 
         if (params.isIncognito()) {
@@ -1340,14 +1371,14 @@
 
                         params.shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent(), false)) {
                 if (DEBUG) Log.i(TAG, "Failed to show incognito alert dialog.");
-                return OverrideUrlLoadingResultType.NO_OVERRIDE;
+                return OverrideUrlLoadingResult.forNoOverride();
             }
             if (DEBUG) Log.i(TAG, "Incognito intent to Play Store.");
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION;
+            return OverrideUrlLoadingResult.forAsyncAction();
         } else {
             startActivity(intent, false, mDelegate);
             if (DEBUG) Log.i(TAG, "Intent to Play Store.");
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            return OverrideUrlLoadingResult.forExternalIntent();
         }
     }
 
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
index 7b46d1a..817f638 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
@@ -81,7 +81,8 @@
 
         ExternalNavigationParams params =
                 new ExternalNavigationParams.Builder(url, incognito).setOpenInNewTab(true).build();
-        mLastOverrideUrlLoadingResultType = mExternalNavHandler.shouldOverrideUrlLoading(params);
+        mLastOverrideUrlLoadingResultType =
+                mExternalNavHandler.shouldOverrideUrlLoading(params).getResultType();
         return mLastOverrideUrlLoadingResultType
                 != ExternalNavigationHandler.OverrideUrlLoadingResultType.NO_OVERRIDE;
     }
@@ -131,7 +132,7 @@
                 buildExternalNavigationParams(navigationParams, redirectHandler, shouldCloseTab)
                         .build();
         @OverrideUrlLoadingResultType
-        int result = mExternalNavHandler.shouldOverrideUrlLoading(params);
+        int result = mExternalNavHandler.shouldOverrideUrlLoading(params).getResultType();
         mLastOverrideUrlLoadingResultType = result;
 
         switch (result) {
diff --git a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index 5165306..fe07bf1 100644
--- a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -34,6 +34,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
@@ -1235,9 +1236,9 @@
                 new ExternalNavigationParams.Builder(YOUTUBE_MOBILE_URL, false)
                         .setOpenInNewTab(true)
                         .build();
-        @OverrideUrlLoadingResultType
-        int result = mUrlHandler.shouldOverrideUrlLoading(params);
-        Assert.assertEquals(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT, result);
+        OverrideUrlLoadingResult result = mUrlHandler.shouldOverrideUrlLoading(params);
+        Assert.assertEquals(
+                OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT, result.getResultType());
         Assert.assertTrue(mDelegate.startActivityIntent != null);
         Assert.assertTrue(
                 mDelegate.startActivityIntent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false));
@@ -2031,11 +2032,10 @@
         }
 
         @Override
-        protected @OverrideUrlLoadingResultType int clobberCurrentTab(
-                String url, String referrerUrl) {
+        protected OverrideUrlLoadingResult clobberCurrentTab(String url, String referrerUrl) {
             mNewUrlAfterClobbering = url;
             mReferrerUrlForClobbering = referrerUrl;
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB;
+            return OverrideUrlLoadingResult.forClobberingTab();
         }
     };
 
@@ -2115,11 +2115,11 @@
         }
 
         @Override
-        public @OverrideUrlLoadingResultType int handleIncognitoIntentTargetingSelf(
+        public OverrideUrlLoadingResult handleIncognitoIntentTargetingSelf(
                 Intent intent, String referrerUrl, String fallbackUrl) {
             handleIncognitoIntentTargetingSelfCalled = true;
-            if (mCanLoadUrlInTab) return OverrideUrlLoadingResultType.OVERRIDE_WITH_CLOBBERING_TAB;
-            return OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT;
+            if (mCanLoadUrlInTab) return OverrideUrlLoadingResult.forClobberingTab();
+            return OverrideUrlLoadingResult.forExternalIntent();
         }
 
         @Override
@@ -2412,8 +2412,7 @@
                             .setHasUserGesture(mHasUserGesture)
                             .setIsRendererInitiated(mIsRendererInitiated)
                             .build();
-            @OverrideUrlLoadingResultType
-            int result = mUrlHandler.shouldOverrideUrlLoading(params);
+            OverrideUrlLoadingResult result = mUrlHandler.shouldOverrideUrlLoading(params);
             boolean startActivityCalled = false;
             boolean startWebApkCalled = false;
 
@@ -2431,7 +2430,7 @@
                 }
             }
 
-            Assert.assertEquals(expectedOverrideResult, result);
+            Assert.assertEquals(expectedOverrideResult, result.getResultType());
             Assert.assertEquals(expectStartIncognito, mUrlHandler.mStartIncognitoIntentCalled);
             Assert.assertEquals(expectStartActivity, startActivityCalled);
             Assert.assertEquals(expectStartWebApk, startWebApkCalled);
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 70b9a2aa..fef8a67 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -317,7 +317,6 @@
         OnDeviceHeadProvider::Create(provider_client_.get(), this);
     if (on_device_head_provider_) {
       providers_.push_back(on_device_head_provider_);
-      on_device_head_provider_->AddModelUpdateCallback();
     }
   }
   if (provider_types & AutocompleteProvider::TYPE_CLIPBOARD) {
diff --git a/components/omnibox/browser/on_device_head_provider.cc b/components/omnibox/browser/on_device_head_provider.cc
index b142afb..3c93e13b 100644
--- a/components/omnibox/browser/on_device_head_provider.cc
+++ b/components/omnibox/browser/on_device_head_provider.cc
@@ -22,6 +22,7 @@
 #include "components/omnibox/browser/base_search_provider.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/on_device_head_provider.h"
+#include "components/omnibox/browser/on_device_model_update_listener.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/search_engines/omnibox_focus_type.h"
 #include "components/search_engines/search_terms_data.h"
@@ -94,20 +95,6 @@
 
 OnDeviceHeadProvider::~OnDeviceHeadProvider() {}
 
-void OnDeviceHeadProvider::AddModelUpdateCallback() {
-  // Bail out if we have already subscribed.
-  if (model_update_subscription_) {
-    return;
-  }
-
-  auto* model_update_listener = OnDeviceModelUpdateListener::GetInstance();
-  if (model_update_listener) {
-    model_update_subscription_ = model_update_listener->AddModelUpdateCallback(
-        base::BindRepeating(&OnDeviceHeadProvider::OnModelUpdate,
-                            weak_ptr_factory_.GetWeakPtr()));
-  }
-}
-
 bool OnDeviceHeadProvider::IsOnDeviceHeadProviderAllowed(
     const AutocompleteInput& input) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
@@ -158,7 +145,7 @@
     return;
 
   matches_.clear();
-  if (input.text().empty() || model_filename_.empty())
+  if (input.text().empty() || GetOnDeviceHeadModelFilename().empty())
     return;
 
   // Note |on_device_search_request_id_| has already been changed in |Stop| so
@@ -198,13 +185,6 @@
   done_ = true;
 }
 
-void OnDeviceHeadProvider::OnModelUpdate(
-    const std::string& new_model_filename) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
-  if (!new_model_filename.empty())
-    model_filename_ = new_model_filename;
-}
-
 // TODO(crbug.com/925072): post OnDeviceHeadModel::GetSuggestionsForPrefix
 // directly and remove this function.
 // static
@@ -252,7 +232,8 @@
   base::PostTaskAndReplyWithResult(
       worker_task_runner_.get(), FROM_HERE,
       base::BindOnce(&OnDeviceHeadProvider::GetSuggestionsFromModel,
-                     model_filename_, provider_max_matches_, std::move(params)),
+                     GetOnDeviceHeadModelFilename(), provider_max_matches_,
+                     std::move(params)),
       base::BindOnce(&OnDeviceHeadProvider::SearchDone,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -302,3 +283,10 @@
   done_ = true;
   listener_->OnProviderUpdate(true);
 }
+
+std::string OnDeviceHeadProvider::GetOnDeviceHeadModelFilename() const {
+  auto* model_update_listener = OnDeviceModelUpdateListener::GetInstance();
+  return model_update_listener != nullptr
+             ? model_update_listener->model_filename()
+             : "";
+}
diff --git a/components/omnibox/browser/on_device_head_provider.h b/components/omnibox/browser/on_device_head_provider.h
index 26273b2..8035214f 100644
--- a/components/omnibox/browser/on_device_head_provider.h
+++ b/components/omnibox/browser/on_device_head_provider.h
@@ -14,7 +14,6 @@
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/on_device_head_model.h"
-#include "components/omnibox/browser/on_device_model_update_listener.h"
 
 class AutocompleteProviderListener;
 
@@ -32,10 +31,6 @@
   static OnDeviceHeadProvider* Create(AutocompleteProviderClient* client,
                                       AutocompleteProviderListener* listener);
 
-  // Adds a callback to on device head model updater listener which will update
-  // |model_filename_| once the model is ready on disk.
-  void AddModelUpdateCallback();
-
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
   void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
@@ -67,9 +62,9 @@
   // fetches by DoSearch and then calls OnProviderUpdate.
   void SearchDone(std::unique_ptr<OnDeviceHeadProviderParams> params);
 
-  // Used by OnDeviceModelUpdateListener to notify this provider when new model
-  // is available.
-  void OnModelUpdate(const std::string& new_model_filename);
+  // Helper functions to read model filename from the static
+  // OnDeviceModelUpdateListener instance.
+  std::string GetOnDeviceHeadModelFilename() const;
 
   // Fetches suggestions matching the params from the given on device head
   // model.
@@ -85,22 +80,15 @@
   // added to offload expensive operations out of the UI sequence.
   scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
 
-  // Sequence checker that ensure utocomplete request handling will only happen
-  // main thread.
+  // Sequence checker that ensure autocomplete request handling will only happen
+  // on main thread.
   SEQUENCE_CHECKER(main_sequence_checker_);
 
-  // The filename points to the on device head model on the disk.
-  std::string model_filename_;
-
   // The request id used to trace current request to the on device head model.
   // The id will be increased whenever a new request is received from the
   // AutocompleteController.
   size_t on_device_search_request_id_;
 
-  // Owns the callback added to the listener such that it can be removed
-  // automatically from the listener on provider's deconstruction.
-  base::CallbackListSubscription model_update_subscription_;
-
   base::WeakPtrFactory<OnDeviceHeadProvider> weak_ptr_factory_{this};
 };
 
diff --git a/components/omnibox/browser/on_device_head_provider_unittest.cc b/components/omnibox/browser/on_device_head_provider_unittest.cc
index 2bb3ce6..4f03c7e 100644
--- a/components/omnibox/browser/on_device_head_provider_unittest.cc
+++ b/components/omnibox/browser/on_device_head_provider_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
 #include "components/omnibox/browser/on_device_head_model.h"
+#include "components/omnibox/browser/on_device_model_update_listener.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/search_engines/omnibox_focus_type.h"
@@ -32,7 +33,6 @@
     client_.reset(new FakeAutocompleteProviderClient());
     SetTestOnDeviceHeadModel();
     provider_ = OnDeviceHeadProvider::Create(client_.get(), this);
-    provider_->AddModelUpdateCallback();
     task_environment_.RunUntilIdle();
   }
 
@@ -60,9 +60,9 @@
   }
 
   void ResetModelInstance() {
-    if (provider_) {
-      provider_->model_filename_.clear();
-    }
+    auto* update_listener = OnDeviceModelUpdateListener::GetInstance();
+    if (update_listener)
+      update_listener->ResetListenerForTest();
   }
 
   bool IsOnDeviceHeadProviderAllowed(const AutocompleteInput& input) {
diff --git a/components/omnibox/browser/on_device_model_update_listener.cc b/components/omnibox/browser/on_device_model_update_listener.cc
index 1cd0fa23..5a67609 100644
--- a/components/omnibox/browser/on_device_model_update_listener.cc
+++ b/components/omnibox/browser/on_device_model_update_listener.cc
@@ -8,7 +8,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
-#include "base/task_runner_util.h"
 #include "build/build_config.h"
 
 namespace {
@@ -41,35 +40,28 @@
   return listener.get();
 }
 
-OnDeviceModelUpdateListener::OnDeviceModelUpdateListener()
-    : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::TaskPriority::BEST_EFFORT,
-           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock()})) {}
+std::string OnDeviceModelUpdateListener::model_filename() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return model_filename_;
+}
+
+OnDeviceModelUpdateListener::OnDeviceModelUpdateListener() = default;
 
 OnDeviceModelUpdateListener::~OnDeviceModelUpdateListener() = default;
 
-base::CallbackListSubscription
-OnDeviceModelUpdateListener::AddModelUpdateCallback(
-    ModelUpdateCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (!model_filename_.empty())
-    callback.Run(model_filename_);
-  return model_update_callbacks_.Add(callback);
-}
-
 void OnDeviceModelUpdateListener::OnModelUpdate(
     const base::FilePath& model_dir) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!model_dir.empty() && model_dir != model_dir_) {
     model_dir_ = model_dir;
-    base::PostTaskAndReplyWithResult(
-        task_runner_.get(), FROM_HERE,
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        {base::TaskPriority::BEST_EFFORT,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock()},
         base::BindOnce(&GetModelFilenameFromDirectory, model_dir),
         base::BindOnce([](const std::string filename) {
-          if (!filename.empty()) {
+          if (!filename.empty())
             GetInstance()->model_filename_ = filename;
-            GetInstance()->model_update_callbacks_.Notify(filename);
-          }
         }));
   }
 }
diff --git a/components/omnibox/browser/on_device_model_update_listener.h b/components/omnibox/browser/on_device_model_update_listener.h
index e162176..1d8d32c 100644
--- a/components/omnibox/browser/on_device_model_update_listener.h
+++ b/components/omnibox/browser/on_device_model_update_listener.h
@@ -7,33 +7,22 @@
 
 #include <memory>
 
-#include "base/callback.h"
-#include "base/callback_list.h"
 #include "base/files/file_path.h"
 #include "base/no_destructor.h"
 #include "base/threading/thread_checker.h"
 
-// This class is used by OnDeviceHeadSuggestComponentInstaller to notify
-// OnDeviceHeadProvider when on device model update is finished.
+// This class is used by OnDeviceHeadSuggestComponentInstaller to hold the
+// directory & filename for the on device model downloaded by Component Updater.
 class OnDeviceModelUpdateListener {
  public:
-  using ModelUpdateCallback =
-      base::RepeatingCallback<void(const std::string& new_model_filename)>;
-  using UpdateCallbacks = base::CallbackList<void(const std::string&)>;
-  using UpdateSubscription = UpdateCallbacks::Subscription;
 
   static OnDeviceModelUpdateListener* GetInstance();
 
-  // Adds a callback which will be run on model update. This method will also
-  // notify the provider immediately if a model is available.
-  base::CallbackListSubscription AddModelUpdateCallback(
-      ModelUpdateCallback callback);
-
-  // Called by Component Updater when model update is completed to notify the
-  // on device head provider to reload the model.
+  // Called by Component Updater when model update is completed to update
+  // |model_dir_| and |model_filename_|.
   void OnModelUpdate(const base::FilePath& model_dir);
 
-  std::string model_filename() const { return model_filename_; }
+  std::string model_filename() const;
 
  private:
   friend class base::NoDestructor<OnDeviceModelUpdateListener>;
@@ -53,13 +42,6 @@
   // The filename of the model.
   std::string model_filename_;
 
-  // A list of callbacks which will be run on model update.
-  UpdateCallbacks model_update_callbacks_;
-
-  // The task runner which will be used to run file operations and
-  // |model_update_callbacks_|.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
   THREAD_CHECKER(thread_checker_);
 };
 
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
index 665c995..b0cb261 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
@@ -488,10 +488,6 @@
             mPendingRunAfterDismissTask.run();
             mPendingRunAfterDismissTask = null;
         }
-        if (mSubpageController != null) {
-            mSubpageController.onSubpageRemoved();
-            mSubpageController = null;
-        }
         mWebContentsObserver.destroy();
         mWebContentsObserver = null;
         PageInfoControllerJni.get().destroy(mNativePageInfoController, PageInfoController.this);
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java
index 9e7ff66..5b170292 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoCookiesController.java
@@ -122,8 +122,10 @@
     public void onSubpageRemoved() {
         assert mSubPage != null;
         AppCompatActivity host = (AppCompatActivity) mRowView.getContext();
-        host.getSupportFragmentManager().beginTransaction().remove(mSubPage).commitNow();
+        PageInfoCookiesPreference subBage = mSubPage;
         mSubPage = null;
+        if (host.isFinishing()) return;
+        host.getSupportFragmentManager().beginTransaction().remove(subBage).commitNow();
     }
 
     @Override
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java
index f99090b7..fb30cfa 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java
@@ -65,8 +65,10 @@
     public void onSubpageRemoved() {
         assert mSubpageFragment != null;
         AppCompatActivity host = (AppCompatActivity) mRowView.getContext();
-        host.getSupportFragmentManager().beginTransaction().remove(mSubpageFragment).commitNow();
+        SingleWebsiteSettings fragment = mSubpageFragment;
         mSubpageFragment = null;
+        if (host.isFinishing()) return;
+        host.getSupportFragmentManager().beginTransaction().remove(fragment).commitNow();
     }
 
     public void setPermissions(PageInfoView.PermissionParams params) {
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 3108abe3..f57a05f 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -594,16 +594,6 @@
 }
 #endif
 
-// This converts `i` to type `Enum`. Terminates in case `i` is outside the valid
-// ranges for `Enum`. Requires `Enum::kMinValue` and `Enum::kMaxValue` to exist
-// and have correct semantics.
-template <typename Enum>
-Enum ToEnumOrDie(int i) {
-  CHECK_LE(static_cast<int>(Enum::kMinValue), i);
-  CHECK_GE(static_cast<int>(Enum::kMaxValue), i);
-  return static_cast<Enum>(i);
-}
-
 }  // namespace
 
 struct LoginDatabase::PrimaryKeyAndPassword {
@@ -1437,9 +1427,11 @@
   form->date_created =
       base::Time::FromInternalValue(s.ColumnInt64(COLUMN_DATE_CREATED));
   form->blocked_by_user = (s.ColumnInt(COLUMN_BLACKLISTED_BY_USER) > 0);
-  form->scheme = ToEnumOrDie<PasswordForm::Scheme>(s.ColumnInt(COLUMN_SCHEME));
+  // TODO(crbug.com/1151214): Add metrics to capture how often these values fall
+  // out of the valid enum range.
+  form->scheme = static_cast<PasswordForm::Scheme>(s.ColumnInt(COLUMN_SCHEME));
   form->type =
-      ToEnumOrDie<PasswordForm::Type>(s.ColumnInt(COLUMN_PASSWORD_TYPE));
+      static_cast<PasswordForm::Type>(s.ColumnInt(COLUMN_PASSWORD_TYPE));
   if (s.ColumnByteLength(COLUMN_POSSIBLE_USERNAME_PAIRS)) {
     base::Pickle pickle(
         static_cast<const char*>(s.ColumnBlob(COLUMN_POSSIBLE_USERNAME_PAIRS)),
@@ -1467,7 +1459,7 @@
       url::Origin::Create(GURL(s.ColumnString(COLUMN_FEDERATION_URL)));
   form->skip_zero_click = (s.ColumnInt(COLUMN_SKIP_ZERO_CLICK) > 0);
   form->generation_upload_status =
-      ToEnumOrDie<PasswordForm::GenerationUploadStatus>(
+      static_cast<PasswordForm::GenerationUploadStatus>(
           s.ColumnInt(COLUMN_GENERATION_UPLOAD_STATUS));
   form->date_last_used = base::Time::FromDeltaSinceWindowsEpoch(
       base::TimeDelta::FromMicroseconds(s.ColumnInt64(COLUMN_DATE_LAST_USED)));
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
index 56d3800..131f87b 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
@@ -9,6 +9,7 @@
 #include <set>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
@@ -238,13 +239,15 @@
   compromised_credentials_reader_.Init();
 }
 
-void InsecureCredentialsManager::StartWeakCheck() {
+void InsecureCredentialsManager::StartWeakCheck(
+    base::OnceClosure on_check_done) {
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
       base::BindOnce(&BulkWeakCheck,
                      ExtractPasswords(presenter_->GetSavedPasswords())),
       base::BindOnce(&InsecureCredentialsManager::OnWeakCheckDone,
-                     weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer()));
+                     weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer())
+          .Then(std::move(on_check_done)));
 }
 
 void InsecureCredentialsManager::SaveCompromisedCredential(
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager.h b/components/password_manager/core/browser/ui/insecure_credentials_manager.h
index 5527477..f1e21e5 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager.h
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager.h
@@ -8,6 +8,8 @@
 #include <map>
 #include <vector>
 
+#include "base/callback_forward.h"
+#include "base/callback_helpers.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/span.h"
 #include "base/memory/scoped_refptr.h"
@@ -164,7 +166,7 @@
 
   // Computes weak credentials in a separate thread and then passes the result
   // to OnWeakCheckDone.
-  void StartWeakCheck();
+  void StartWeakCheck(base::OnceClosure on_check_done = base::DoNothing());
 
   // Marks all saved credentials which have same username & password as
   // compromised.
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
index bcc2bde6..9ef6d5b 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_piece_forward.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
@@ -509,6 +510,13 @@
               ElementsAreArray(store().stored_passwords().at(kExampleOrg)));
 }
 
+TEST_F(InsecureCredentialsManagerTest, StartWeakCheckNotifiesOnCompletion) {
+  base::MockOnceClosure closure;
+  provider().StartWeakCheck(closure.Get());
+  EXPECT_CALL(closure, Run);
+  RunUntilIdle();
+}
+
 TEST_F(InsecureCredentialsManagerTest, StartWeakCheckOnEmptyPasswordsList) {
   EXPECT_THAT(
       histogram_tester().GetTotalCountsForPrefix("PasswordManager.WeakCheck"),
diff --git a/components/password_manager/core/browser/votes_uploader.cc b/components/password_manager/core/browser/votes_uploader.cc
index f6b904d..7eeae75 100644
--- a/components/password_manager/core/browser/votes_uploader.cc
+++ b/components/password_manager/core/browser/votes_uploader.cc
@@ -369,7 +369,7 @@
   }
 
   // Annotate the form with the source language of the page.
-  form_structure.set_page_language(client_->GetPageLanguage());
+  form_structure.set_original_page_language(client_->GetPageLanguage());
 
   // Attach the Randomized Encoder.
   form_structure.set_randomized_encoder(
@@ -423,7 +423,7 @@
   form_structure.set_upload_required(UPLOAD_REQUIRED);
 
   // Annotate the form with the source language of the page.
-  form_structure.set_page_language(client_->GetPageLanguage());
+  form_structure.set_original_page_language(client_->GetPageLanguage());
 
   // Attach the Randomized Encoder.
   form_structure.set_randomized_encoder(
diff --git a/components/permissions/notification_permission_ui_selector.cc b/components/permissions/notification_permission_ui_selector.cc
index b7e0a12..226b7e6 100644
--- a/components/permissions/notification_permission_ui_selector.cc
+++ b/components/permissions/notification_permission_ui_selector.cc
@@ -37,4 +37,9 @@
   return Decision(UseNormalUi(), ShowNoWarning());
 }
 
+base::Optional<PermissionUmaUtil::PredictionGrantLikelihood>
+NotificationPermissionUiSelector::PredictedGrantLikelihoodForUKM() {
+  return base::nullopt;
+}
+
 }  // namespace permissions
diff --git a/components/permissions/notification_permission_ui_selector.h b/components/permissions/notification_permission_ui_selector.h
index 9888bc5..c2dca79f 100644
--- a/components/permissions/notification_permission_ui_selector.h
+++ b/components/permissions/notification_permission_ui_selector.h
@@ -8,6 +8,7 @@
 #include "base/callback_forward.h"
 #include "base/optional.h"
 #include "components/permissions/permission_request.h"
+#include "components/permissions/permission_uma_util.h"
 
 namespace permissions {
 
@@ -79,6 +80,12 @@
   // can be issued. Can be called when there is no pending request which will
   // simply be a no-op.
   virtual void Cancel() {}
+
+  // Will return the selector's discretized prediction value, if any is
+  // applicable to be recorded in UKMs. This is specific only to a selector that
+  // makes use of the Web Permission Predictions Service to make decisions.
+  virtual base::Optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  PredictedGrantLikelihoodForUKM();
 };
 
 }  // namespace permissions
diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc
index 2a52cec..c11b3c2 100644
--- a/components/permissions/permission_request_manager.cc
+++ b/components/permissions/permission_request_manager.cc
@@ -23,7 +23,6 @@
 #include "components/permissions/permission_prompt.h"
 #include "components/permissions/permission_request.h"
 #include "components/permissions/permission_request_id.h"
-#include "components/permissions/permission_uma_util.h"
 #include "components/permissions/permissions_client.h"
 #include "components/permissions/switches.h"
 #include "content/public/browser/back_forward_cache.h"
@@ -598,6 +597,7 @@
     selector->Cancel();
 
   current_request_already_displayed_ = false;
+  prediction_grant_likelihood_.reset();
   current_request_ui_to_use_.reset();
   selector_decisions_.clear();
 
@@ -611,7 +611,9 @@
 
   PermissionUmaUtil::PermissionPromptResolved(
       requests_, web_contents(), permission_action,
-      DetermineCurrentRequestUIDispositionForUMA());
+      DetermineCurrentRequestUIDispositionForUMA(),
+      DetermineCurrentRequestUIDispositionReasonForUMA(),
+      prediction_grant_likelihood_);
 
   content::BrowserContext* browser_context =
       web_contents()->GetBrowserContext();
@@ -793,6 +795,12 @@
     }
   }
 
+  if (!prediction_grant_likelihood_.has_value()) {
+    prediction_grant_likelihood_ =
+        notification_permission_ui_selectors_[selector_index]
+            ->PredictedGrantLikelihoodForUKM();
+  }
+
   // We have already made a decision because of a higher priority selector
   // therefore this selector's decision can be discarded.
   if (current_request_ui_to_use_.has_value())
@@ -831,6 +839,24 @@
   return PermissionPromptDisposition::NONE_VISIBLE;
 }
 
+PermissionPromptDispositionReason
+PermissionRequestManager::DetermineCurrentRequestUIDispositionReasonForUMA() {
+  if (!ShouldCurrentRequestUseQuietUI()) {
+    return PermissionPromptDispositionReason::DEFAULT_FALLBACK;
+  }
+
+  switch (ReasonForUsingQuietUi()) {
+    case QuietUiReason::kEnabledInPrefs:
+      return PermissionPromptDispositionReason::USER_PREFERENCE_IN_SETTINGS;
+    case QuietUiReason::kTriggeredByCrowdDeny:
+    case QuietUiReason::kTriggeredDueToAbusiveRequests:
+    case QuietUiReason::kTriggeredDueToAbusiveContent:
+      return PermissionPromptDispositionReason::SAFE_BROWSING_VERDICT;
+    case QuietUiReason::kPredictedVeryUnlikelyGrant:
+      return PermissionPromptDispositionReason::PREDICTION_SERVICE;
+  }
+}
+
 void PermissionRequestManager::LogWarningToConsole(const char* message) {
   web_contents()->GetMainFrame()->AddMessageToConsole(
       blink::mojom::ConsoleMessageLevel::kWarning, message);
diff --git a/components/permissions/permission_request_manager.h b/components/permissions/permission_request_manager.h
index 3ccdbff..f12a19f 100644
--- a/components/permissions/permission_request_manager.h
+++ b/components/permissions/permission_request_manager.h
@@ -16,6 +16,7 @@
 #include "base/observer_list.h"
 #include "components/permissions/notification_permission_ui_selector.h"
 #include "components/permissions/permission_prompt.h"
+#include "components/permissions/permission_uma_util.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -33,6 +34,7 @@
 class PermissionRequest;
 enum class PermissionAction;
 enum class PermissionPromptDisposition;
+enum class PermissionPromptDispositionReason;
 
 // The message to be printed in the Developer Tools console when the quiet
 // notification permission prompt UI is shown on sites with abusive permission
@@ -235,6 +237,8 @@
                                               const UiDecision& decision);
 
   PermissionPromptDisposition DetermineCurrentRequestUIDispositionForUMA();
+  PermissionPromptDispositionReason
+  DetermineCurrentRequestUIDispositionReasonForUMA();
 
   void LogWarningToConsole(const char* message);
 
@@ -307,6 +311,11 @@
   // still waiting on the result from |notification_permission_ui_selectors_|.
   base::Optional<UiDecision> current_request_ui_to_use_;
 
+  // The likelihood value returned by the Web Permission Predictions Service,
+  // to be recoreded in UKM.
+  base::Optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      prediction_grant_likelihood_;
+
   // Whether the bubble is being destroyed by this class, rather than in
   // response to a UI event. In this case, callbacks from the bubble itself
   // should be ignored.
diff --git a/components/permissions/permission_uma_util.cc b/components/permissions/permission_uma_util.cc
index aef1b9e..393dca1 100644
--- a/components/permissions/permission_uma_util.cc
+++ b/components/permissions/permission_uma_util.cc
@@ -127,8 +127,11 @@
     int ignore_count,
     PermissionSourceUI source_ui,
     PermissionPromptDisposition ui_disposition,
+    base::Optional<PermissionPromptDispositionReason> ui_reason,
     base::Optional<bool> has_three_consecutive_denies,
     base::Optional<bool> has_previously_revoked_permission,
+    base::Optional<PermissionUmaUtil::PredictionGrantLikelihood>
+        predicted_grant_likelihood,
     base::Optional<ukm::SourceId> source_id) {
   // Only record the permission change if the origin is in the history.
   if (!source_id.has_value())
@@ -146,6 +149,14 @@
       .SetSource(static_cast<int64_t>(source_ui))
       .SetPromptDisposition(static_cast<int64_t>(ui_disposition));
 
+  if (ui_reason.has_value())
+    builder.SetPromptDispositionReason(static_cast<int64_t>(ui_reason.value()));
+
+  if (predicted_grant_likelihood.has_value()) {
+    builder.SetPredictionsApiResponse_GrantLikelihood(
+        static_cast<int64_t>(predicted_grant_likelihood.value()));
+  }
+
   if (has_three_consecutive_denies.has_value()) {
     int64_t satisfied_adaptive_triggers = 0;
     if (has_three_consecutive_denies.value())
@@ -284,8 +295,9 @@
     RecordPermissionAction(permission, PermissionAction::REVOKED, source_ui,
                            PermissionRequestGestureType::UNKNOWN,
                            PermissionPromptDisposition::NOT_APPLICABLE,
-                           revoked_origin,
-                           /*web_contents=*/nullptr, browser_context);
+                           base::nullopt /* ui_reason */, revoked_origin,
+                           nullptr /* web_contents */, browser_context,
+                           base::nullopt /* predicted_grant_likelihood */);
   }
 }
 
@@ -349,7 +361,9 @@
     const std::vector<PermissionRequest*>& requests,
     content::WebContents* web_contents,
     PermissionAction permission_action,
-    PermissionPromptDisposition ui_disposition) {
+    PermissionPromptDisposition ui_disposition,
+    base::Optional<PermissionPromptDispositionReason> ui_reason,
+    base::Optional<PredictionGrantLikelihood> predicted_grant_likelihood) {
   std::string action_string;
 
   switch (permission_action) {
@@ -388,10 +402,10 @@
     PermissionRequestGestureType gesture_type = request->GetGestureType();
     const GURL& requesting_origin = request->GetOrigin();
 
-    RecordPermissionAction(permission, permission_action,
-                           PermissionSourceUI::PROMPT, gesture_type,
-                           ui_disposition, requesting_origin, web_contents,
-                           web_contents->GetBrowserContext());
+    RecordPermissionAction(
+        permission, permission_action, PermissionSourceUI::PROMPT, gesture_type,
+        ui_disposition, ui_reason, requesting_origin, web_contents,
+        web_contents->GetBrowserContext(), predicted_grant_likelihood);
 
     std::string priorDismissPrefix =
         "Permissions.Prompt." + action_string + ".PriorDismissCount2.";
@@ -552,9 +566,11 @@
     PermissionSourceUI source_ui,
     PermissionRequestGestureType gesture_type,
     PermissionPromptDisposition ui_disposition,
+    base::Optional<PermissionPromptDispositionReason> ui_reason,
     const GURL& requesting_origin,
     const content::WebContents* web_contents,
-    content::BrowserContext* browser_context) {
+    content::BrowserContext* browser_context,
+    base::Optional<PredictionGrantLikelihood> predicted_grant_likelihood) {
   PermissionDecisionAutoBlocker* autoblocker =
       PermissionsClient::Get()->GetPermissionDecisionAutoBlocker(
           browser_context);
@@ -566,14 +582,15 @@
       browser_context, web_contents, requesting_origin,
       base::BindOnce(
           &RecordPermissionActionUkm, action, gesture_type, permission,
-          dismiss_count, ignore_count, source_ui, ui_disposition,
+          dismiss_count, ignore_count, source_ui, ui_disposition, ui_reason,
           permission == ContentSettingsType::NOTIFICATIONS
               ? PermissionsClient::Get()
                     ->HadThreeConsecutiveNotificationPermissionDenies(
                         browser_context)
               : base::nullopt,
           PermissionsClient::Get()->HasPreviouslyAutoRevokedPermission(
-              browser_context, requesting_origin, permission)));
+              browser_context, requesting_origin, permission),
+          predicted_grant_likelihood));
 
   switch (permission) {
     case ContentSettingsType::GEOLOCATION:
diff --git a/components/permissions/permission_uma_util.h b/components/permissions/permission_uma_util.h
index 4bf397f..21833073 100644
--- a/components/permissions/permission_uma_util.h
+++ b/components/permissions/permission_uma_util.h
@@ -13,6 +13,7 @@
 #include "components/permissions/permission_request.h"
 #include "components/permissions/permission_result.h"
 #include "components/permissions/permission_util.h"
+#include "components/permissions/prediction_service/prediction_service_messages.pb.h"
 
 namespace content {
 class BrowserContext;
@@ -106,6 +107,25 @@
   NONE_VISIBLE = 7,
 };
 
+// The reason why the permission prompt disposition was used. Enum used in UKMs,
+// do not re-order or change values. Deprecated items should only be commented
+// out.
+enum class PermissionPromptDispositionReason {
+  // Disposition was selected in prefs.
+  USER_PREFERENCE_IN_SETTINGS = 0,
+
+  // Disposition was chosen because Safe Browsing classifies the origin
+  // as being spammy or abusive with permission requests.
+  SAFE_BROWSING_VERDICT = 1,
+
+  // Disposition was chosen based on grant likelihood predicted by the
+  // Web Permission Prediction Service.
+  PREDICTION_SERVICE = 2,
+
+  // Disposition was used as a fallback, if no selector made a decision.
+  DEFAULT_FALLBACK = 3,
+};
+
 enum class AdaptiveTriggers {
   // None of the adaptive triggers were met. Currently this means two or less
   // consecutive denies in a row.
@@ -126,6 +146,9 @@
 // Provides a convenient way of logging UMA for permission related operations.
 class PermissionUmaUtil {
  public:
+  using PredictionGrantLikelihood =
+      PermissionSuggestion_Likelihood_DiscretizedLikelihood;
+
   static const char kPermissionsPromptShown[];
   static const char kPermissionsPromptShownGesture[];
   static const char kPermissionsPromptShownNoGesture[];
@@ -167,7 +190,9 @@
       const std::vector<PermissionRequest*>& requests,
       content::WebContents* web_contents,
       PermissionAction permission_action,
-      PermissionPromptDisposition ui_disposition);
+      PermissionPromptDisposition ui_disposition,
+      base::Optional<PermissionPromptDispositionReason> ui_reason,
+      base::Optional<PredictionGrantLikelihood> predicted_grant_likelihood);
 
   static void RecordWithBatteryBucket(const std::string& histogram);
 
@@ -225,14 +250,17 @@
   friend class PermissionUmaUtilTest;
 
   // web_contents may be null when for recording non-prompt actions.
-  static void RecordPermissionAction(ContentSettingsType permission,
-                                     PermissionAction action,
-                                     PermissionSourceUI source_ui,
-                                     PermissionRequestGestureType gesture_type,
-                                     PermissionPromptDisposition ui_disposition,
-                                     const GURL& requesting_origin,
-                                     const content::WebContents* web_contents,
-                                     content::BrowserContext* browser_context);
+  static void RecordPermissionAction(
+      ContentSettingsType permission,
+      PermissionAction action,
+      PermissionSourceUI source_ui,
+      PermissionRequestGestureType gesture_type,
+      PermissionPromptDisposition ui_disposition,
+      base::Optional<PermissionPromptDispositionReason> ui_reason,
+      const GURL& requesting_origin,
+      const content::WebContents* web_contents,
+      content::BrowserContext* browser_context,
+      base::Optional<PredictionGrantLikelihood> predicted_grant_likelihood);
 
   // Records |count| total prior actions for a prompt of type |permission|
   // for a single origin using |prefix| for the metric.
diff --git a/components/permissions/prediction_service/BUILD.gn b/components/permissions/prediction_service/BUILD.gn
index 18d731d6..96b42b9 100644
--- a/components/permissions/prediction_service/BUILD.gn
+++ b/components/permissions/prediction_service/BUILD.gn
@@ -18,12 +18,12 @@
     "prediction_service_common.h",
   ]
   deps = [
-    ":prediction_service_messages_proto",
     "//components/keyed_service/content",
     "//components/permissions:permissions_common",
     "//services/network/public/cpp:cpp",
     "//third_party/protobuf:protobuf_lite",
   ]
+  public_deps = [ ":prediction_service_messages_proto" ]
 }
 
 source_set("unit_tests") {
@@ -31,7 +31,6 @@
   sources = [ "prediction_service_unittest.cc" ]
   deps = [
     ":prediction_service",
-    ":prediction_service_messages_proto",
     "//base/test:test_support",
     "//components/permissions:permissions_common",
     "//services/network:test_support",
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 2e488fc6..206536aa 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -1007,11 +1007,6 @@
     const std::string error_msg =
         "ProcessSyncChanges failed on ChangeType " +
         syncer::SyncChange::ChangeTypeToString(iter->change_type());
-    if (iter->change_type() == syncer::SyncChange::ACTION_INVALID) {
-      error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
-      continue;
-    }
-
     if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
       if (!existing_turl) {
         // Can't DELETE a non-existent engine, although we log it.
@@ -1231,7 +1226,6 @@
     const base::Location& from_here,
     const TemplateURL* turl,
     syncer::SyncChange::SyncChangeType type) {
-  DCHECK_NE(type, syncer::SyncChange::ACTION_INVALID);
   DCHECK(turl);
 
   if (!models_associated_)
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java
index a8a81015..617e077 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java
@@ -72,8 +72,21 @@
         /**
          * Notifies that an account's profile data has been updated.
          * @param accountEmail An account email.
+         *
+         * This method will be removed after migrating all the callers to the second method
          */
+        @Deprecated
         void onProfileDataUpdated(String accountEmail);
+
+        /**
+         * Notifies that an account's profile data has been updated.
+         */
+        void onProfileDataUpdated(ProfileData profileData);
+
+        /**
+         * Removes the profile data of a given accountEmail.
+         */
+        void removeProfileData(String accountEmail);
     }
 
     /**
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 7bba5178..fb8dc3637 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -476,6 +476,7 @@
     "nigori/nigori_unittest.cc",
     "protocol/proto_enum_conversions_unittest.cc",
     "protocol/proto_value_conversions_unittest.cc",
+    "trusted_vault/download_keys_response_handler_unittest.cc",
     "trusted_vault/securebox_unittest.cc",
     "trusted_vault/standalone_trusted_vault_backend_unittest.cc",
     "trusted_vault/trusted_vault_access_token_fetcher_frontend_unittest.cc",
diff --git a/components/sync/model/sync_change.cc b/components/sync/model/sync_change.cc
index 8b56fbe..c618a23 100644
--- a/components/sync/model/sync_change.cc
+++ b/components/sync/model/sync_change.cc
@@ -10,8 +10,6 @@
 
 namespace syncer {
 
-SyncChange::SyncChange() : change_type_(ACTION_INVALID) {}
-
 SyncChange::SyncChange(const base::Location& from_here,
                        SyncChangeType change_type,
                        const SyncData& sync_data)
@@ -22,7 +20,9 @@
 SyncChange::~SyncChange() {}
 
 bool SyncChange::IsValid() const {
-  if (change_type_ == ACTION_INVALID || !sync_data_.IsValid())
+  // TODO(crbug.com/1152824): This implementation could be simplified if the
+  // public API provides guarantees around when it returns false.
+  if (!sync_data_.IsValid())
     return false;
 
   // Data from the syncer must always have valid specifics.
@@ -57,8 +57,6 @@
 // static
 std::string SyncChange::ChangeTypeToString(SyncChangeType change_type) {
   switch (change_type) {
-    case ACTION_INVALID:
-      return "ACTION_INVALID";
     case ACTION_ADD:
       return "ACTION_ADD";
     case ACTION_UPDATE:
diff --git a/components/sync/model/sync_change.h b/components/sync/model/sync_change.h
index 93a4798..8b786c1 100644
--- a/components/sync/model/sync_change.h
+++ b/components/sync/model/sync_change.h
@@ -22,28 +22,28 @@
 class SyncChange {
  public:
   enum SyncChangeType {
-    ACTION_INVALID,
     ACTION_ADD,
     ACTION_UPDATE,
     ACTION_DELETE,
   };
 
-  // Default constructor creates an invalid change.
-  SyncChange();
   // Create a new change with the specified sync data.
   SyncChange(const base::Location& from_here,
              SyncChangeType change_type,
              const SyncData& sync_data);
+  // Copy constructor and assignment operator welcome.
+  SyncChange(const SyncChange&) = default;
+  SyncChange& operator=(const SyncChange&) = default;
+  // Move constructor and assignment operator allowed (although questionable).
+  // TODO(crbug.com/1152824): Avoid move semantics if that leads invalid state.
+  SyncChange(SyncChange&&) = default;
+  SyncChange& operator=(SyncChange&&) = default;
   ~SyncChange();
 
-  // Copy constructor and assignment operator welcome.
-
   // Whether this change is valid. This must be true before attempting to access
-  // the data.
-  // Deletes: Requires valid tag when going to the syncer. Requires valid
-  //          specifics when coming from the syncer.
-  // Adds, Updates: Require valid tag and specifics when going to the syncer.
-  //                Require only valid specifics when coming from the syncer.
+  // the data. It may only return false for moved-away instances (unspecified
+  // behavior). Otherwise it's guaranteed to return true.
+  // TODO(crbug.com/1152824): Remove this API once move semantics are removed.
   bool IsValid() const;
 
   // Getters.
diff --git a/components/sync/model_impl/syncable_service_based_bridge.cc b/components/sync/model_impl/syncable_service_based_bridge.cc
index 5852bbb..2895911 100644
--- a/components/sync/model_impl/syncable_service_based_bridge.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge.cc
@@ -70,7 +70,7 @@
       return SyncChange::ACTION_UPDATE;
   }
   NOTREACHED();
-  return SyncChange::ACTION_INVALID;
+  return SyncChange::ACTION_UPDATE;
 }
 
 // Parses the content of |record_list| into |*in_memory_store|. The output
@@ -133,10 +133,6 @@
 
     for (const SyncChange& change : change_list) {
       switch (change.change_type()) {
-        case SyncChange::ACTION_INVALID:
-          NOTREACHED() << " from " << change.location().ToString();
-          break;
-
         case SyncChange::ACTION_ADD:
         case SyncChange::ACTION_UPDATE: {
           DCHECK_EQ(type_, change.sync_data().GetDataType());
diff --git a/components/sync/protocol/autofill_specifics.proto b/components/sync/protocol/autofill_specifics.proto
index 671413b3..d8dd598 100644
--- a/components/sync/protocol/autofill_specifics.proto
+++ b/components/sync/protocol/autofill_specifics.proto
@@ -40,6 +40,8 @@
     // This is currently only applicable to the full name, since users cannot
     // edit individual components of their name.
     USER_VERIFIED = 4;
+    // The token was parsed remotely.
+    SERVER_PARSED = 5;
   }
 
   optional string guid = 15;
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index 853885b..a2f6ee73 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -49,7 +49,7 @@
 const char* ProtoEnumToString(
     sync_pb::AutofillProfileSpecifics::VerificationStatus status) {
   ASSERT_ENUM_BOUNDS(sync_pb::AutofillProfileSpecifics, VerificationStatus,
-                     VERIFICATION_STATUS_UNSPECIFIED, USER_VERIFIED);
+                     VERIFICATION_STATUS_UNSPECIFIED, SERVER_PARSED);
   switch (status) {
     ENUM_CASE(sync_pb::AutofillProfileSpecifics,
               VERIFICATION_STATUS_UNSPECIFIED);
@@ -57,6 +57,7 @@
     ENUM_CASE(sync_pb::AutofillProfileSpecifics, FORMATTED);
     ENUM_CASE(sync_pb::AutofillProfileSpecifics, OBSERVED);
     ENUM_CASE(sync_pb::AutofillProfileSpecifics, USER_VERIFIED);
+    ENUM_CASE(sync_pb::AutofillProfileSpecifics, SERVER_PARSED);
   }
   NOTREACHED();
   return "";
diff --git a/components/sync/protocol/vault.proto b/components/sync/protocol/vault.proto
index ebc51e1..55a3835 100644
--- a/components/sync/protocol/vault.proto
+++ b/components/sync/protocol/vault.proto
@@ -13,6 +13,7 @@
   optional int32 epoch = 1;
   optional bytes wrapped_key = 2;
   optional bytes member_proof = 3;
+  optional bytes key_proof = 4;
 }
 
 message SecurityDomain {
@@ -29,3 +30,7 @@
 message JoinSecurityDomainsRequest {
   repeated SecurityDomain security_domains = 1;
 }
+
+message ListSecurityDomainsResponse {
+  repeated SecurityDomain security_domains = 1;
+}
diff --git a/components/sync/trusted_vault/BUILD.gn b/components/sync/trusted_vault/BUILD.gn
index 8494610..fb4341b 100644
--- a/components/sync/trusted_vault/BUILD.gn
+++ b/components/sync/trusted_vault/BUILD.gn
@@ -4,6 +4,8 @@
 
 static_library("trusted_vault") {
   sources = [
+    "download_keys_response_handler.cc",
+    "download_keys_response_handler.h",
     "securebox.cc",
     "securebox.h",
     "standalone_trusted_vault_backend.cc",
diff --git a/components/sync/trusted_vault/download_keys_response_handler.cc b/components/sync/trusted_vault/download_keys_response_handler.cc
new file mode 100644
index 0000000..b733635
--- /dev/null
+++ b/components/sync/trusted_vault/download_keys_response_handler.cc
@@ -0,0 +1,220 @@
+// Copyright 2020 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 "components/sync/trusted_vault/download_keys_response_handler.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "components/sync/protocol/vault.pb.h"
+#include "components/sync/trusted_vault/securebox.h"
+#include "crypto/hmac.h"
+
+namespace syncer {
+
+namespace {
+
+// TODO(crbug.com/1113598): move these constants to a dedicated header, since
+// they are used in multiple places already.
+const size_t kKeyProofLength = 32;
+const uint8_t kWrappedKeyHeader[] = {'V', '1', ' ', 's', 'h', 'a', 'r',
+                                     'e', 'd', '_', 'k', 'e', 'y'};
+const char kSecurityDomainName[] = "chromesync";
+
+struct ExtractedSharedKey {
+  int version;
+  std::vector<uint8_t> trusted_vault_key;
+  std::vector<uint8_t> key_proof;
+};
+
+// Returns pointer to sync security domain in |response|. Returns nullptr if
+// there is no sync security domain.
+const sync_pb::SecurityDomain* FindSyncSecurityDomain(
+    const sync_pb::ListSecurityDomainsResponse& response) {
+  for (const sync_pb::SecurityDomain& security_domain :
+       response.security_domains()) {
+    if (security_domain.name() == kSecurityDomainName) {
+      return &security_domain;
+    }
+  }
+  return nullptr;
+}
+
+// Returns pointer to the member in |response| corresponding to
+// |member_public_key|. Returns nullptr if sync security domain doesn't exist
+// in |response| or there is no such member in sync security domain.
+const sync_pb::SecurityDomain::Member* FindMember(
+    const sync_pb::ListSecurityDomainsResponse& response,
+    const std::vector<uint8_t>& member_public_key_bytes) {
+  const sync_pb::SecurityDomain* sync_security_domain =
+      FindSyncSecurityDomain(response);
+  if (!sync_security_domain) {
+    return nullptr;
+  }
+
+  const std::string member_public_key_string(member_public_key_bytes.begin(),
+                                             member_public_key_bytes.end());
+  for (const sync_pb::SecurityDomain::Member& member :
+       sync_security_domain->members()) {
+    if (member.public_key() == member_public_key_string) {
+      return &member;
+    }
+  }
+  return nullptr;
+}
+
+// Extracts (decrypts |wrapped_key| and converts to ExtractedSharedKey) shared
+// keys from |member| and sorts them by version.
+std::vector<ExtractedSharedKey> ExtractAndSortSharedKeys(
+    const sync_pb::SecurityDomain::Member& member,
+    const SecureBoxPrivateKey& member_private_key) {
+  std::vector<ExtractedSharedKey> result;
+  for (const sync_pb::SharedKey& shared_key : member.keys()) {
+    std::vector<uint8_t> wrapped_key(shared_key.wrapped_key().begin(),
+                                     shared_key.wrapped_key().end());
+    base::Optional<std::vector<uint8_t>> decrypted_key =
+        member_private_key.Decrypt(base::span<const uint8_t>(),
+                                   kWrappedKeyHeader, wrapped_key);
+    if (!decrypted_key.has_value()) {
+      // Decryption failed.
+      return std::vector<ExtractedSharedKey>();
+    }
+    result.push_back(
+        ExtractedSharedKey{/*version=*/shared_key.epoch(), *decrypted_key,
+                           /*key_proof=*/
+                           std::vector<uint8_t>(shared_key.key_proof().begin(),
+                                                shared_key.key_proof().end())});
+  }
+
+  std::sort(result.begin(), result.end(),
+            [](const ExtractedSharedKey& a, const ExtractedSharedKey& b) {
+              return a.version < b.version;
+            });
+  return result;
+}
+
+// Validates |key_proof| starting from the key next to
+// |last_known_trusted_vault_key|, returns false if validation fails or |keys|
+// doesn't have a key next to |last_known_trusted_vault_key|.
+bool IsValidKeyChain(const std::vector<ExtractedSharedKey>& key_chain,
+                     const std::vector<uint8_t>& last_known_trusted_vault_key,
+                     const int last_known_trusted_vault_key_version) {
+  DCHECK(!key_chain.empty());
+  if (key_chain.back().version <= last_known_trusted_vault_key_version) {
+    // |keys| doesn't contain any new key. Note: this may mean that key rotation
+    // happened, but state corresponding to the current member wasn't updated.
+    return false;
+  }
+  int last_valid_key_version = last_known_trusted_vault_key_version;
+  std::vector<uint8_t> last_valid_key = last_known_trusted_vault_key;
+  for (const ExtractedSharedKey& next_key : key_chain) {
+    if (next_key.version <= last_valid_key_version) {
+      continue;
+    }
+    if (next_key.version != last_valid_key_version + 1) {
+      // Missing intermediate key.
+      return false;
+    }
+
+    crypto::HMAC hmac(crypto::HMAC::SHA256);
+    CHECK(hmac.Init(last_valid_key));
+
+    std::vector<uint8_t> digest_bytes(kKeyProofLength);
+    if (!hmac.Verify(next_key.trusted_vault_key, next_key.key_proof)) {
+      // |key_proof| isn't valid.
+      return false;
+    }
+    last_valid_key_version = next_key.version;
+    last_valid_key = next_key.trusted_vault_key;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+DownloadKeysResponseHandler::ProcessedResponse::ProcessedResponse(
+    TrustedVaultRequestStatus status)
+    : status(status), last_key_version(0) {}
+
+DownloadKeysResponseHandler::ProcessedResponse::ProcessedResponse(
+    TrustedVaultRequestStatus status,
+    std::vector<std::vector<uint8_t>> keys,
+    int last_key_version)
+    : status(status), keys(keys), last_key_version(last_key_version) {}
+
+DownloadKeysResponseHandler::ProcessedResponse::ProcessedResponse(
+    const ProcessedResponse& other) = default;
+
+DownloadKeysResponseHandler::ProcessedResponse&
+DownloadKeysResponseHandler::ProcessedResponse::operator=(
+    const ProcessedResponse& other) = default;
+
+DownloadKeysResponseHandler::ProcessedResponse::~ProcessedResponse() = default;
+
+DownloadKeysResponseHandler::DownloadKeysResponseHandler(
+    const std::vector<uint8_t>& last_trusted_vault_key,
+    int last_trusted_vault_key_version,
+    std::unique_ptr<SecureBoxKeyPair> device_key_pair)
+    : last_trusted_vault_key_(last_trusted_vault_key),
+      last_trusted_vault_key_version_(last_trusted_vault_key_version),
+      device_key_pair_(std::move(device_key_pair)) {
+  DCHECK(device_key_pair_);
+}
+
+DownloadKeysResponseHandler::~DownloadKeysResponseHandler() = default;
+
+DownloadKeysResponseHandler::ProcessedResponse
+DownloadKeysResponseHandler::ProcessResponse(
+    TrustedVaultRequest::HttpStatus http_status,
+    const std::string& response_body) const {
+  switch (http_status) {
+    case TrustedVaultRequest::HttpStatus::kSuccess:
+      break;
+    case TrustedVaultRequest::HttpStatus::kOtherError:
+    case TrustedVaultRequest::HttpStatus::kBadRequest:
+      // Don't distinguish kBadRequest here, because request content doesn't
+      // depend on the local state.
+      return ProcessedResponse(
+          /*status=*/TrustedVaultRequestStatus::kOtherError);
+  }
+
+  sync_pb::ListSecurityDomainsResponse deserialized_response;
+  if (!deserialized_response.ParseFromString(response_body)) {
+    return ProcessedResponse(/*status=*/TrustedVaultRequestStatus::kOtherError);
+  }
+
+  const sync_pb::SecurityDomain::Member* current_member = FindMember(
+      deserialized_response, device_key_pair_->public_key().ExportToBytes());
+  if (!current_member) {
+    // |device_key_pair_| isn't registered server-side, while client assumes
+    // it's registered when downloading keys.
+    return ProcessedResponse(
+        /*status=*/TrustedVaultRequestStatus::kLocalDataObsolete);
+  }
+
+  std::vector<ExtractedSharedKey> extracted_keys = ExtractAndSortSharedKeys(
+      *current_member, device_key_pair_->private_key());
+  if (extracted_keys.empty() ||
+      !IsValidKeyChain(extracted_keys, last_trusted_vault_key_,
+                       last_trusted_vault_key_version_)) {
+    return ProcessedResponse(
+        /*status=*/TrustedVaultRequestStatus::kLocalDataObsolete);
+  }
+
+  std::vector<std::vector<uint8_t>> new_keys;
+  for (const ExtractedSharedKey& key : extracted_keys) {
+    if (key.version >= last_trusted_vault_key_version_) {
+      // Don't include previous keys into the result, because they weren't
+      // validated using |last_trusted_vault_key_| and client should be already
+      // aware of them.
+      new_keys.push_back(key.trusted_vault_key);
+    }
+  }
+  return ProcessedResponse(/*status=*/TrustedVaultRequestStatus::kSuccess,
+                           new_keys,
+                           /*last_key_version=*/extracted_keys.back().version);
+}
+
+}  // namespace syncer
diff --git a/components/sync/trusted_vault/download_keys_response_handler.h b/components/sync/trusted_vault/download_keys_response_handler.h
new file mode 100644
index 0000000..f8fe26b9
--- /dev/null
+++ b/components/sync/trusted_vault/download_keys_response_handler.h
@@ -0,0 +1,73 @@
+// Copyright 2020 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 COMPONENTS_SYNC_TRUSTED_VAULT_DOWNLOAD_KEYS_RESPONSE_HANDLER_H_
+#define COMPONENTS_SYNC_TRUSTED_VAULT_DOWNLOAD_KEYS_RESPONSE_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/sync/trusted_vault/trusted_vault_connection.h"
+#include "components/sync/trusted_vault/trusted_vault_request.h"
+
+namespace syncer {
+
+class SecureBoxKeyPair;
+
+// Helper class to extract and validate trusted vault keys from
+// ListSecurityDomainsResponse.
+class DownloadKeysResponseHandler {
+ public:
+  struct ProcessedResponse {
+    explicit ProcessedResponse(TrustedVaultRequestStatus status);
+    ProcessedResponse(TrustedVaultRequestStatus status,
+                      std::vector<std::vector<uint8_t>> keys,
+                      int last_key_version);
+    ProcessedResponse(const ProcessedResponse& other);
+    ProcessedResponse& operator=(const ProcessedResponse& other);
+    ~ProcessedResponse();
+
+    // kSuccess is reported if extraction was successful and there are new
+    // trusted vault keys.
+    // kLocalDataObsolete is reported if it's impossible to extract keys due to
+    // data corruption or absence of SecurityDomain/Member or if there is no new
+    // keys.
+    // kOtherError is reported in case of http/network errors or if the response
+    // isn't valid serialized ListSecurityDomainsResponse proto.
+    TrustedVaultRequestStatus status;
+
+    // Contains new keys and potentially |last_trusted_vault_key| if it wasn't
+    // removed server-side. Doesn't contain keys that predate
+    // |last_trusted_vault_key|, because it's impossible to validate them and
+    // the client should be aware of them already.
+    std::vector<std::vector<uint8_t>> keys;
+    int last_key_version;
+  };
+
+  // |device_key_pair| must not be null. This class doesn't make an assumption
+  // of |last_trusted_vault_key| being non-empty or
+  // |last_trusted_vault_key_version| being non-negative.
+  DownloadKeysResponseHandler(
+      const std::vector<uint8_t>& last_trusted_vault_key,
+      int last_trusted_vault_key_version,
+      std::unique_ptr<SecureBoxKeyPair> device_key_pair);
+  DownloadKeysResponseHandler(const DownloadKeysResponseHandler& other) =
+      delete;
+  DownloadKeysResponseHandler& operator=(
+      const DownloadKeysResponseHandler& other) = delete;
+  ~DownloadKeysResponseHandler();
+
+  ProcessedResponse ProcessResponse(TrustedVaultRequest::HttpStatus http_status,
+                                    const std::string& response_body) const;
+
+ private:
+  const std::vector<uint8_t> last_trusted_vault_key_;
+  const int last_trusted_vault_key_version_;
+  const std::unique_ptr<SecureBoxKeyPair> device_key_pair_;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_TRUSTED_VAULT_DOWNLOAD_KEYS_RESPONSE_HANDLER_H_
diff --git a/components/sync/trusted_vault/download_keys_response_handler_unittest.cc b/components/sync/trusted_vault/download_keys_response_handler_unittest.cc
new file mode 100644
index 0000000..608fc17
--- /dev/null
+++ b/components/sync/trusted_vault/download_keys_response_handler_unittest.cc
@@ -0,0 +1,492 @@
+// Copyright 2020 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 "components/sync/trusted_vault/download_keys_response_handler.h"
+
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "components/sync/protocol/vault.pb.h"
+#include "components/sync/trusted_vault/securebox.h"
+#include "crypto/hmac.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::IsEmpty;
+
+const size_t kKeyProofLength = 32;
+const char kEncodedPrivateKey[] =
+    "49e052293c29b5a50b0013eec9d030ac2ad70a42fe093be084264647cb04e16f";
+const uint8_t kWrappedKeyHeader[] = {'V', '1', ' ', 's', 'h', 'a', 'r',
+                                     'e', 'd', '_', 'k', 'e', 'y'};
+const char kSecurityDomainName[] = "chromesync";
+
+void AssignBytesToProtoString(base::span<const uint8_t> bytes,
+                              std::string* bytes_proto_field) {
+  *bytes_proto_field = std::string(bytes.begin(), bytes.end());
+}
+
+std::unique_ptr<SecureBoxKeyPair> MakeTestKeyPair() {
+  std::vector<uint8_t> private_key_bytes;
+  bool success = base::HexStringToBytes(kEncodedPrivateKey, &private_key_bytes);
+  DCHECK(success);
+  return SecureBoxKeyPair::CreateByPrivateKeyImport(private_key_bytes);
+}
+
+void FillSecurityDomainMember(
+    const SecureBoxPublicKey& public_key,
+    const std::vector<std::vector<uint8_t>>& trusted_vault_keys,
+    const std::vector<int> trusted_vault_keys_versions,
+    const std::vector<std::vector<uint8_t>>& signing_keys,
+    sync_pb::SecurityDomain::Member* member) {
+  DCHECK(member);
+  DCHECK_EQ(trusted_vault_keys.size(), trusted_vault_keys_versions.size());
+  DCHECK_EQ(trusted_vault_keys.size(), signing_keys.size());
+
+  std::vector<uint8_t> public_key_bytes = public_key.ExportToBytes();
+  AssignBytesToProtoString(public_key.ExportToBytes(),
+                           member->mutable_public_key());
+
+  for (size_t i = 0; i < trusted_vault_keys.size(); ++i) {
+    sync_pb::SharedKey* shared_key = member->add_keys();
+    shared_key->set_epoch(trusted_vault_keys_versions[i]);
+    AssignBytesToProtoString(
+        public_key.Encrypt(
+            /*shared_secret=*/base::span<const uint8_t>(), kWrappedKeyHeader,
+            /*payload=*/trusted_vault_keys[i]),
+        shared_key->mutable_wrapped_key());
+
+    if (!signing_keys[i].empty()) {
+      crypto::HMAC hmac(crypto::HMAC::SHA256);
+      CHECK(hmac.Init(signing_keys[i]));
+
+      std::vector<uint8_t> key_proof_bytes(kKeyProofLength);
+      CHECK(hmac.Sign(trusted_vault_keys[i], key_proof_bytes));
+      AssignBytesToProtoString(key_proof_bytes,
+                               shared_key->mutable_key_proof());
+    }
+  }
+}
+
+std::string CreateListSecurityDomainsResponseWithSingleSyncMember(
+    const std::vector<std::vector<uint8_t>>& trusted_vault_keys,
+    const std::vector<int> trusted_vault_keys_versions,
+    const std::vector<std::vector<uint8_t>>& signing_keys) {
+  sync_pb::ListSecurityDomainsResponse response;
+  sync_pb::SecurityDomain* security_domain = response.add_security_domains();
+  security_domain->set_name(kSecurityDomainName);
+  FillSecurityDomainMember(MakeTestKeyPair()->public_key(), trusted_vault_keys,
+                           trusted_vault_keys_versions, signing_keys,
+                           security_domain->add_members());
+  return response.SerializeAsString();
+}
+
+class DownloadKeysResponseHandlerTest : public testing::Test {
+ public:
+  DownloadKeysResponseHandlerTest()
+      : handler_(kKnownTrustedVaultKey,
+                 kKnownTrustedVaultKeyVersion,
+                 MakeTestKeyPair()) {}
+
+  ~DownloadKeysResponseHandlerTest() override = default;
+
+  const DownloadKeysResponseHandler& handler() const { return handler_; }
+
+  const int kKnownTrustedVaultKeyVersion = 5;
+  const std::vector<uint8_t> kKnownTrustedVaultKey = {1, 2, 3, 4};
+  const std::vector<uint8_t> kTrustedVaultKey1 = {1, 2, 3, 5};
+  const std::vector<uint8_t> kTrustedVaultKey2 = {1, 2, 3, 6};
+  const std::vector<uint8_t> kTrustedVaultKey3 = {1, 2, 3, 7};
+
+ private:
+  const DownloadKeysResponseHandler handler_;
+};
+
+// All HttpStatuses except kSuccess should end up in kOtherError reporting,
+// because underlying request doesn't have any parameters inferred from local
+// state.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleHttpErrors) {
+  EXPECT_THAT(
+      handler()
+          .ProcessResponse(
+              /*http_status=*/TrustedVaultRequest::HttpStatus::kBadRequest,
+              /*response_body=*/std::string())
+          .status,
+      Eq(TrustedVaultRequestStatus::kOtherError));
+  EXPECT_THAT(
+      handler()
+          .ProcessResponse(
+              /*http_status=*/TrustedVaultRequest::HttpStatus::kOtherError,
+              /*response_body=*/std::string())
+          .status,
+      Eq(TrustedVaultRequestStatus::kOtherError));
+}
+
+// Simplest legitimate case of key rotation, server side state corresponds to
+// kKnownTrustedVaultKey -> kTrustedVaultKey1 key chain.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleSingleKeyRotation) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+              /*signing_keys=*/{{}, kKnownTrustedVaultKey}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kSuccess));
+  EXPECT_THAT(processed_response.keys,
+              ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey1));
+  EXPECT_THAT(processed_response.last_key_version,
+              Eq(kKnownTrustedVaultKeyVersion + 1));
+}
+
+// Multiple key rotations may happen while client is offline, server-side key
+// chain is kKnownTrustedVaultKey -> kTrustedVaultKey1 -> kTrustedVaultKey2.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleMultipleKeyRotations) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/
+              {kKnownTrustedVaultKey, kTrustedVaultKey1, kTrustedVaultKey2},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1,
+               kKnownTrustedVaultKeyVersion + 2},
+              /*signing_keys=*/{{}, kKnownTrustedVaultKey, kTrustedVaultKey1}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kSuccess));
+  EXPECT_THAT(
+      processed_response.keys,
+      ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey1, kTrustedVaultKey2));
+  EXPECT_THAT(processed_response.last_key_version,
+              Eq(kKnownTrustedVaultKeyVersion + 2));
+}
+
+// There might be keys, that predates latest client-side trusted vault key.
+// Server-side key chain is kTrustedVaultKey1 -> kKnownTrustedVaultKey ->
+// kTrustedVaultKey2 -> kTrustedVaultKey3.
+// Since kTrustedVaultKey1 can't be validated using kKnownTrustedVaultKey it
+// shouldn't be included in processed response.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandlePriorKeys) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/
+              {kTrustedVaultKey1, kKnownTrustedVaultKey, kTrustedVaultKey2,
+               kTrustedVaultKey3},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion - 1, kKnownTrustedVaultKeyVersion,
+               kKnownTrustedVaultKeyVersion + 1,
+               kKnownTrustedVaultKeyVersion + 2},
+              /*signing_keys=*/
+              {{},
+               kTrustedVaultKey1,
+               kKnownTrustedVaultKey,
+               kTrustedVaultKey2}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kSuccess));
+  EXPECT_THAT(
+      processed_response.keys,
+      ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey2, kTrustedVaultKey3));
+  EXPECT_THAT(processed_response.last_key_version,
+              Eq(kKnownTrustedVaultKeyVersion + 2));
+}
+
+// Server can already clean-up kKnownTrustedVaultKey, but it might still be
+// possible to validate the key-chain.
+// Full key chain is: kKnownTrustedVaultKey -> kTrustedVaultKey1 ->
+// kTrustedVaultKey2.
+// Server-side key chain is: kTrustedVaultKey1 -> kTrustedVaultKey2.
+TEST_F(DownloadKeysResponseHandlerTest,
+       ShouldHandleAbsenseOfKnownKeyWhenKeyChainIsRecoverable) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/
+              {kTrustedVaultKey1, kTrustedVaultKey2},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion + 1,
+               kKnownTrustedVaultKeyVersion + 2},
+              /*signing_keys=*/
+              {kKnownTrustedVaultKey, kTrustedVaultKey1}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kSuccess));
+  EXPECT_THAT(processed_response.keys,
+              ElementsAre(kTrustedVaultKey1, kTrustedVaultKey2));
+  EXPECT_THAT(processed_response.last_key_version,
+              Eq(kKnownTrustedVaultKeyVersion + 2));
+}
+
+// Server can already clean-up kKnownTrustedVaultKey and the following key. In
+// this case client state is not sufficient to silently download keys and
+// kLocalDataObsolete should be reported.
+// Possible full key chain is: kKnownTrustedVaultKey -> kTrustedVaultKey1 ->
+// kTrustedVaultKey2 -> kTrustedVaultKey3.
+// Server side key chain is: kTrustedVaultKey2 -> kTrustedVaultKey3.
+TEST_F(DownloadKeysResponseHandlerTest,
+       ShouldHandleAbsenseOfKnownKeyWhenKeyChainIsNotRecoverable) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/
+              {kTrustedVaultKey2, kTrustedVaultKey3},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion + 2,
+               kKnownTrustedVaultKeyVersion + 3},
+              /*signing_keys=*/
+              {kTrustedVaultKey1, kTrustedVaultKey2}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+  EXPECT_THAT(processed_response.keys, IsEmpty());
+}
+
+// The test populates undecryptable/corrupted |wrapped_key| field, handler
+// should return kLocalDataObsolete to allow client to restore Member by
+// re-registration.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleUndecryptableKey) {
+  sync_pb::ListSecurityDomainsResponse response;
+  sync_pb::SecurityDomain* security_domain = response.add_security_domains();
+  security_domain->set_name(kSecurityDomainName);
+  sync_pb::SecurityDomain::Member* member = security_domain->add_members();
+  FillSecurityDomainMember(
+      MakeTestKeyPair()->public_key(),
+      /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+      /*trusted_vault_keys_versions=*/
+      {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+      /*signing_keys=*/{{}, kKnownTrustedVaultKey}, member);
+
+  // Corrupt wrapped key corresponding to kTrustedVaultKey1.
+  member->mutable_keys(1)->set_wrapped_key("undecryptable_key");
+
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/response.SerializeAsString())
+                  .status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+}
+
+// The test populates invalid |key_proof| field for the single key rotation.
+// kTrustedVaultKey1 is expected to be signed with kKnownTrustedVaultKey, but
+// instead it's signed with kTrustedVaultKey2.
+TEST_F(DownloadKeysResponseHandlerTest,
+       ShouldHandleInvalidKeyProofOnSingleKeyRotation) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+              /*signing_keys=*/{{}, kTrustedVaultKey2}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+  EXPECT_THAT(processed_response.keys, IsEmpty());
+}
+
+// The test populates invalid |key_proof| field for intermediate key when
+// multiple key rotations have happened.
+// kTrustedVaultKey1 is expected to be signed with kKnownTrustedVaultKey, but
+// instead it's signed with kTrustedVaultKey2.
+TEST_F(DownloadKeysResponseHandlerTest,
+       ShouldHandleInvalidKeyProofOnMultipleKeyRotations) {
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/
+          CreateListSecurityDomainsResponseWithSingleSyncMember(
+              /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1,
+                                      kTrustedVaultKey2},
+              /*trusted_vault_keys_versions=*/
+              {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1,
+               kKnownTrustedVaultKeyVersion + 2},
+              /*signing_keys=*/{{}, kTrustedVaultKey2, kTrustedVaultKey1}));
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+  EXPECT_THAT(processed_response.keys, IsEmpty());
+}
+
+// In this scenario client already has most recent trusted vault key. It should
+// be reported as kLocalDataObsolete, because by issuing the request client
+// indicates that there should be new keys and it's possible that key rotation
+// has happened by Member state wasn't updated (requires re-registration).
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAbsenseOfNewKeys) {
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/
+                      CreateListSecurityDomainsResponseWithSingleSyncMember(
+                          /*trusted_vault_keys=*/{kKnownTrustedVaultKey},
+                          /*trusted_vault_keys_versions=*/
+                          {kKnownTrustedVaultKeyVersion},
+                          /*signing_keys=*/{{}}))
+                  .status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+}
+
+// Tests handling the situation, when response isn't a valid serialized
+// ListSecurityDomains proto.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleCorruptedResponseProto) {
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/"corrupted_proto")
+                  .status,
+              Eq(TrustedVaultRequestStatus::kOtherError));
+}
+
+// Client expects that the security domain exists, but the response indicates
+// it doesn't by having no security domains.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleEmptyResponse) {
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/std::string())
+                  .status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+}
+
+// Same as above, but there is a different security domain.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAbsenseOfSecurityDomain) {
+  sync_pb::ListSecurityDomainsResponse response;
+  sync_pb::SecurityDomain* security_domain = response.add_security_domains();
+  security_domain->set_name("other_domain");
+
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/response.SerializeAsString())
+                  .status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+}
+
+// Tests handling presence of other security domains.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleMultipleSecurityDomains) {
+  sync_pb::ListSecurityDomainsResponse response;
+  sync_pb::SecurityDomain* other_domain = response.add_security_domains();
+  other_domain->set_name("other_domain");
+
+  sync_pb::SecurityDomain* sync_domain = response.add_security_domains();
+  sync_domain->set_name(kSecurityDomainName);
+  FillSecurityDomainMember(
+      /*public_key=*/MakeTestKeyPair()->public_key(),
+      /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+      /*trusted_vault_keys_versions=*/
+      {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+      /*signing_keys=*/{{}, kKnownTrustedVaultKey}, sync_domain->add_members());
+
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/response.SerializeAsString());
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kSuccess));
+  EXPECT_THAT(processed_response.keys,
+              ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey1));
+  EXPECT_THAT(processed_response.last_key_version,
+              Eq(kKnownTrustedVaultKeyVersion + 1));
+}
+
+// Security domain exists, but doesn't contain member corresponding to the
+// current device.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleAbsenseOfMember) {
+  sync_pb::ListSecurityDomainsResponse response;
+  sync_pb::SecurityDomain* security_domain = response.add_security_domains();
+  security_domain->set_name(kSecurityDomainName);
+
+  FillSecurityDomainMember(
+      /*public_key=*/SecureBoxKeyPair::GenerateRandom()->public_key(),
+      /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+      /*trusted_vault_keys_versions=*/
+      {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+      /*signing_keys=*/{{}, kKnownTrustedVaultKey},
+      security_domain->add_members());
+
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/response.SerializeAsString())
+                  .status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+}
+
+// Tests handling presence of other members.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleMultipleMembers) {
+  sync_pb::ListSecurityDomainsResponse response;
+  sync_pb::SecurityDomain* security_domain = response.add_security_domains();
+  security_domain->set_name(kSecurityDomainName);
+
+  // Other member.
+  FillSecurityDomainMember(
+      /*public_key=*/SecureBoxKeyPair::GenerateRandom()->public_key(),
+      /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+      /*trusted_vault_keys_versions=*/
+      {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+      /*signing_keys=*/{{}, kKnownTrustedVaultKey},
+      security_domain->add_members());
+
+  // Member corresponding to the current device.
+  FillSecurityDomainMember(
+      /*public_key=*/MakeTestKeyPair()->public_key(),
+      /*trusted_vault_keys=*/{kKnownTrustedVaultKey, kTrustedVaultKey1},
+      /*trusted_vault_keys_versions=*/
+      {kKnownTrustedVaultKeyVersion, kKnownTrustedVaultKeyVersion + 1},
+      /*signing_keys=*/{{}, kKnownTrustedVaultKey},
+      security_domain->add_members());
+
+  const DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      handler().ProcessResponse(
+          /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+          /*response_body=*/response.SerializeAsString());
+
+  EXPECT_THAT(processed_response.status,
+              Eq(TrustedVaultRequestStatus::kSuccess));
+  EXPECT_THAT(processed_response.keys,
+              ElementsAre(kKnownTrustedVaultKey, kTrustedVaultKey1));
+  EXPECT_THAT(processed_response.last_key_version,
+              Eq(kKnownTrustedVaultKeyVersion + 1));
+}
+
+// Corrupted data case: the member corresponding to the current device exists,
+// but has no keys.
+TEST_F(DownloadKeysResponseHandlerTest, ShouldHandleEmptyMember) {
+  EXPECT_THAT(handler()
+                  .ProcessResponse(
+                      /*http_status=*/TrustedVaultRequest::HttpStatus::kSuccess,
+                      /*response_body=*/
+                      CreateListSecurityDomainsResponseWithSingleSyncMember(
+                          /*trusted_vault_keys=*/{},
+                          /*trusted_vault_keys_versions=*/{},
+                          /*signing_keys=*/{}))
+                  .status,
+              Eq(TrustedVaultRequestStatus::kLocalDataObsolete));
+}
+
+}  // namespace
+
+}  // namespace syncer
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
index 54751cf..fa8ea11 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
@@ -357,6 +357,7 @@
 
   switch (status) {
     case TrustedVaultRequestStatus::kSuccess:
+      // TODO(crbug.com/1102340): consider keeping old keys as well.
       StoreKeys(gaia_id, vault_keys, last_vault_key_version);
       break;
     case TrustedVaultRequestStatus::kLocalDataObsolete: {
diff --git a/components/sync/trusted_vault/trusted_vault_connection_impl.cc b/components/sync/trusted_vault/trusted_vault_connection_impl.cc
index 5293bce..15311fdc 100644
--- a/components/sync/trusted_vault/trusted_vault_connection_impl.cc
+++ b/components/sync/trusted_vault/trusted_vault_connection_impl.cc
@@ -9,6 +9,7 @@
 #include "base/containers/span.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/sync/protocol/vault.pb.h"
+#include "components/sync/trusted_vault/download_keys_response_handler.h"
 #include "components/sync/trusted_vault/securebox.h"
 #include "components/sync/trusted_vault/trusted_vault_access_token_fetcher.h"
 #include "components/sync/trusted_vault/trusted_vault_request.h"
@@ -23,11 +24,15 @@
 const uint8_t kWrappedKeyHeader[] = {'V', '1', ' ', 's', 'h', 'a', 'r',
                                      'e', 'd', '_', 'k', 'e', 'y'};
 const char kJoinSecurityDomainsURLPath[] = "/domain:join";
+const char kListSecurityDomainsURLPathAndQuery[] = "/domain:list?view=1";
 const char kSecurityDomainName[] = "chromesync";
 
 // Helper function for filling protobuf bytes field: protobuf represent them as
 // std::string, while in code std::vector<uint8_t> or base::span<uint8_t> is
 // more common.
+// TODO(crbug.com/1113598): this function and its counterpart (proto string to
+// bytes vector) is useful in many places under trusted_vault directory,
+// consider moving it to some utility file.
 void AssignBytesToProtoString(base::span<const uint8_t> bytes,
                               std::string* bytes_proto_field) {
   *bytes_proto_field = std::string(bytes.begin(), bytes.end());
@@ -106,6 +111,17 @@
   return result;
 }
 
+void ProcessDownloadKeysResponse(
+    std::unique_ptr<DownloadKeysResponseHandler> response_handler,
+    TrustedVaultConnection::DownloadKeysCallback callback,
+    TrustedVaultRequest::HttpStatus http_status,
+    const std::string& response_body) {
+  DownloadKeysResponseHandler::ProcessedResponse processed_response =
+      response_handler->ProcessResponse(http_status, response_body);
+  std::move(callback).Run(processed_response.status, processed_response.keys,
+                          processed_response.last_key_version);
+}
+
 }  // namespace
 
 TrustedVaultConnectionImpl::TrustedVaultConnectionImpl(
@@ -149,9 +165,23 @@
     int last_trusted_vault_key_version,
     std::unique_ptr<SecureBoxKeyPair> device_key_pair,
     DownloadKeysCallback callback) {
-  NOTIMPLEMENTED();
-  // TODO(crbug.com/1113598): implement logic.
-  return std::make_unique<Request>();
+  auto request = std::make_unique<TrustedVaultRequest>(
+      TrustedVaultRequest::HttpMethod::kGet,
+      GURL(trusted_vault_service_url_.spec() +
+           kListSecurityDomainsURLPathAndQuery),
+      /*serialized_request_proto=*/base::nullopt);
+
+  request->FetchAccessTokenAndSendRequest(
+      account_info.account_id, GetOrCreateURLLoaderFactory(),
+      access_token_fetcher_.get(),
+      base::BindOnce(ProcessDownloadKeysResponse,
+                     /*response_processor=*/
+                     std::make_unique<DownloadKeysResponseHandler>(
+                         last_trusted_vault_key, last_trusted_vault_key_version,
+                         std::move(device_key_pair)),
+                     std::move(callback)));
+
+  return request;
 }
 
 scoped_refptr<network::SharedURLLoaderFactory>
diff --git a/components/sync/trusted_vault/trusted_vault_connection_impl_unittest.cc b/components/sync/trusted_vault/trusted_vault_connection_impl_unittest.cc
index ec38e05..a8023b0 100644
--- a/components/sync/trusted_vault/trusted_vault_connection_impl_unittest.cc
+++ b/components/sync/trusted_vault/trusted_vault_connection_impl_unittest.cc
@@ -39,6 +39,8 @@
 const char kTestURL[] = "https://test.com/test";
 const char kTestJoinSecurityDomainsURL[] =
     "https://test.com/test/domain:join?alt=proto";
+const char kTestListSecurityDomainsURL[] =
+    "https://test.com/test/domain:list?view=1&alt=proto";
 const uint8_t kWrappedKeyHeader[] = {'V', '1', ' ', 's', 'h', 'a', 'r',
                                      'e', 'd', '_', 'k', 'e', 'y'};
 
@@ -115,6 +117,15 @@
         /*content=*/std::string(), response_http_code);
   }
 
+  bool RespondToListSecurityDomainsRequest(
+      net::HttpStatusCode response_http_code) {
+    // Allow request to reach |test_url_loader_factory_|.
+    base::RunLoop().RunUntilIdle();
+    return test_url_loader_factory_.SimulateResponseForPendingRequest(
+        kTestListSecurityDomainsURL, /*content=*/std::string(),
+        response_http_code);
+  }
+
   const std::vector<uint8_t> kTrustedVaultKey = {1, 2, 3, 4};
 
  private:
@@ -300,6 +311,86 @@
   RespondToJoinSecurityDomainsRequest(net::HTTP_OK);
 }
 
+TEST_F(TrustedVaultConnectionImplTest, ShouldSendListSecurityDomainsRequest) {
+  std::unique_ptr<TrustedVaultConnection::Request> request =
+      connection()->DownloadKeys(
+          /*account_info=*/CoreAccountInfo(),
+          /*last_trusted_vault_key=*/std::vector<uint8_t>(),
+          /*last_trusted_vault_key_version=*/0,
+          /*device_key_pair=*/MakeTestKeyPair(), base::DoNothing());
+  EXPECT_THAT(request, NotNull());
+
+  network::TestURLLoaderFactory::PendingRequest* pending_http_request =
+      GetPendingHTTPRequest();
+  ASSERT_THAT(pending_http_request, NotNull());
+
+  const network::ResourceRequest& resource_request =
+      pending_http_request->request;
+  EXPECT_THAT(resource_request.method, Eq("GET"));
+  EXPECT_THAT(resource_request.url, Eq(GURL(kTestListSecurityDomainsURL)));
+}
+
+// TODO(crbug.com/1113598): add coverage for at least one successful case
+// (need to share some helper functions with
+// download_keys_response_handler_unittest.cc).
+TEST_F(TrustedVaultConnectionImplTest,
+       ShouldHandleFailedListSecurityDomainsRequest) {
+  base::MockCallback<TrustedVaultConnection::DownloadKeysCallback> callback;
+
+  std::unique_ptr<TrustedVaultConnection::Request> request =
+      connection()->DownloadKeys(
+          /*account_info=*/CoreAccountInfo(),
+          /*last_trusted_vault_key=*/std::vector<uint8_t>(),
+          /*last_trusted_vault_key_version=*/0,
+          /*device_key_pair=*/MakeTestKeyPair(), callback.Get());
+  ASSERT_THAT(request, NotNull());
+
+  EXPECT_CALL(callback, Run(Eq(TrustedVaultRequestStatus::kOtherError), _, _));
+  EXPECT_TRUE(
+      RespondToListSecurityDomainsRequest(net::HTTP_INTERNAL_SERVER_ERROR));
+}
+
+TEST_F(TrustedVaultConnectionImplTest,
+       ShouldHandleAccessTokenFetchingFailureWhenDownloadingKeys) {
+  std::unique_ptr<TrustedVaultConnectionImpl> connection =
+      CreateConnectionWithAccessToken(
+          /*access_token=*/base::nullopt);
+
+  base::MockCallback<TrustedVaultConnection::DownloadKeysCallback> callback;
+
+  // |callback| is called immediately after DownloadKeys(), because there is no
+  // access token.
+  EXPECT_CALL(callback, Run(Eq(TrustedVaultRequestStatus::kOtherError), _, _));
+  std::unique_ptr<TrustedVaultConnection::Request> request =
+      connection->DownloadKeys(
+          /*account_info=*/CoreAccountInfo(),
+          /*last_trusted_vault_key=*/std::vector<uint8_t>(),
+          /*last_trusted_vault_key_version=*/0,
+          /*device_key_pair=*/MakeTestKeyPair(), callback.Get());
+  ASSERT_THAT(request, NotNull());
+
+  // No requests should be sent to the network.
+  EXPECT_THAT(GetPendingHTTPRequest(), IsNull());
+}
+
+TEST_F(TrustedVaultConnectionImplTest, ShouldCancelListSecurityDomainsRequest) {
+  base::MockCallback<TrustedVaultConnection::DownloadKeysCallback> callback;
+
+  std::unique_ptr<TrustedVaultConnection::Request> request =
+      connection()->DownloadKeys(
+          /*account_info=*/CoreAccountInfo(),
+          /*last_trusted_vault_key=*/std::vector<uint8_t>(),
+          /*last_trusted_vault_key_version=*/0,
+          /*device_key_pair=*/MakeTestKeyPair(), callback.Get());
+  ASSERT_THAT(request, NotNull());
+
+  EXPECT_CALL(callback, Run).Times(0);
+  request.reset();
+  // Returned value isn't checked here, because the request can be cancelled
+  // before reaching TestURLLoaderFactory.
+  RespondToListSecurityDomainsRequest(net::HTTP_OK);
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc
index e689cba..f99ea09 100644
--- a/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -132,8 +132,8 @@
                                     syncer::ModelType model_type) {
   std::string serialized;
   JSONStringValueSerializer json(&serialized);
-  if (!json.Serialize(value))
-    return syncer::SyncChange();
+  bool success = json.Serialize(value);
+  DCHECK(success);
   sync_pb::EntitySpecifics entity;
   sync_pb::PreferenceSpecifics* pref =
       PrefModelAssociator::GetMutableSpecifics(model_type, &entity);
diff --git a/components/ui_devtools/BUILD.gn b/components/ui_devtools/BUILD.gn
index 4063225..7bccde2 100644
--- a/components/ui_devtools/BUILD.gn
+++ b/components/ui_devtools/BUILD.gn
@@ -153,6 +153,7 @@
     "//base/test:test_support",
     "//net:test_support",
     "//services/network:network_service",
+    "//services/network:test_support",
     "//testing/gtest",
   ]
 
diff --git a/components/ui_devtools/DEPS b/components/ui_devtools/DEPS
index 454d9846..1251e4bb 100644
--- a/components/ui_devtools/DEPS
+++ b/components/ui_devtools/DEPS
@@ -14,5 +14,6 @@
   "devtools_server_unittest\.cc": [
     "+services/network/network_context.h",
     "+services/network/network_service.h",
+    "+services/network/test/fake_test_cert_verifier_params_factory.h",
   ]
 }
diff --git a/components/ui_devtools/devtools_server_unittest.cc b/components/ui_devtools/devtools_server_unittest.cc
index 07a30f7..f7439ede 100644
--- a/components/ui_devtools/devtools_server_unittest.cc
+++ b/components/ui_devtools/devtools_server_unittest.cc
@@ -15,6 +15,7 @@
 #include "net/socket/tcp_client_socket.h"
 #include "services/network/network_context.h"
 #include "services/network/network_service.h"
+#include "services/network/test/fake_test_cert_verifier_params_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ui_devtools {
@@ -39,10 +40,17 @@
   mojo::Remote<network::mojom::NetworkService> network_service_remote;
   auto network_service = network::NetworkService::Create(
       network_service_remote.BindNewPipeAndPassReceiver());
+
+  network::mojom::NetworkContextParamsPtr context_params =
+      network::mojom::NetworkContextParams::New();
+  // Use a dummy CertVerifier that always passes cert verification, since
+  // these unittests don't need to test CertVerifier behavior.
+  context_params->cert_verifier_params =
+      network::FakeTestCertVerifierParamsFactory::GetCertVerifierParams();
   mojo::Remote<network::mojom::NetworkContext> network_context_remote;
   network_service_remote->CreateNetworkContext(
       network_context_remote.BindNewPipeAndPassReceiver(),
-      network::mojom::NetworkContextParams::New());
+      std::move(context_params));
 
   std::unique_ptr<UiDevToolsServer> server =
       UiDevToolsServer::CreateForViews(network_context_remote.get(), fake_port);
diff --git a/components/update_client/background_downloader_win.cc b/components/update_client/background_downloader_win.cc
index 682c357..dc9c961 100644
--- a/components/update_client/background_downloader_win.cc
+++ b/components/update_client/background_downloader_win.cc
@@ -25,6 +25,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/scoped_thread_priority.h"
 #include "base/win/atl.h"
 #include "base/win/scoped_co_mem.h"
 #include "components/update_client/task_traits.h"
@@ -139,6 +140,9 @@
 
 // Retrieves the singleton instance of GIT for this process.
 HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) {
+  // Mitigate the issues caused by loading DLLs on a background thread
+  // (http://crbug/973868).
+  SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
   return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr,
                             CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&(*git)));
 }
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
index e4c0e24a..c358b62 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -77,6 +77,8 @@
     }
   }
 
+  void OnContextLost() { representation_->OnContextLost(); }
+
   bool unique() const { return ref_ == 1; }
   const gpu::Mailbox& mailbox() const { return representation_->mailbox(); }
   gpu::SharedImageRepresentationOverlay::ScopedReadAccess* scoped_read_access()
@@ -389,6 +391,18 @@
     it->Unref();
   }
 
+  // Code below can destroy last representation of the overlay shared image. On
+  // MacOS it needs context to be current.
+#if defined(OS_APPLE)
+  // TODO(vasilyt): We shouldn't need this after we stop using
+  // SharedImageBackingGLImage as backing.
+  if (!dependency_->GetSharedContextState()->MakeCurrent(nullptr)) {
+    for (auto& overlay : overlays_) {
+      overlay.OnContextLost();
+    }
+  }
+#endif
+
   // Go through backings of all overlays, and release overlay backings which are
   // not used.
   std::vector<gpu::Mailbox> released_overlays;
@@ -409,10 +423,11 @@
   });
 
   DCHECK(!result.gpu_fence);
+  const auto& mailbox =
+      image ? image->skia_representation()->mailbox() : gpu::Mailbox();
   FinishSwapBuffers(std::move(result), size, latency_info,
-                    /*damage_area=*/base::nullopt,
-                    std::move(released_overlays),
-                    image ? image->skia_representation()->mailbox() : gpu::Mailbox());
+                    /*damage_area=*/base::nullopt, std::move(released_overlays),
+                    mailbox);
   PageFlipComplete(image.get());
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 463dc99..a75f9bef5 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -467,8 +467,10 @@
   // callbacks.
   auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SwapBuffersSkipped,
                              base::Unretained(impl_on_gpu_.get()));
+  // SwapBuffersSkipped currently does mostly the same as SwapBuffers and needs
+  // MakeCurrent.
   EnqueueGpuTask(std::move(task), std::move(resource_sync_tokens_),
-                 /*make_current=*/false, /*need_framebuffer=*/false);
+                 /*make_current=*/true, /*need_framebuffer=*/false);
 
   // TODO(vasilyt): reuse root recorder
   RecreateRootRecorder();
@@ -657,7 +659,8 @@
       base::BindOnce(&SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource,
                      base::Unretained(impl_on_gpu_.get()), std::move(ids),
                      std::move(image_contexts));
-  EnqueueGpuTask(std::move(callback), {}, /*make_current=*/false,
+  // RemoveRenderPassResources will delete gpu resources and needs MakeCurrent.
+  EnqueueGpuTask(std::move(callback), {}, /*make_current=*/true,
                  /*need_framebuffer=*/false);
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 3d96b81..42ef422 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -1384,7 +1384,11 @@
 #if defined(OS_APPLE)
   // Release any backings which are not reused by the current frame, probably
   // because the properties of render passes are changed or render passes are
-  // removed.
+  // removed
+  if (context_is_lost_) {
+    for (auto& image : available_render_pass_overlay_backings_)
+      image->OnContextLost();
+  }
   available_render_pass_overlay_backings_.clear();
 #endif
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android.cc b/content/browser/accessibility/accessibility_tree_formatter_android.cc
index e3e9ebe5..1a497c9 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -99,8 +99,7 @@
                      base::DictionaryValue* dict) const;
 
   std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) const override;
+      const base::DictionaryValue& node) const override;
 };
 
 // static
@@ -244,8 +243,7 @@
 }
 
 std::string AccessibilityTreeFormatterAndroid::ProcessTreeForOutput(
-    const base::DictionaryValue& dict,
-    base::DictionaryValue* filtered_dict_result) const {
+    const base::DictionaryValue& dict) const {
   std::string error_value;
   if (dict.GetString("error", &error_value))
     return error_value;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index 8a14d708..d150bf30 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -39,8 +39,7 @@
 
  private:
   std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) const override;
+      const base::DictionaryValue& node) const override;
 
   base::Value BuildTree(BrowserAccessibility* root) const override;
   base::Value BuildTreeForWindow(gfx::AcceleratedWidget hwnd) const override;
@@ -608,8 +607,7 @@
 };
 
 std::string AccessibilityTreeFormatterAuraLinux::ProcessTreeForOutput(
-    const base::DictionaryValue& node,
-    base::DictionaryValue* filtered_dict_result) const {
+    const base::DictionaryValue& node) const {
   std::string error_value;
   if (node.GetString("error", &error_value))
     return error_value;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.cc b/content/browser/accessibility/accessibility_tree_formatter_base.cc
index 337da77..32d2fc8 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.cc
@@ -71,22 +71,6 @@
   return contents;
 }
 
-base::Value AccessibilityTreeFormatterBase::FilterTree(
-    const base::Value& dict) const {
-  base::DictionaryValue filtered_dict;
-  ProcessTreeForOutput(base::Value::AsDictionaryValue(dict), &filtered_dict);
-  const base::Value* children = dict.FindListPath(kChildrenDictAttr);
-  if (children && !children->GetList().empty()) {
-    base::Value filtered_children(base::Value::Type::LIST);
-    for (const auto& child_dict : children->GetList()) {
-      auto filtered_child = FilterTree(child_dict);
-      filtered_children.Append(std::move(filtered_child));
-    }
-    filtered_dict.SetPath(kChildrenDictAttr, std::move(filtered_children));
-  }
-  return std::move(filtered_dict);
-}
-
 void AccessibilityTreeFormatterBase::RecursiveFormatTree(
     const base::Value& dict,
     std::string* contents,
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.h b/content/browser/accessibility/accessibility_tree_formatter_base.h
index b211183..5af8d38 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.h
@@ -74,7 +74,6 @@
   // AXTreeFormatter overrides.
   void AddDefaultFilters(
       std::vector<AXPropertyFilter>* property_filters) override;
-  base::Value FilterTree(const base::Value& dict) const override;
   std::string FormatTree(const base::Value& tree_node) const override;
   void SetPropertyFilters(
       const std::vector<AXPropertyFilter>& property_filters) override;
@@ -102,8 +101,7 @@
   // - Provides a filtered version of the dictionary in an out param,
   //   (only if the out param is provided).
   virtual std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) const = 0;
+      const base::DictionaryValue& node) const = 0;
 
   //
   // Utility functions to be used by each platform.
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index 2179f6c..d3fe32da 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -402,8 +402,7 @@
 }
 
 std::string AccessibilityTreeFormatterBlink::ProcessTreeForOutput(
-    const base::DictionaryValue& dict,
-    base::DictionaryValue* filtered_dict_result) const {
+    const base::DictionaryValue& dict) const {
   std::string error_value;
   if (dict.GetString("error", &error_value))
     return error_value;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.h b/content/browser/accessibility/accessibility_tree_formatter_blink.h
index 80312d3..0bd1c570 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.h
@@ -41,8 +41,7 @@
                      base::DictionaryValue* dict) const;
 
   std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) const override;
+      const base::DictionaryValue& node) const override;
 };
 
 }  // namespace content
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index ee70a95..d5742c5 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -102,8 +102,7 @@
   std::string NodeToLineIndex(id, const LineIndexer*) const;
 
   std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) const override;
+      const base::DictionaryValue& node) const override;
 
   std::string FormatAttributeValue(const base::Value& value) const;
 };
@@ -453,8 +452,7 @@
 }
 
 std::string AccessibilityTreeFormatterMac::ProcessTreeForOutput(
-    const base::DictionaryValue& dict,
-    base::DictionaryValue* filtered_dict_result) const {
+    const base::DictionaryValue& dict) const {
   std::string error_value;
   if (dict.GetString("error", &error_value))
     return error_value;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc b/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc
index 2a76c2e..d033e0d 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_uia_win.cc
@@ -996,8 +996,7 @@
 }
 
 std::string AccessibilityTreeFormatterUia::ProcessTreeForOutput(
-    const base::DictionaryValue& dict,
-    base::DictionaryValue* filtered_result) const {
+    const base::DictionaryValue& dict) const {
   std::unique_ptr<base::DictionaryValue> tree;
   std::string line;
 
@@ -1006,16 +1005,10 @@
   dict.GetString(UiaIdentifierToCondensedString(UIA_ControlTypePropertyId),
                  &control_type_value);
   WriteAttribute(true, control_type_value, &line);
-  if (filtered_result) {
-    filtered_result->SetString(
-        UiaIdentifierToStringUTF8(UIA_ControlTypePropertyId),
-        control_type_value);
-  }
 
   // properties
   for (long i : properties_) {
-    ProcessPropertyForOutput(UiaIdentifierToCondensedString(i), dict, line,
-                             filtered_result);
+    ProcessPropertyForOutput(UiaIdentifierToCondensedString(i), dict, line);
   }
 
   // patterns
@@ -1049,8 +1042,7 @@
       "Window.IsModal"};
 
   for (const std::string& pattern_property_name : pattern_property_names) {
-    ProcessPropertyForOutput(pattern_property_name, dict, line,
-                             filtered_result);
+    ProcessPropertyForOutput(pattern_property_name, dict, line);
   }
 
   return line;
@@ -1059,75 +1051,60 @@
 void AccessibilityTreeFormatterUia::ProcessPropertyForOutput(
     const std::string& property_name,
     const base::DictionaryValue& dict,
-    std::string& line,
-    base::DictionaryValue* filtered_result) const {
+    std::string& line) const {
   //
   const base::Value* value;
   if (dict.Get(property_name, &value))
-    ProcessValueForOutput(property_name, value, line, filtered_result);
+    ProcessValueForOutput(property_name, value, line);
 }
 
 void AccessibilityTreeFormatterUia::ProcessValueForOutput(
     const std::string& name,
     const base::Value* value,
-    std::string& line,
-    base::DictionaryValue* filtered_result) const {
+    std::string& line) const {
   switch (value->type()) {
     case base::Value::Type::STRING: {
       std::string string_value;
       value->GetAsString(&string_value);
-      bool did_pass_filters = WriteAttribute(
+      WriteAttribute(
           false,
           base::StringPrintf("%s='%s'", name.c_str(), string_value.c_str()),
           &line);
-      if (filtered_result && did_pass_filters)
-        filtered_result->SetString(name, string_value);
       break;
     }
     case base::Value::Type::BOOLEAN: {
       bool bool_value = 0;
       value->GetAsBoolean(&bool_value);
-      bool did_pass_filters =
           WriteAttribute(false,
                          base::StringPrintf("%s=%s", name.c_str(),
                                             (bool_value ? "true" : "false")),
                          &line);
-      if (filtered_result && did_pass_filters)
-        filtered_result->SetBoolean(name, bool_value);
       break;
     }
     case base::Value::Type::INTEGER: {
       int int_value = 0;
       value->GetAsInteger(&int_value);
-      bool did_pass_filters = WriteAttribute(
+      WriteAttribute(
           false, base::StringPrintf("%s=%d", name.c_str(), int_value), &line);
-      if (filtered_result && did_pass_filters)
-        filtered_result->SetInteger(name, int_value);
       break;
     }
     case base::Value::Type::DOUBLE: {
       double double_value = 0.0;
       value->GetAsDouble(&double_value);
-      bool did_pass_filters = WriteAttribute(
-          false, base::StringPrintf("%s=%.2f", name.c_str(), double_value),
-          &line);
-      if (filtered_result && did_pass_filters)
-        filtered_result->SetDouble(name, double_value);
+      WriteAttribute(false,
+                     base::StringPrintf("%s=%.2f", name.c_str(), double_value),
+                     &line);
       break;
     }
     case base::Value::Type::DICTIONARY: {
       const base::DictionaryValue* dict_value = nullptr;
       value->GetAsDictionary(&dict_value);
-      bool did_pass_filters = false;
       if (name == "BoundingRectangle") {
-        did_pass_filters =
-            WriteAttribute(false,
-                           FormatRectangle(*dict_value, "BoundingRectangle",
-                                           "left", "top", "width", "height"),
-                           &line);
+        WriteAttribute(false,
+                       FormatRectangle(*dict_value, "BoundingRectangle", "left",
+                                       "top", "width", "height"),
+                       &line);
       }
-      if (filtered_result && did_pass_filters)
-        filtered_result->SetKey(name, dict_value->Clone());
       break;
     }
     default:
diff --git a/content/browser/accessibility/accessibility_tree_formatter_uia_win.h b/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
index d0c8eec..be0c6b0 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
@@ -91,16 +91,13 @@
                          base::DictionaryValue* dict) const;
   base::string16 GetNodeName(IUIAutomationElement* node) const;
   std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_result = nullptr) const override;
+      const base::DictionaryValue& node) const override;
   void ProcessPropertyForOutput(const std::string& property_name,
                                 const base::DictionaryValue& dict,
-                                std::string& line,
-                                base::DictionaryValue* filtered_result) const;
+                                std::string& line) const;
   void ProcessValueForOutput(const std::string& name,
                              const base::Value* value,
-                             std::string& line,
-                             base::DictionaryValue* filtered_result) const;
+                             std::string& line) const;
   Microsoft::WRL::ComPtr<IUIAutomation> uia_;
   Microsoft::WRL::ComPtr<IUIAutomationCacheRequest> element_cache_request_;
   Microsoft::WRL::ComPtr<IUIAutomationCacheRequest> children_cache_request_;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index 9684afd..ba79ad75 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -79,8 +79,7 @@
   void AddIA2ValueProperties(const Microsoft::WRL::ComPtr<IAccessible>,
                              base::DictionaryValue* dict) const;
   std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node,
-      base::DictionaryValue* filtered_dict_result = nullptr) const override;
+      const base::DictionaryValue& node) const override;
 };
 
 // static
@@ -872,16 +871,13 @@
 }
 
 std::string AccessibilityTreeFormatterWin::ProcessTreeForOutput(
-    const base::DictionaryValue& dict,
-    base::DictionaryValue* filtered_dict_result) const {
+    const base::DictionaryValue& dict) const {
   std::string line;
 
   // Always show role, and show it first.
   std::string role_value;
   dict.GetString("role", &role_value);
   WriteAttribute(true, role_value, &line);
-  if (filtered_dict_result)
-    filtered_dict_result->SetString("role", role_value);
 
   for (const char* attribute_name : ALL_ATTRIBUTES) {
     const base::Value* value;
@@ -892,32 +888,26 @@
       case base::Value::Type::STRING: {
         std::string string_value;
         value->GetAsString(&string_value);
-        bool did_pass_filters = WriteAttribute(
+        WriteAttribute(
             false,
             base::StringPrintf("%s='%s'", attribute_name, string_value.c_str()),
             &line);
-        if (filtered_dict_result && did_pass_filters)
-          filtered_dict_result->SetString(attribute_name, string_value);
         break;
       }
       case base::Value::Type::INTEGER: {
         int int_value = 0;
         value->GetAsInteger(&int_value);
-        bool did_pass_filters = WriteAttribute(
-            false, base::StringPrintf("%s=%d", attribute_name, int_value),
-            &line);
-        if (filtered_dict_result && did_pass_filters)
-          filtered_dict_result->SetInteger(attribute_name, int_value);
+        WriteAttribute(false,
+                       base::StringPrintf("%s=%d", attribute_name, int_value),
+                       &line);
         break;
       }
       case base::Value::Type::DOUBLE: {
         double double_value = 0.0;
         value->GetAsDouble(&double_value);
-        bool did_pass_filters = WriteAttribute(
+        WriteAttribute(
             false, base::StringPrintf("%s=%.2f", attribute_name, double_value),
             &line);
-        if (filtered_dict_result && did_pass_filters)
-          filtered_dict_result->SetDouble(attribute_name, double_value);
         break;
       }
       case base::Value::Type::LIST: {
@@ -934,8 +924,6 @@
             if (WriteAttribute(false, string_value, &line))
               filtered_list->AppendString(string_value);
         }
-        if (filtered_dict_result && !filtered_list->empty())
-          filtered_dict_result->Set(attribute_name, std::move(filtered_list));
         break;
       }
       case base::Value::Type::DICTIONARY: {
@@ -943,18 +931,15 @@
         // Revisit this if that changes.
         const base::DictionaryValue* dict_value;
         value->GetAsDictionary(&dict_value);
-        bool did_pass_filters = false;
         if (strcmp(attribute_name, "size") == 0) {
-          did_pass_filters = WriteAttribute(
+          WriteAttribute(
               false, FormatCoordinates(*dict_value, "size", "width", "height"),
               &line);
         } else if (strcmp(attribute_name, "location") == 0) {
-          did_pass_filters = WriteAttribute(
-              false, FormatCoordinates(*dict_value, "location", "x", "y"),
-              &line);
+          WriteAttribute(false,
+                         FormatCoordinates(*dict_value, "location", "x", "y"),
+                         &line);
         }
-        if (filtered_dict_result && did_pass_filters)
-          filtered_dict_result->SetKey(attribute_name, dict_value->Clone());
         break;
       }
       default:
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 4c1ef76..97e6f89d 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -271,7 +271,8 @@
   DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::RequestSent,
                    request_id.ToString(), loader_id.ToString(), request,
                    protocol::Network::Initiator::TypeEnum::SignedExchange,
-                   signed_exchange_url, timestamp);
+                   signed_exchange_url, /*initiator_devtools_request_id=*/"",
+                   timestamp);
 
   auto value = std::make_unique<base::trace_event::TracedValue>();
   value->SetString("requestId", request_id.ToString());
@@ -719,21 +720,17 @@
                             int32_t render_frame_id,
                             const base::UnguessableToken& devtools_request_id,
                             const network::ResourceRequest& request,
-                            const GURL& initiator_url) {
+                            const GURL& initiator_url,
+                            const std::string& initiator_devtools_request_id) {
   FrameTreeNode* ftn = GetFtnForNetworkRequest(process_id, render_frame_id);
   if (!ftn)
     return;
   auto timestamp = base::TimeTicks::Now();
   auto id = devtools_request_id.ToString();
-  // TODO(crbug.com/941297): Currently we are using an empty string for
-  // |loader_id|. But when we will introduce a better UI for preflight requests,
-  // consider using the navigation token which is same as the |loader_id| of the
-  // original request or the |devtools_request_id| of the original request, so
-  // that we can associate the requests in the DevTools front end.
   DispatchToAgents(ftn, &protocol::NetworkHandler::RequestSent, id,
                    /* loader_id=*/"", request,
-                   protocol::Network::Initiator::TypeEnum::Other, initiator_url,
-                   timestamp);
+                   protocol::Network::Initiator::TypeEnum::Preflight,
+                   initiator_url, initiator_devtools_request_id, timestamp);
 }
 
 void OnCorsPreflightResponse(int32_t process_id,
@@ -747,7 +744,7 @@
   auto id = devtools_request_id.ToString();
   DispatchToAgents(ftn, &protocol::NetworkHandler::ResponseReceived, id,
                    /* loader_id=*/"", url,
-                   protocol::Network::ResourceTypeEnum::Other, *head,
+                   protocol::Network::ResourceTypeEnum::Preflight, *head,
                    protocol::Maybe<std::string>());
 }
 
@@ -761,7 +758,7 @@
     return;
   auto id = devtools_request_id.ToString();
   DispatchToAgents(ftn, &protocol::NetworkHandler::LoadingComplete, id,
-                   protocol::Network::ResourceTypeEnum::Other, status);
+                   protocol::Network::ResourceTypeEnum::Preflight, status);
 }
 
 namespace {
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index 5636489..736e746 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -160,7 +160,8 @@
                             int32_t render_frame_id,
                             const base::UnguessableToken& devtools_request_id,
                             const network::ResourceRequest& request,
-                            const GURL& signed_exchange_url);
+                            const GURL& signed_exchange_url,
+                            const std::string& initiator_devtools_request_id);
 void OnCorsPreflightResponse(int32_t process_id,
                              int32_t render_frame_id,
                              const base::UnguessableToken& devtools_request_id,
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 7085282..4b8442f2 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1776,12 +1776,14 @@
       common_params.has_user_gesture);
 }
 
-void NetworkHandler::RequestSent(const std::string& request_id,
-                                 const std::string& loader_id,
-                                 const network::ResourceRequest& request,
-                                 const char* initiator_type,
-                                 const base::Optional<GURL>& initiator_url,
-                                 base::TimeTicks timestamp) {
+void NetworkHandler::RequestSent(
+    const std::string& request_id,
+    const std::string& loader_id,
+    const network::ResourceRequest& request,
+    const char* initiator_type,
+    const base::Optional<GURL>& initiator_url,
+    const std::string& initiator_devtools_request_id,
+    base::TimeTicks timestamp) {
   if (!enabled_)
     return;
   std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create());
@@ -1791,6 +1793,8 @@
       Network::Initiator::Create().SetType(initiator_type).Build();
   if (initiator_url)
     initiator->SetUrl(initiator_url->spec());
+  if (initiator_devtools_request_id.size())
+    initiator->SetRequestId(initiator_devtools_request_id);
   std::string url_fragment;
   std::string url_without_fragment =
       ExtractFragment(request.url, &url_fragment);
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index d03370b..735b49e1 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -177,6 +177,7 @@
                    const network::ResourceRequest& request,
                    const char* initiator_type,
                    const base::Optional<GURL>& initiator_url,
+                   const std::string& initiator_devtools_request_id,
                    base::TimeTicks timestamp);
   void ResponseReceived(const std::string& request_id,
                         const std::string& loader_id,
diff --git a/content/browser/devtools/service_worker_devtools_manager.cc b/content/browser/devtools/service_worker_devtools_manager.cc
index f8003e1..fd1c524 100644
--- a/content/browser/devtools/service_worker_devtools_manager.cc
+++ b/content/browser/devtools/service_worker_devtools_manager.cc
@@ -216,7 +216,8 @@
        protocol::NetworkHandler::ForAgentHost(it->second.get())) {
     network->RequestSent(request_id, std::string(), request,
                          protocol::Network::Initiator::TypeEnum::Preload,
-                         base::nullopt /* initiator_url */, timestamp);
+                         /*initiator_url=*/base::nullopt,
+                         /*initiator_devtools_request_id=*/"", timestamp);
   }
 }
 
diff --git a/content/browser/net/cross_origin_opener_policy_reporter.cc b/content/browser/net/cross_origin_opener_policy_reporter.cc
index 422c43d2..395d780 100644
--- a/content/browser/net/cross_origin_opener_policy_reporter.cc
+++ b/content/browser/net/cross_origin_opener_policy_reporter.cc
@@ -76,18 +76,15 @@
 std::vector<FrameTreeNode*> CollectOtherWindowForCoopAccess(
     FrameTreeNode* frame) {
   DCHECK(frame->IsMainFrame());
-  SiteInstance* site_instance = frame->current_frame_host()->GetSiteInstance();
   int virtual_browsing_context_group =
       frame->current_frame_host()->virtual_browsing_context_group();
 
   std::vector<FrameTreeNode*> out;
-  for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) {
-    RenderFrameHostImpl* rfh = wc->GetMainFrame();
-
-    // Filters out windows from a different browsing context group.
-    if (!rfh->GetSiteInstance()->IsRelatedSiteInstance(site_instance))
-      continue;
-
+  for (RenderFrameHostImpl* rfh :
+       frame->current_frame_host()
+           ->delegate()
+           ->GetActiveTopLevelDocumentsInBrowsingContextGroup(
+               frame->current_frame_host())) {
     // Filter out windows from the same virtual browsing context group.
     if (rfh->virtual_browsing_context_group() == virtual_browsing_context_group)
       continue;
diff --git a/content/browser/network_service_client.cc b/content/browser/network_service_client.cc
index 44e8b72..38ab568 100644
--- a/content/browser/network_service_client.cc
+++ b/content/browser/network_service_client.cc
@@ -236,9 +236,11 @@
     int32_t render_frame_id,
     const base::UnguessableToken& devtools_request_id,
     const network::ResourceRequest& request,
-    const GURL& initiator_url) {
+    const GURL& initiator_url,
+    const std::string& initiator_devtools_request_id) {
   devtools_instrumentation::OnCorsPreflightRequest(
-      process_id, render_frame_id, devtools_request_id, request, initiator_url);
+      process_id, render_frame_id, devtools_request_id, request, initiator_url,
+      initiator_devtools_request_id);
 }
 
 void NetworkServiceClient::OnCorsPreflightResponse(
diff --git a/content/browser/network_service_client.h b/content/browser/network_service_client.h
index 6849c44..2eb3415 100644
--- a/content/browser/network_service_client.h
+++ b/content/browser/network_service_client.h
@@ -64,11 +64,13 @@
       const net::CookieAndLineAccessResultList& cookies_with_access_result,
       std::vector<network::mojom::HttpRawHeaderPairPtr> headers,
       const base::Optional<std::string>& raw_response_headers) override;
-  void OnCorsPreflightRequest(int32_t process_id,
-                              int32_t render_frame_id,
-                              const base::UnguessableToken& devtool_request_id,
-                              const network::ResourceRequest& request,
-                              const GURL& initiator_url) override;
+  void OnCorsPreflightRequest(
+      int32_t process_id,
+      int32_t render_frame_id,
+      const base::UnguessableToken& devtool_request_id,
+      const network::ResourceRequest& request,
+      const GURL& initiator_url,
+      const std::string& initiator_devtools_request_id) override;
   void OnCorsPreflightResponse(
       int32_t process_id,
       int32_t render_frame_id,
diff --git a/content/browser/renderer_host/cross_origin_opener_policy_status.cc b/content/browser/renderer_host/cross_origin_opener_policy_status.cc
index 3296a4c..58d11400 100644
--- a/content/browser/renderer_host/cross_origin_opener_policy_status.cc
+++ b/content/browser/renderer_host/cross_origin_opener_policy_status.cc
@@ -9,6 +9,7 @@
 #include "base/feature_list.h"
 #include "base/time/time.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
+#include "content/browser/renderer_host/render_frame_host_delegate.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
@@ -84,7 +85,6 @@
     : frame_tree_node_(frame_tree_node),
       virtual_browsing_context_group_(frame_tree_node->current_frame_host()
                                           ->virtual_browsing_context_group()),
-      had_opener_(!!frame_tree_node->opener()),
       is_initial_navigation_(!frame_tree_node_->has_committed_real_load()),
       current_coop_(
           frame_tree_node->current_frame_host()->cross_origin_opener_policy()),
@@ -176,17 +176,20 @@
           current_coop_.report_only_value, current_origin_,
           is_initial_navigation_, response_coop.value, response_origin);
 
+  bool has_other_window_in_browsing_context_group =
+      frame_tree_node_->current_frame_host()
+          ->delegate()
+          ->GetActiveTopLevelDocumentsInBrowsingContextGroup(
+              frame_tree_node_->current_frame_host())
+          .size() > 1;
+
   if (cross_origin_policy_swap) {
     require_browsing_instance_swap_ = true;
 
-    // If this response's COOP causes a BrowsingInstance swap that severs an
-    // opener, report this to the previous COOP reporter and/or the COOP
-    // reporter of the response if they exist.
-    // TODO(clamy): This is not correct. We should be sending a report if there
-    // is any other top-level browsing context in the browsing context group,
-    // and not only when the browsing context has an opener. Otherwise, we
-    // would not emit a report when the opener of a window has a bcg switch.
-    if (had_opener_) {
+    // If this response's COOP causes a BrowsingInstance swap that severs
+    // communication with another page, report this to the previous COOP
+    // reporter and/or the COOP reporter of the response if they exist.
+    if (has_other_window_in_browsing_context_group) {
       response_reporter->QueueNavigationToCOOPReport(
           current_url_, current_origin_.IsSameOriginWith(response_origin),
           false /* is_report_only */);
@@ -205,13 +208,10 @@
                                 navigating_from_report_only_coop_swap);
   if (virtual_browsing_instance_swap) {
     // If this response's report-only COOP would cause a BrowsingInstance swap
-    // that would sever an opener, report this to the previous COOP reporter
-    // and/or the COOP reporter of the response if they exist.
-    // TODO(clamy): This is not correct. We should be sending a report if there
-    // is any other top-level browsing context in the browsing context group,
-    // and not only when the browsing context has an opener. Otherwise, we
-    // would not emit a report when the opener of a window has a bcg switch.
-    if (had_opener_) {
+    // that would sever communication with another page, report this to the
+    // previous COOP reporter and/or the COOP reporter of the response if they
+    // exist.
+    if (has_other_window_in_browsing_context_group) {
       response_reporter->QueueNavigationToCOOPReport(
           current_url_, current_origin_.IsSameOriginWith(response_origin),
           true /* is_report_only */);
diff --git a/content/browser/renderer_host/cross_origin_opener_policy_status.h b/content/browser/renderer_host/cross_origin_opener_policy_status.h
index ceaeb87..0eea5fe 100644
--- a/content/browser/renderer_host/cross_origin_opener_policy_status.h
+++ b/content/browser/renderer_host/cross_origin_opener_policy_status.h
@@ -88,11 +88,6 @@
 
   int virtual_browsing_context_group_;
 
-  // When a page has a reachable opener and COOP triggers a browsing instance
-  // swap we sever the window.open relationship. This is one of the cases that
-  // can be reported using the COOP reporting API.
-  const bool had_opener_;
-
   // Whether this is the first navigation happening in the browsing context.
   const bool is_initial_navigation_;
 
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index bd009ae..a50786d6 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -199,4 +199,10 @@
   return false;
 }
 
+std::vector<RenderFrameHostImpl*>
+RenderFrameHostDelegate::GetActiveTopLevelDocumentsInBrowsingContextGroup(
+    RenderFrameHostImpl* render_frame_host) {
+  return std::vector<RenderFrameHostImpl*>();
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index 2d2d197..32c0a67 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -637,6 +637,12 @@
   // The page is trying to move the main frame's representation in the client.
   virtual void SetWindowRect(const gfx::Rect& new_bounds) {}
 
+  // Returns the list of top-level RenderFrameHosts hosting active documents
+  // that belong to the same browsing context group as |render_frame_host|.
+  virtual std::vector<RenderFrameHostImpl*>
+  GetActiveTopLevelDocumentsInBrowsingContextGroup(
+      RenderFrameHostImpl* render_frame_host);
+
  protected:
   virtual ~RenderFrameHostDelegate() = default;
 };
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 68b2d351..5da79c8 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8727,7 +8727,25 @@
   // renderer one. The browser will just "push" the correct value.
   if (navigation_request->state() >=
       NavigationRequest::NavigationState::WILL_PROCESS_RESPONSE) {
-    DCHECK_EQ(params.sandbox_flags, navigation_request->SandboxFlagsToCommit());
+    if (params.sandbox_flags != navigation_request->SandboxFlagsToCommit()) {
+      DCHECK(false);
+
+      base::debug::ScopedCrashKeyString scoped_url(
+          base::debug::AllocateCrashKeyString(
+              "url", base::debug::CrashKeySize::Size256),
+          params.url.possibly_invalid_spec());
+      base::debug::ScopedCrashKeyString scoped_sandbox(
+          base::debug::AllocateCrashKeyString(
+              "sandbox", base::debug::CrashKeySize::Size256),
+          base::StringPrintf(
+              "%u, %u", uint32_t(params.sandbox_flags),
+              uint32_t(navigation_request->SandboxFlagsToCommit())));
+      base::debug::SetCrashKeyString(
+          base::debug::AllocateCrashKeyString(
+              "is_main_frame", base::debug::CrashKeySize::Size32),
+          frame_tree_node_->IsMainFrame() ? "true" : "false");
+      base::debug::DumpWithoutCrashing();
+    }
   }
 
   coep_reporter_ = navigation_request->TakeCoepReporter();
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 6dc4ca9e..adf1c1a 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -4167,7 +4167,7 @@
 
   GURL url = InsecureURL(*embedded_test_server(), "/empty.html");
 
-  content::TestNavigationManager child_navigation_manager(web_contents(), url);
+  TestNavigationManager child_navigation_manager(web_contents(), url);
 
   EXPECT_TRUE(ExecJs(root_frame_host(), R"(
     const iframe = document.createElement("iframe");
@@ -4204,7 +4204,7 @@
 
   GURL url = InsecureURL(*embedded_test_server(), "/empty.html");
 
-  content::TestNavigationManager child_navigation_manager(web_contents(), url);
+  TestNavigationManager child_navigation_manager(web_contents(), url);
 
   EXPECT_TRUE(ExecJs(root_frame_host(), R"(
     const iframe = document.createElement("iframe");
@@ -4224,6 +4224,123 @@
   EXPECT_FALSE(child_frame->GetLastCommittedOrigin().opaque());
 }
 
+// TODO(https://crbug.com/1129326): Revisit this when main-frame navigations are
+// subject to CORS-RFC1918 checks.
+IN_PROC_BROWSER_TEST_F(
+    RenderFrameHostImplBrowserTestWithInsecurePrivateNetworkRequestsBlocked,
+    FormSubmissionFromInsecurePublictoLocalIsNotBlockedInMainFrame) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), InsecureTreatAsPublicAddressURL(*embedded_test_server())));
+
+  GURL url = InsecureDefaultURL(*embedded_test_server());
+  TestNavigationManager navigation_manager(web_contents(), url);
+
+  base::StringPiece script_template = R"(
+    const form = document.createElement("form");
+    form.action = $1;
+    form.method = "post";
+    document.body.appendChild(form);
+    form.submit();
+  )";
+
+  EXPECT_TRUE(ExecJs(root_frame_host(), JsReplace(script_template, url)));
+
+  navigation_manager.WaitForNavigationFinished();
+
+  // Check that the child iframe was not blocked.
+  EXPECT_TRUE(navigation_manager.was_successful());
+
+  EXPECT_EQ(url, EvalJs(root_frame_host(), "document.location.href"));
+  EXPECT_EQ(url, root_frame_host()->GetLastCommittedURL());
+  EXPECT_FALSE(root_frame_host()->GetLastCommittedOrigin().opaque());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    RenderFrameHostImplBrowserTestWithInsecurePrivateNetworkRequestsBlocked,
+    FormSubmissionFromInsecurePublictoLocalIsBlockedInChildFrame) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), InsecureTreatAsPublicAddressURL(*embedded_test_server())));
+
+  GURL url = InsecureDefaultURL(*embedded_test_server());
+  TestNavigationManager navigation_manager(web_contents(), url);
+
+  base::StringPiece script_template = R"(
+    const iframe = document.createElement("iframe");
+    document.body.appendChild(iframe);
+
+    const childDoc = iframe.contentDocument;
+    const form = childDoc.createElement("form");
+    form.action = $1;
+    form.method = "post";
+    childDoc.body.appendChild(form);
+    form.submit();
+  )";
+
+  EXPECT_TRUE(ExecJs(root_frame_host(), JsReplace(script_template, url)));
+
+  navigation_manager.WaitForNavigationFinished();
+
+  // Check that the child iframe was blocked.
+  EXPECT_FALSE(navigation_manager.was_successful());
+
+  ASSERT_EQ(1ul, root_frame_host()->child_count());
+  auto* child_frame = root_frame_host()->child_at(0)->current_frame_host();
+
+  // Failed navigation.
+  EXPECT_EQ(GURL(kUnreachableWebDataURL),
+            EvalJs(child_frame, "document.location.href"));
+
+  // The URL is the form target URL, to allow for reloading.
+  // The origin is opaque though, a symptom of the failed navigation.
+  EXPECT_EQ(url, child_frame->GetLastCommittedURL());
+  EXPECT_TRUE(child_frame->GetLastCommittedOrigin().opaque());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    RenderFrameHostImplBrowserTestWithInsecurePrivateNetworkRequestsBlocked,
+    FormSubmissionGetFromInsecurePublictoLocalIsBlockedInChildFrame) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), InsecureTreatAsPublicAddressURL(*embedded_test_server())));
+
+  GURL target_url = InsecureDefaultURL(*embedded_test_server());
+
+  // The page navigates to `url` followed by an empty query: '?'.
+  GURL expected_url = GURL(target_url.spec() + "?");
+  TestNavigationManager navigation_manager(web_contents(), expected_url);
+
+  base::StringPiece script_template = R"(
+    const iframe = document.createElement("iframe");
+    document.body.appendChild(iframe);
+
+    const childDoc = iframe.contentDocument;
+    const form = childDoc.createElement("form");
+    form.action = $1;
+    form.method = "get";
+    childDoc.body.appendChild(form);
+    form.submit();
+  )";
+
+  EXPECT_TRUE(
+      ExecJs(root_frame_host(), JsReplace(script_template, target_url)));
+
+  navigation_manager.WaitForNavigationFinished();
+
+  // Check that the child iframe was blocked.
+  EXPECT_FALSE(navigation_manager.was_successful());
+
+  ASSERT_EQ(1ul, root_frame_host()->child_count());
+  auto* child_frame = root_frame_host()->child_at(0)->current_frame_host();
+
+  // Failed navigation.
+  EXPECT_EQ(GURL(kUnreachableWebDataURL),
+            EvalJs(child_frame, "document.location.href"));
+
+  // The URL is the form target URL, to allow for reloading.
+  // The origin is opaque though, a symptom of the failed navigation.
+  EXPECT_EQ(expected_url, child_frame->GetLastCommittedURL());
+  EXPECT_TRUE(child_frame->GetLastCommittedOrigin().opaque());
+}
+
 // TODO(https://crbug.com/1134601): `about:` URLs are all treated as `kUnknown`
 // today. This is ~incorrect, but safe, as their web-facing behavior will be
 // equivalent to "public".
diff --git a/content/browser/service_worker/service_worker_context_core_unittest.cc b/content/browser/service_worker/service_worker_context_core_unittest.cc
index 68126309..e94a17a 100644
--- a/content/browser/service_worker/service_worker_context_core_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_core_unittest.cc
@@ -312,7 +312,7 @@
   loop.Run();
 
   // The operation should still complete.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed, status);
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorAbort, status);
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc
index 01eceb12..f0fe587 100644
--- a/content/browser/service_worker/service_worker_registry.cc
+++ b/content/browser/service_worker/service_worker_registry.cc
@@ -202,9 +202,11 @@
     blink::mojom::ServiceWorkerRegistrationOptions options,
     NewRegistrationCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->GetNewRegistrationId(base::BindOnce(
-      &ServiceWorkerRegistry::DidGetNewRegistrationId,
-      weak_factory_.GetWeakPtr(), std::move(options), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::GetNewRegistrationId,
+      base::BindRepeating(&ServiceWorkerRegistry::DidGetNewRegistrationId,
+                          weak_factory_.GetWeakPtr(), base::Passed(&options),
+                          base::Passed(&callback)));
 }
 
 void ServiceWorkerRegistry::CreateNewVersion(
@@ -214,9 +216,11 @@
     NewVersionCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   DCHECK(registration);
-  GetRemoteStorageControl()->GetNewVersionId(base::BindOnce(
-      &ServiceWorkerRegistry::DidGetNewVersionId, weak_factory_.GetWeakPtr(),
-      std::move(registration), script_url, script_type, std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::GetNewVersionId,
+      base::BindRepeating(&ServiceWorkerRegistry::DidGetNewVersionId,
+                          weak_factory_.GetWeakPtr(), registration, script_url,
+                          script_type, base::Passed(&callback)));
 }
 
 void ServiceWorkerRegistry::FindRegistrationForClientUrl(
@@ -230,11 +234,14 @@
   TRACE_EVENT_ASYNC_BEGIN1(
       "ServiceWorker", "ServiceWorkerRegistry::FindRegistrationForClientUrl",
       trace_event_id, "URL", client_url.spec());
-  GetRemoteStorageControl()->FindRegistrationForClientUrl(
-      client_url,
-      base::BindOnce(&ServiceWorkerRegistry::DidFindRegistrationForClientUrl,
-                     weak_factory_.GetWeakPtr(), client_url, trace_event_id,
-                     std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::
+          FindRegistrationForClientUrl,
+      base::BindRepeating(
+          &ServiceWorkerRegistry::DidFindRegistrationForClientUrl,
+          weak_factory_.GetWeakPtr(), client_url, trace_event_id,
+          base::Passed(&callback)),
+      client_url);
 }
 
 void ServiceWorkerRegistry::FindRegistrationForScope(
@@ -258,9 +265,11 @@
     return;
   }
 
-  GetRemoteStorageControl()->FindRegistrationForScope(
-      scope, base::BindOnce(&ServiceWorkerRegistry::DidFindRegistrationForScope,
-                            weak_factory_.GetWeakPtr(), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::FindRegistrationForScope,
+      base::BindRepeating(&ServiceWorkerRegistry::DidFindRegistrationForScope,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback)),
+      scope);
 }
 
 void ServiceWorkerRegistry::FindRegistrationForId(
@@ -281,10 +290,12 @@
     const url::Origin& origin,
     GetRegistrationsCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->GetRegistrationsForOrigin(
-      origin,
-      base::BindOnce(&ServiceWorkerRegistry::DidGetRegistrationsForOrigin,
-                     weak_factory_.GetWeakPtr(), std::move(callback), origin));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::GetRegistrationsForOrigin,
+      base::BindRepeating(&ServiceWorkerRegistry::DidGetRegistrationsForOrigin,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback),
+                          origin),
+      origin);
 }
 
 void ServiceWorkerRegistry::GetStorageUsageForOrigin(
@@ -301,9 +312,11 @@
 void ServiceWorkerRegistry::GetAllRegistrationsInfos(
     GetRegistrationsInfosCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->GetAllRegistrationsDeprecated(
-      base::BindOnce(&ServiceWorkerRegistry::DidGetAllRegistrations,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::
+          GetAllRegistrationsDeprecated,
+      base::BindRepeating(&ServiceWorkerRegistry::DidGetAllRegistrations,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback)));
 }
 
 ServiceWorkerRegistration* ServiceWorkerRegistry::GetUninstallingRegistration(
@@ -422,11 +435,12 @@
   DCHECK(!registration->is_deleted())
       << "attempt to delete a registration twice";
 
-  GetRemoteStorageControl()->DeleteRegistration(
-      registration->id(), origin,
-      base::BindOnce(&ServiceWorkerRegistry::DidDeleteRegistration,
-                     weak_factory_.GetWeakPtr(), registration->id(), origin,
-                     std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::DeleteRegistration,
+      base::BindRepeating(&ServiceWorkerRegistry::DidDeleteRegistration,
+                          weak_factory_.GetWeakPtr(), registration->id(),
+                          origin, base::Passed(&callback)),
+      registration->id(), origin);
 
   DCHECK(!base::Contains(uninstalling_registrations_, registration->id()));
   uninstalling_registrations_[registration->id()] = registration;
@@ -470,10 +484,11 @@
                                                 const GURL& origin,
                                                 StatusCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->UpdateToActiveState(
-      registration_id, origin,
-      base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::UpdateToActiveState,
+      base::BindRepeating(&ServiceWorkerRegistry::DidUpdateRegistration,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback)),
+      static_cast<const int64_t>(registration_id), origin);
 }
 
 void ServiceWorkerRegistry::UpdateLastUpdateCheckTime(
@@ -482,10 +497,12 @@
     base::Time last_update_check_time,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->UpdateLastUpdateCheckTime(
-      registration_id, origin, last_update_check_time,
-      base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::UpdateLastUpdateCheckTime,
+      base::BindRepeating(&ServiceWorkerRegistry::DidUpdateRegistration,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback)),
+      static_cast<const int64_t>(registration_id), origin,
+      static_cast<const base::Time&>(last_update_check_time));
 }
 
 void ServiceWorkerRegistry::UpdateNavigationPreloadEnabled(
@@ -494,10 +511,13 @@
     bool enable,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->UpdateNavigationPreloadEnabled(
-      registration_id, origin, enable,
-      base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::
+          UpdateNavigationPreloadEnabled,
+      base::BindRepeating(&ServiceWorkerRegistry::DidUpdateRegistration,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback)),
+      static_cast<const int64_t>(registration_id), origin,
+      static_cast<const bool>(enable));
 }
 
 void ServiceWorkerRegistry::UpdateNavigationPreloadHeader(
@@ -506,10 +526,12 @@
     const std::string& value,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  GetRemoteStorageControl()->UpdateNavigationPreloadHeader(
-      registration_id, origin, value,
-      base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+  CreateInvokerAndStartRemoteCall(
+      &storage::mojom::ServiceWorkerStorageControl::
+          UpdateNavigationPreloadHeader,
+      base::BindRepeating(&ServiceWorkerRegistry::DidUpdateRegistration,
+                          weak_factory_.GetWeakPtr(), base::Passed(&callback)),
+      static_cast<const int64_t>(registration_id), origin, value);
 }
 
 void ServiceWorkerRegistry::StoreUncommittedResourceId(int64_t resource_id,
@@ -843,9 +865,11 @@
     const GURL& client_url,
     int64_t trace_event_id,
     FindRegistrationCallback callback,
+    uint64_t call_id,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     storage::mojom::ServiceWorkerFindRegistrationResultPtr result) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   if (database_status != storage::mojom::ServiceWorkerDatabaseStatus::kOk &&
       database_status !=
           storage::mojom::ServiceWorkerDatabaseStatus::kErrorNotFound) {
@@ -895,9 +919,11 @@
 
 void ServiceWorkerRegistry::DidFindRegistrationForScope(
     FindRegistrationCallback callback,
+    uint64_t call_id,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     storage::mojom::ServiceWorkerFindRegistrationResultPtr result) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   if (database_status != storage::mojom::ServiceWorkerDatabaseStatus::kOk &&
       database_status !=
           storage::mojom::ServiceWorkerDatabaseStatus::kErrorNotFound) {
@@ -962,10 +988,12 @@
 void ServiceWorkerRegistry::DidGetRegistrationsForOrigin(
     GetRegistrationsCallback callback,
     const url::Origin& origin_filter,
+    uint64_t call_id,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     std::vector<storage::mojom::ServiceWorkerFindRegistrationResultPtr>
         entries) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
 
   blink::ServiceWorkerStatusCode status =
       DatabaseStatusToStatusCode(database_status);
@@ -1004,9 +1032,11 @@
 
 void ServiceWorkerRegistry::DidGetAllRegistrations(
     GetRegistrationsInfosCallback callback,
+    uint64_t call_id,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     RegistrationList registration_data_list) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   blink::ServiceWorkerStatusCode status =
       DatabaseStatusToStatusCode(database_status);
 
@@ -1147,9 +1177,11 @@
     int64_t registration_id,
     const GURL& origin,
     StatusCallback callback,
+    uint64_t call_id,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     ServiceWorkerStorage::OriginState origin_state) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   blink::ServiceWorkerStatusCode status =
       DatabaseStatusToStatusCode(database_status);
 
@@ -1177,8 +1209,10 @@
 
 void ServiceWorkerRegistry::DidUpdateRegistration(
     StatusCallback callback,
+    uint64_t call_id,
     storage::mojom::ServiceWorkerDatabaseStatus status) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   if (status != storage::mojom::ServiceWorkerDatabaseStatus::kOk &&
       status != storage::mojom::ServiceWorkerDatabaseStatus::kErrorNotFound) {
     ScheduleDeleteAndStartOver();
@@ -1264,8 +1298,10 @@
 void ServiceWorkerRegistry::DidGetNewRegistrationId(
     blink::mojom::ServiceWorkerRegistrationOptions options,
     NewRegistrationCallback callback,
+    uint64_t call_id,
     int64_t registration_id) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   if (registration_id == blink::mojom::kInvalidServiceWorkerRegistrationId) {
     std::move(callback).Run(nullptr);
     return;
@@ -1279,10 +1315,12 @@
     const GURL& script_url,
     blink::mojom::ScriptType script_type,
     NewVersionCallback callback,
+    uint64_t call_id,
     int64_t version_id,
     mojo::PendingRemote<storage::mojom::ServiceWorkerLiveVersionRef>
         version_reference) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  FinishRemoteCall(call_id);
   if (version_id == blink::mojom::kInvalidServiceWorkerVersionId) {
     std::move(callback).Run(nullptr);
     return;
diff --git a/content/browser/service_worker/service_worker_registry.h b/content/browser/service_worker/service_worker_registry.h
index 4dbdeec..2b2a37f 100644
--- a/content/browser/service_worker/service_worker_registry.h
+++ b/content/browser/service_worker/service_worker_registry.h
@@ -281,10 +281,12 @@
       const GURL& client_url,
       int64_t trace_event_id,
       FindRegistrationCallback callback,
+      uint64_t call_id,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       storage::mojom::ServiceWorkerFindRegistrationResultPtr result);
   void DidFindRegistrationForScope(
       FindRegistrationCallback callback,
+      uint64_t call_id,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       storage::mojom::ServiceWorkerFindRegistrationResultPtr result);
   void DidFindRegistrationForId(
@@ -296,11 +298,13 @@
   void DidGetRegistrationsForOrigin(
       GetRegistrationsCallback callback,
       const url::Origin& origin_filter,
+      uint64_t call_id,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       std::vector<storage::mojom::ServiceWorkerFindRegistrationResultPtr>
           entries);
   void DidGetAllRegistrations(
       GetRegistrationsInfosCallback callback,
+      uint64_t call_id,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       RegistrationList registration_data_list);
   void DidGetStorageUsageForOrigin(
@@ -320,11 +324,13 @@
       int64_t registration_id,
       const GURL& origin,
       StatusCallback callback,
+      uint64_t call_id,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       ServiceWorkerStorage::OriginState origin_state);
 
   void DidUpdateRegistration(
       StatusCallback callback,
+      uint64_t call_id,
       storage::mojom::ServiceWorkerDatabaseStatus status);
   void DidWriteUncommittedResourceIds(
       storage::mojom::ServiceWorkerDatabaseStatus status);
@@ -349,12 +355,14 @@
   void DidGetNewRegistrationId(
       blink::mojom::ServiceWorkerRegistrationOptions options,
       NewRegistrationCallback callback,
+      uint64_t call_id,
       int64_t registration_id);
   void DidGetNewVersionId(
       scoped_refptr<ServiceWorkerRegistration> registration,
       const GURL& script_url,
       blink::mojom::ScriptType script_type,
       NewVersionCallback callback,
+      uint64_t call_id,
       int64_t version_id,
       mojo::PendingRemote<storage::mojom::ServiceWorkerLiveVersionRef>
           version_reference);
diff --git a/content/browser/service_worker/service_worker_registry_unittest.cc b/content/browser/service_worker/service_worker_registry_unittest.cc
index 951cf42..982a0e4 100644
--- a/content/browser/service_worker/service_worker_registry_unittest.cc
+++ b/content/browser/service_worker/service_worker_registry_unittest.cc
@@ -1290,6 +1290,7 @@
       CreateServiceWorkerRegistrationAndVersion(context(), kScope2, kScriptUrl2,
                                                 /*resource_id=*/2);
 
+  // Store two registrations. Restart the remote storage several times.
   {
     registry()->SimulateStorageRestartForTesting();
 
@@ -1311,10 +1312,184 @@
           loop2.Quit();
         }));
 
+    EXPECT_EQ(inflight_call_count(), 2U);
     registry()->SimulateStorageRestartForTesting();
 
     loop1.Run();
     loop2.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
+  }
+
+  // Finding registrations stored in the previous block.
+  {
+    base::RunLoop loop1;
+    registry()->FindRegistrationForClientUrl(
+        kScope1,
+        base::BindLambdaForTesting(
+            [&](blink::ServiceWorkerStatusCode status,
+                scoped_refptr<ServiceWorkerRegistration> found_registration) {
+              EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+              EXPECT_EQ(found_registration, registration1);
+              loop1.Quit();
+            }));
+
+    base::RunLoop loop2;
+    registry()->FindRegistrationForScope(
+        GURL("http://www.example.com/not-in-scope"),
+        base::BindLambdaForTesting(
+            [&](blink::ServiceWorkerStatusCode status,
+                scoped_refptr<ServiceWorkerRegistration> found_registration) {
+              EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorNotFound);
+              EXPECT_EQ(found_registration, nullptr);
+              loop2.Quit();
+            }));
+
+    EXPECT_EQ(inflight_call_count(), 2U);
+    registry()->SimulateStorageRestartForTesting();
+
+    // TODO(crbug.com/1133143): Add test for FindRegistrationForId().
+
+    loop1.Run();
+    loop2.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
+  }
+
+  // Get both of the registrations by these APIs.
+  {
+    base::RunLoop loop1;
+    registry()->GetRegistrationsForOrigin(
+        kOrigin1,
+        base::BindLambdaForTesting(
+            [&](blink::ServiceWorkerStatusCode status,
+                const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
+                    registrations) {
+              EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+              EXPECT_EQ(registrations.size(), 2U);
+              loop1.Quit();
+            }));
+
+    base::RunLoop loop2;
+    registry()->GetAllRegistrationsInfos(base::BindLambdaForTesting(
+        [&](blink::ServiceWorkerStatusCode status,
+            const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
+          EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+          EXPECT_EQ(registrations.size(), 2U);
+          loop2.Quit();
+        }));
+
+    EXPECT_EQ(inflight_call_count(), 2U);
+    registry()->SimulateStorageRestartForTesting();
+
+    loop1.Run();
+    loop2.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
+  }
+
+  // Delete `registrations` from the storage.
+  {
+    base::RunLoop loop;
+    registry()->DeleteRegistration(
+        registration2, kScope2.GetOrigin(),
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
+          EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+          loop.Quit();
+        }));
+
+    EXPECT_EQ(inflight_call_count(), 1U);
+    registry()->SimulateStorageRestartForTesting();
+
+    loop.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
+  }
+
+  // Update fields of `registration1` in the storage.
+  {
+    base::RunLoop loop1;
+    registry()->UpdateToActiveState(
+        registration1->id(), kScope1.GetOrigin(),
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
+          EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+          loop1.Quit();
+        }));
+
+    base::RunLoop loop2;
+    registry()->UpdateLastUpdateCheckTime(
+        registration1->id(), kScope1.GetOrigin(), base::Time::Now(),
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
+          EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+          loop2.Quit();
+        }));
+
+    base::RunLoop loop3;
+    registry()->UpdateNavigationPreloadEnabled(
+        registration1->id(), kScope1.GetOrigin(), /*enable=*/true,
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
+          EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+          loop3.Quit();
+        }));
+
+    base::RunLoop loop4;
+    registry()->UpdateNavigationPreloadHeader(
+        registration1->id(), kScope1.GetOrigin(), "header",
+        base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
+          EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
+          loop4.Quit();
+        }));
+
+    EXPECT_EQ(inflight_call_count(), 4U);
+    registry()->SimulateStorageRestartForTesting();
+
+    loop1.Run();
+    loop2.Run();
+    loop3.Run();
+    loop4.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
+  }
+}
+
+TEST_F(ServiceWorkerRegistryTest,
+       RetryInflightCalls_CreateNewRegistrationAndVersion) {
+  const GURL kScope("http://www.example.com/scope/");
+  const GURL kScriptUrl("http://www.example.com/script.js");
+
+  scoped_refptr<ServiceWorkerRegistration> registration;
+
+  {
+    blink::mojom::ServiceWorkerRegistrationOptions options;
+    options.scope = kScope;
+    base::RunLoop loop;
+    registry()->CreateNewRegistration(
+        std::move(options),
+        base::BindLambdaForTesting(
+            [&](scoped_refptr<ServiceWorkerRegistration> new_registration) {
+              EXPECT_EQ(new_registration->scope(), kScope);
+              registration = new_registration;
+              loop.Quit();
+            }));
+
+    registry()->SimulateStorageRestartForTesting();
+    EXPECT_EQ(inflight_call_count(), 1U);
+
+    loop.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
+  }
+
+  {
+    base::RunLoop loop;
+    registry()->CreateNewVersion(
+        registration, kScriptUrl, blink::mojom::ScriptType::kClassic,
+        base::BindLambdaForTesting(
+            [&](scoped_refptr<ServiceWorkerVersion> new_version) {
+              EXPECT_EQ(new_version->script_url(), kScriptUrl);
+              EXPECT_EQ(new_version->registration_id(), registration->id());
+              loop.Quit();
+            }));
+
+    registry()->SimulateStorageRestartForTesting();
+    EXPECT_EQ(inflight_call_count(), 1U);
+
+    loop.Run();
+    EXPECT_EQ(inflight_call_count(), 0U);
   }
 }
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 5f9e1190..e3f0f5cb 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -9023,6 +9023,19 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
+                       HeaderPolicyOnXSLTNavigation) {
+  GURL url(embedded_test_server()->GetURL("a.com", "/permissions-policy.xml"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  EXPECT_EQ(CreateParsedFeaturePolicy(
+                {blink::mojom::FeaturePolicyFeature::kGeolocation},
+                {url.GetOrigin()}),
+            root->current_replication_state().feature_policy_header);
+}
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
                        TestPolicyReplicationOnSameOriginNavigation) {
   GURL start_url(
       embedded_test_server()->GetURL("a.com", "/feature-policy1.html"));
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 718f4c2..66018d8 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7024,6 +7024,30 @@
   delegate_->SetContentsBounds(this, bounds);
 }
 
+std::vector<RenderFrameHostImpl*>
+WebContentsImpl::GetActiveTopLevelDocumentsInBrowsingContextGroup(
+    RenderFrameHostImpl* render_frame_host) {
+  std::vector<RenderFrameHostImpl*> out;
+  for (WebContentsImpl* web_contents : GetAllWebContents()) {
+    RenderFrameHostImpl* other_render_frame_host = web_contents->GetMainFrame();
+
+    // Filters out inactive documents.
+    if (other_render_frame_host->lifecycle_state() !=
+        RenderFrameHostImpl::LifecycleState::kActive) {
+      continue;
+    }
+
+    // Filters out documents from a different browsing context group.
+    if (!render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
+            other_render_frame_host->GetSiteInstance())) {
+      continue;
+    }
+
+    out.push_back(other_render_frame_host);
+  }
+  return out;
+}
+
 void WebContentsImpl::DidStartLoading(FrameTreeNode* frame_tree_node,
                                       bool to_different_document) {
   OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::DidStartLoading",
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 03778c5..3db90d0 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -765,6 +765,9 @@
       RenderFrameHostImpl::LifecycleState old_state,
       RenderFrameHostImpl::LifecycleState new_state) override;
   void SetWindowRect(const gfx::Rect& new_bounds) override;
+  std::vector<RenderFrameHostImpl*>
+  GetActiveTopLevelDocumentsInBrowsingContextGroup(
+      RenderFrameHostImpl* render_frame_host) override;
 
   // RenderViewHostDelegate ----------------------------------------------------
   RenderViewHostDelegateView* GetDelegateView() override;
diff --git a/content/test/data/permissions-policy.xml b/content/test/data/permissions-policy.xml
new file mode 100644
index 0000000..cbf667f
--- /dev/null
+++ b/content/test/data/permissions-policy.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="permissions-policy.xslt" type="text/xsl"?>
+<!-- This page has a 'Permissions-Policy' header. -->
+<page>
+</page>
diff --git a/content/test/data/permissions-policy.xml.mock-http-headers b/content/test/data/permissions-policy.xml.mock-http-headers
new file mode 100644
index 0000000..a17e049
--- /dev/null
+++ b/content/test/data/permissions-policy.xml.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Permissions-Policy: geolocation=self
\ No newline at end of file
diff --git a/content/test/data/permissions-policy.xslt b/content/test/data/permissions-policy.xslt
new file mode 100644
index 0000000..50c6e66c
--- /dev/null
+++ b/content/test/data/permissions-policy.xslt
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+              xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="page">
+  <html></html>
+</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index 7039af0..4823598 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -22,6 +22,8 @@
 _SUPPORTED_WIN_VERSIONS = ['win7', 'win10']
 _SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION = ['win10']
 _SUPPORTED_WIN_GPU_VENDORS = [0x8086, 0x10de, 0x1002]
+_SUPPORTED_WIN_AMD_GPUS = [0x6613, 0x699f, 0x7340]
+_SUPPORTED_WIN_AMD_GPUS_WITH_NV12_OVERLAYS = [0x7340]
 _SUPPORTED_WIN_INTEL_GPUS = [0x5912, 0x3e92]
 _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS = [0x5912, 0x3e92]
 _SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS = [0x5912, 0x3e92]
@@ -458,7 +460,11 @@
       config['supports_overlays'] = True
       config['yuy2_overlay_support'] = 'SOFTWARE'
       config['nv12_overlay_support'] = 'SOFTWARE'
-      if gpu_vendor_id == 0x8086:
+      if gpu_vendor_id == 0x1002:
+        assert gpu_device_id in _SUPPORTED_WIN_AMD_GPUS
+        if gpu_device_id in _SUPPORTED_WIN_AMD_GPUS_WITH_NV12_OVERLAYS:
+          config['nv12_overlay_support'] = 'SCALING'
+      elif gpu_vendor_id == 0x8086:
         assert gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS
         gpu_device_and_driver = ('%x-' + gpu.driver_version) % gpu_device_id
         if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS:
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 2fd788522..fab3f804 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -72,7 +72,7 @@
     self.restart_browser_after_test = restart_browser_after_test
     # These are used to pass additional arguments to the test harness.
     # VideoPathTraceTest and OverlayModeTest support the following boolean
-    # arguments: pixel_format, zero_copy, no_overlay, and presentation_mode.
+    # arguments: pixel_format, zero_copy, no_overlay and video_is_rotated.
     self.other_args = other_args
     # This allows a newly added test to be exempted from failures for a
     # (hopefully) short period after being added. This is so that any slightly
@@ -764,21 +764,21 @@
                       '_DirectComposition_Video_MP4_FourColors_Rot_90',
                       test_rect=[0, 0, 270, 240],
                       browser_args=browser_args,
-                      other_args={'presentation_mode': 'COMPOSED'},
+                      other_args={'video_is_rotated': True},
                       matching_algorithm=strict_dc_sobel_algorithm),
         PixelTestPage('pixel_video_mp4_four_colors_rot_180.html',
                       base_name +
                       '_DirectComposition_Video_MP4_FourColors_Rot_180',
                       test_rect=[0, 0, 240, 135],
                       browser_args=browser_args,
-                      other_args={'presentation_mode': 'COMPOSED'},
+                      other_args={'video_is_rotated': True},
                       matching_algorithm=strict_dc_sobel_algorithm),
         PixelTestPage('pixel_video_mp4_four_colors_rot_270.html',
                       base_name +
                       '_DirectComposition_Video_MP4_FourColors_Rot_270',
                       test_rect=[0, 0, 270, 240],
                       browser_args=browser_args,
-                      other_args={'presentation_mode': 'COMPOSED'},
+                      other_args={'video_is_rotated': True},
                       matching_algorithm=strict_dc_sobel_algorithm),
         PixelTestPage('pixel_video_vp9.html',
                       base_name + '_DirectComposition_Video_VP9',
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index dbb3dd1d..97b8f51 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
index 2eb5a5a1..7da651cbc 100644
--- a/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/depth_capture_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 43a39a16..a18abf2e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index 60d1a298..84505ac 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index 0d839c0..cd0863e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
index c95443e..1cbc0c8 100644
--- a/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/maps_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 10647a7..d1623f1 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
@@ -435,3 +435,7 @@
 crbug.com/1129879 [ android ] Pixel_Video_Media_Stream_Incompatible_Stride [ Skip ]
 crbug.com/1129879 [ chromeos ] Pixel_Video_Media_Stream_Incompatible_Stride [ Skip ]
 crbug.com/1129879 [ fuchsia ] Pixel_Video_Media_Stream_Incompatible_Stride [ Skip ]
+
+# Produce incorrect image on Win10 AMD RX 5500 XT
+crbug.com/1151797 [ win10 amd-0x7340 skia-renderer-gl ] Pixel_DirectComposition_Video_VP9_YUY2 [ Failure ]
+crbug.com/1151797 [ win10 amd-0x7340 skia-renderer-gl ] Pixel_DirectComposition_Video_VP9_BGRA [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index fea271bc..d0cd708 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index 06e0d5c..9ac4cbc1 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index 8d4debd..3588d61 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
@@ -185,3 +185,6 @@
 # YUY2 swap chains don't work on AMD.
 crbug.com/1132381 [ win amd-0x699f ] VideoPathTraceTest_DirectComposition_Video_MP4_YUY2 [ Failure ]
 crbug.com/1132381 [ win amd-0x699f ] VideoPathTraceTest_DirectComposition_Video_VP9_YUY2 [ Failure ]
+# Failed to create YUY2 swap chain on Win10 AMD RX 5500 XT
+crbug.com/1151767 [ win10 amd-0x7340 ] VideoPathTraceTest_DirectComposition_Video_MP4_YUY2 [ Failure ]
+crbug.com/1151767 [ win10 amd-0x7340 ] VideoPathTraceTest_DirectComposition_Video_VP9_YUY2 [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 1850c83..4c6b151 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
@@ -336,6 +336,18 @@
 # Failing on AMD RX 550
 crbug.com/angleproject/3354 [ win amd-0x699f ] deqp/functional/gles3/fborender/shared_colorbuffer_02.html [ Skip ]
 
+# Failing on AMD RX 5500 XT
+crbug.com/1152597 [ win amd-0x7340 angle-d3d11 ] conformance/renderbuffers/framebuffer-state-restoration.html [ Failure ]
+crbug.com/1152599 [ win amd-0x7340 angle-d3d11 ] conformance/rendering/polygon-offset.html [ Failure ]
+crbug.com/1152602 [ win amd-0x7340 angle-d3d11 ] conformance/canvas/drawingbuffer-static-canvas-test.html [ RetryOnFailure ]
+crbug.com/1152603 [ win amd-0x7340 angle-d3d11 ] conformance2/renderbuffers/multisampled-depth-renderbuffer-initialization.html [ Failure ]
+crbug.com/1152606 [ win amd-0x7340 angle-d3d11 ] deqp/functional/gles3/fboinvalidate/* [ Failure ]
+crbug.com/1152606 [ win amd-0x7340 angle-d3d11 ] deqp/functional/gles3/fbomultisample.2_samples.html [ Failure ]
+crbug.com/1152606 [ win amd-0x7340 angle-d3d11 ] deqp/functional/gles3/fborender/shared_colorbuffer_01.html [ Failure ]
+crbug.com/1152606 [ win amd-0x7340 angle-d3d11 ] deqp/functional/gles3/fborender/shared_colorbuffer_02.html [ Failure ]
+crbug.com/1152606 [ win amd-0x7340 angle-d3d11 ] deqp/functional/gles3/fragdepth.html [ Failure ]
+crbug.com/1152606 [ win amd-0x7340 angle-d3d11 ] deqp/functional/gles3/framebufferblit/depth_stencil.html [ Failure ]
+
 # Flaky on AMD Win7
 crbug.com/989050 [ angle-d3d11 win7 amd ] conformance2/textures/misc/tex-unpack-params-imagedata.html [ RetryOnFailure ]
 
@@ -891,6 +903,12 @@
 crbug.com/809237 [ linux amd-0x6613 ] conformance2/uniforms/incompatible-texture-type-for-sampler.html [ Skip ]
 crbug.com/1018028 [ linux amd-0x6613 ] conformance/rendering/bind-framebuffer-flush-bug.html [ Failure ]
 
+# Linux AMD RX 5500 XT
+crbug.com/1147232 [ linux amd-0x7340 no-passthrough ] conformance/textures/misc/texture-size-limit.html [ Skip ]
+crbug.com/1152588 [ linux amd-0x7340 ] conformance2/rendering/multisampling-fragment-evaluation.html [ Failure ]
+crbug.com/1152590 [ linux amd-0x7340 no-passthrough ] conformance2/state/gl-get-calls.html [ Failure ]
+crbug.com/1152590 [ linux amd-0x7340 no-passthrough ] deqp/functional/gles3/integerstatequery.html [ Failure ]
+
 ####################
 # Android failures #
 ####################
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 01c413b..6e4f8cb 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -21,7 +21,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
@@ -236,7 +236,9 @@
 crbug.com/979444 [ chromeos ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ linux ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ mac no-passthrough no-asan ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
-crbug.com/979444 [ win ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/979444 [ win intel ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/979444 [ win nvidia ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/979444 [ win7 amd ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ android ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 
 # TODO(crbug.com/1097338): simplify this expectation once fuchsia case is fixed
@@ -366,6 +368,15 @@
 # Win / AMD D3D9 failures
 crbug.com/475095 [ win amd angle-d3d9 ] conformance/extensions/angle-instanced-arrays.html [ Failure ]
 
+# Win / AMD RX 5500 XT failures
+crbug.com/1152597 [ win amd-0x7340 angle-d3d11 ] conformance/renderbuffers/framebuffer-state-restoration.html [ Failure ]
+crbug.com/1152599 [ win amd-0x7340 angle-d3d11 ] conformance/rendering/polygon-offset.html [ Failure ]
+crbug.com/1152602 [ win amd-0x7340 angle-d3d11 ] conformance/canvas/drawingbuffer-static-canvas-test.html [ RetryOnFailure ]
+crbug.com/1152619 [ win10 amd-0x7340 angle-d3d9 ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
+crbug.com/1152619 [ win amd-0x7340 angle-d3d9 ] conformance/textures/misc/texture-npot-video.html [ Failure ]
+crbug.com/1152621 [ win amd-0x7340 angle-vulkan ] conformance/attribs/gl-vertexattribpointer-offsets.html [ Failure ]
+crbug.com/1152623 [ win amd-0x7340 angle-vulkan ] conformance/extensions/webgl-draw-buffers.html [ Failure ]
+
 # Win / D3D9 failures
 # Skipping these two tests because they're causing assertion failures.
 crbug.com/angleproject/896 [ win angle-d3d9 no-passthrough ] conformance/extensions/oes-texture-float-with-canvas.html [ Skip ]
@@ -565,6 +576,9 @@
 crbug.com/642822 [ linux amd ] conformance/rendering/clipping-wide-points.html [ Failure ]
 crbug.com/1018028 [ linux amd-0x6613 ] conformance/rendering/bind-framebuffer-flush-bug.html [ Failure ]
 
+# Linux AMD RX 5500 XT
+crbug.com/1147232 [ linux amd-0x7340 no-passthrough ] conformance/textures/misc/texture-size-limit.html [ Skip ]
+
 # Linux passthrough AMD
 crbug.com/1143392 [ linux amd passthrough ] conformance/glsl/misc/shader-with-non-reserved-words.html [ Failure ]
 crbug.com/998498 [ linux amd passthrough ] conformance/textures/misc/tex-input-validation.html [ Failure ]
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py
index 0318384..0f7926e 100644
--- a/content/test/gpu/gpu_tests/trace_integration_test.py
+++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -88,6 +88,8 @@
 _PRESENT_MAIN_SWAP_CHAIN_EVENT_NAME =\
     'DirectCompositionChildSurfaceWin::PresentSwapChain'
 
+_SUPPORTED_WIN_AMD_GPUS_WITH_NV12_ROTATED_OVERLAYS = [0x7340]
+
 
 class _TraceTestArguments(object):
   """Struct-like object for passing trace test arguments instead of dicts."""
@@ -285,7 +287,7 @@
     expected.zero_copy = other_args.get('zero_copy', None)
     expected.pixel_format = other_args.get('pixel_format', None)
     expected.no_overlay = other_args.get('no_overlay', False)
-    expected.presentation_mode = other_args.get('presentation_mode', None)
+    video_is_rotated = other_args.get('video_is_rotated', False)
 
     if overlay_bot_config.get('supports_overlays', False):
       supports_hw_nv12_overlays = overlay_bot_config[
@@ -304,18 +306,25 @@
           assert supports_sw_nv12_overlays
           expected.pixel_format = 'NV12'
 
-      if expected.presentation_mode is None:
-        if supports_hw_nv12_overlays or supports_hw_yuy2_overlays:
-          expected.presentation_mode = 'OVERLAY'
-        else:
-          expected.presentation_mode = 'COMPOSED'
+      gpu = self.browser.GetSystemInfo().gpu.devices[0]
+      supports_rotated_video_overlays = (
+          gpu.vendor_id == 0x1002 and
+          gpu.device_id in _SUPPORTED_WIN_AMD_GPUS_WITH_NV12_ROTATED_OVERLAYS)
+
+      if (((supports_hw_nv12_overlays and expected.pixel_format == 'NV12')
+           or supports_hw_yuy2_overlays)
+          and (not video_is_rotated or supports_rotated_video_overlays)):
+        expected.presentation_mode = 'OVERLAY'
+      else:
+        expected.presentation_mode = 'COMPOSED'
 
       if expected.zero_copy is None:
         # TODO(sunnyps): Check for overlay scaling support after making the same
         # change in SwapChainPresenter.
         expected.zero_copy = (expected.presentation_mode == 'OVERLAY'
                               and expected.pixel_format == 'NV12'
-                              and supports_hw_nv12_overlays)
+                              and supports_hw_nv12_overlays
+                              and not video_is_rotated)
 
     return expected
 
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index b4ff2621..a3d028b 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -32,7 +32,7 @@
 #         debug debug-x64
 #         release release-x64 ]
 # GPU
-# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f
+# tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 amd-0x699f amd-0x7340
 #         apple apple-apple-a12z
 #         arm
 #         google google-0xffff
diff --git a/docs/origin_trials_integration.md b/docs/origin_trials_integration.md
index 2ca187f..f17efb9 100644
--- a/docs/origin_trials_integration.md
+++ b/docs/origin_trials_integration.md
@@ -7,6 +7,11 @@
 
 ## Code Changes
 
+NOTE: You can land these code changes before requesting to run an origin trial.
+These code changes make it possible to control a feature via an origin trial,
+but don't require an origin trial to be approved. For more on the process, see
+[Running an Origin Trial].
+
 ### Runtime Enabled Features
 
 First, you’ll need to configure [runtime\_enabled\_features.json5]. This is
@@ -186,6 +191,14 @@
       tools/origin_trials/generate_token.py http://localhost:8000 MyFeature
       ```
 
+   There are additional flags to generate third-party tokens, set the expiry
+   date, and control other options. See the command help for details (`--help`).
+   For example, to generate a third-party token, with [user subset exclusion]:
+
+      ```
+      tools/origin_trials/generate_token.py --is-third-party --usage-restriction=subset http://localhost:8000 MyFeature
+      ```
+
 2. Copy the token from the end of the output and use it in a `<meta>` tag or
    an `Origin-Trial` header as described in the [Developer Guide].
 
@@ -236,4 +249,5 @@
 [css\_properties.json5]: /third_party/blink/renderer/core/css/css_properties.json5
 [origin-trial-test-property]: https://chromium.googlesource.com/chromium/src/+/ff2ab8b89745602c8300322c2a0158e210178c7e/third_party/blink/renderer/core/css/css_properties.json5#2635
 [CSSStyleDeclaration]: /third_party/blink/renderer/core/css/css_style_declaration.idl
-
+[Running an Origin Trial]: https://www.chromium.org/blink/origin-trials/running-an-origin-trial
+[user subset exclusion]: https://docs.google.com/document/d/1xALH9W7rWmX0FpjudhDeS2TNTEOXuPn4Tlc9VmuPdHA/edit#heading=h.myaz1twlipw
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
index 354d494..e68d819 100644
--- a/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_gl_texture.cc
@@ -253,8 +253,9 @@
     return nullptr;
   }
 
+  const gfx::GpuMemoryBufferType handle_type = handle.type;
   GLenum target =
-      (handle.type == gfx::SHARED_MEMORY_BUFFER ||
+      (handle_type == gfx::SHARED_MEMORY_BUFFER ||
        !NativeBufferNeedsPlatformSpecificTextureTarget(buffer_format))
           ? GL_TEXTURE_2D
           : gpu::GetPlatformSpecificTextureTarget();
@@ -279,7 +280,7 @@
   texture_2d_support =
       (gpu::GetPlatformSpecificTextureTarget() == GL_TEXTURE_2D);
 #endif  // defined(OS_MAC)
-  DCHECK(handle.type == gfx::SHARED_MEMORY_BUFFER || target != GL_TEXTURE_2D ||
+  DCHECK(handle_type == gfx::SHARED_MEMORY_BUFFER || target != GL_TEXTURE_2D ||
          texture_2d_support || image->ShouldBindOrCopy() == gl::GLImage::BIND);
 #endif  // DCHECK_IS_ON()
   if (color_space.IsValid())
diff --git a/ios/chrome/app/startup/chrome_app_startup_parameters.mm b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
index 0f7e23a..8b9de6a 100644
--- a/ios/chrome/app/startup/chrome_app_startup_parameters.mm
+++ b/ios/chrome/app/startup/chrome_app_startup_parameters.mm
@@ -556,7 +556,7 @@
 
   if (![_declaredSourceApp length]) {
     if (self.completeURL.SchemeIs(url::kHttpScheme) ||
-        self.completeURL.SchemeIs(url::kHttpScheme)) {
+        self.completeURL.SchemeIs(url::kHttpsScheme)) {
       // If Chrome is opened via the system default browser mechanism, the
       // action should be differentiated from the case where the source is
       // unknown.
diff --git a/ios/chrome/browser/link_to_text/BUILD.gn b/ios/chrome/browser/link_to_text/BUILD.gn
index 1006f36..4f138b9c 100644
--- a/ios/chrome/browser/link_to_text/BUILD.gn
+++ b/ios/chrome/browser/link_to_text/BUILD.gn
@@ -14,6 +14,7 @@
   deps = [
     ":internal",
     "//components/shared_highlighting/core/common",
+    "//components/ukm/ios:ukm_url_recorder",
     "//ios/chrome/browser/tabs",
     "//ios/web/public",
     "//ios/web/public/js_messaging",
diff --git a/ios/chrome/browser/link_to_text/link_to_text_response.h b/ios/chrome/browser/link_to_text/link_to_text_response.h
index d752e79..400340b 100644
--- a/ios/chrome/browser/link_to_text/link_to_text_response.h
+++ b/ios/chrome/browser/link_to_text/link_to_text_response.h
@@ -30,8 +30,8 @@
 
 // Parses a serialized response stored in |value| into a LinkToTextResponse
 // instance.
-+ (instancetype)createFromValue:(const base::Value*)value
-                       webState:(web::WebState*)webState;
++ (instancetype)linkToTextResponseWithValue:(const base::Value*)value
+                                   webState:(web::WebState*)webState;
 
 // Response payload. Nil when an error occurred.
 @property(nonatomic, readonly) LinkToTextPayload* payload;
@@ -42,6 +42,9 @@
     base::Optional<shared_highlighting::LinkGenerationError>
         error;
 
+// Source ID for the associated WebState.
+@property(nonatomic, readonly) ukm::SourceId sourceID;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_LINK_TO_TEXT_LINK_TO_TEXT_RESPONSE_H_
diff --git a/ios/chrome/browser/link_to_text/link_to_text_response.mm b/ios/chrome/browser/link_to_text/link_to_text_response.mm
index 6c336454..0c31be0 100644
--- a/ios/chrome/browser/link_to_text/link_to_text_response.mm
+++ b/ios/chrome/browser/link_to_text/link_to_text_response.mm
@@ -8,6 +8,7 @@
 #import "base/values.h"
 #import "components/shared_highlighting/core/common/text_fragment.h"
 #import "components/shared_highlighting/core/common/text_fragments_utils.h"
+#import "components/ukm/ios/ukm_url_recorder.h"
 #import "ios/chrome/browser/link_to_text/link_to_text_payload.h"
 #import "ios/chrome/browser/link_to_text/link_to_text_utils.h"
 #import "ios/chrome/browser/tabs/tab_title_util.h"
@@ -24,50 +25,72 @@
 
 @interface LinkToTextResponse ()
 
-// Initializes an object with the given |payload| of the link generation
-// request.
-- (instancetype)initWithPayload:(LinkToTextPayload*)payload
+// Initializes an object with a |sourceID| representing the current WebState.
+- (instancetype)initWithSourceID:(ukm::SourceId)sourceID
     NS_DESIGNATED_INITIALIZER;
 
+// Initializes an object with the given |payload| of the link generation
+// request, and a |sourceID| representing the current WebState.
+- (instancetype)initWithPayload:(LinkToTextPayload*)payload
+                       sourceID:(ukm::SourceId)sourceID;
+
 // Initializes an object with the given |error| which occurred while trying to
-// generate a link.
-- (instancetype)initWithError:(LinkGenerationError)error;
+// generate a link, and a |sourceID| representing the current WebState.
+- (instancetype)initWithError:(LinkGenerationError)error
+                     sourceID:(ukm::SourceId)sourceID;
 
 @end
 
 @implementation LinkToTextResponse
 
-- (instancetype)initWithPayload:(LinkToTextPayload*)payload {
+- (instancetype)initWithSourceID:(ukm::SourceId)sourceID {
   if (self = [super init]) {
-    // Payload may be nil in cases of link generation error.
+    _sourceID = sourceID;
+  }
+  return self;
+}
+
+- (instancetype)initWithPayload:(LinkToTextPayload*)payload
+                       sourceID:(ukm::SourceId)sourceID {
+  DCHECK(payload);
+  DCHECK(sourceID != ukm::kInvalidSourceId);
+  if (self = [self initWithSourceID:sourceID]) {
     _payload = payload;
   }
   return self;
 }
 
-- (instancetype)initWithError:(LinkGenerationError)error {
-  if (self = [self initWithPayload:nil]) {
+- (instancetype)initWithError:(LinkGenerationError)error
+                     sourceID:(ukm::SourceId)sourceID {
+  if (self = [self initWithSourceID:sourceID]) {
     _error = error;
   }
   return self;
 }
 
-+ (instancetype)createFromValue:(const base::Value*)value
-                       webState:(web::WebState*)webState {
-  if (!webState || !link_to_text::IsValidDictValue(value)) {
-    return [LinkToTextResponse unknownError];
++ (instancetype)linkToTextResponseWithValue:(const base::Value*)value
+                                   webState:(web::WebState*)webState {
+  if (!webState) {
+    return [LinkToTextResponse linkToTextResponseWithUnknownError];
+  }
+
+  ukm::SourceId sourceID = ukm::GetSourceIdForWebStateDocument(webState);
+
+  if (!link_to_text::IsValidDictValue(value)) {
+    return [self linkToTextResponseWithUnknownErrorAndSourceID:sourceID];
   }
 
   base::Optional<LinkGenerationOutcome> outcome =
       link_to_text::ParseStatus(value->FindDoubleKey("status"));
   if (!outcome.has_value()) {
-    return [LinkToTextResponse unknownError];
+    return [self linkToTextResponseWithUnknownErrorAndSourceID:sourceID];
   }
 
   if (outcome.value() != LinkGenerationOutcome::kSuccess) {
     // Convert to Error.
-    return [[LinkToTextResponse alloc]
-        initWithError:link_to_text::OutcomeToError(outcome.value())];
+    return [[self alloc]
+        initWithError:link_to_text::OutcomeToError(outcome.value())
+             sourceID:sourceID];
   }
 
   // Attempts to parse a payload from the response.
@@ -81,7 +104,7 @@
   // All values must be present to have a valid payload.
   if (!title || !fragment || !selectedText || !sourceRect) {
     // Library replied Success but some values are missing.
-    return [LinkToTextResponse unknownError];
+    return [self linkToTextResponseWithUnknownErrorAndSourceID:sourceID];
   }
 
   // Create the deep-link.
@@ -95,14 +118,20 @@
         sourceView:webState->GetView()
         sourceRect:link_to_text::ConvertToBrowserRect(sourceRect.value(),
                                                       webState)];
-  return [[LinkToTextResponse alloc] initWithPayload:payload];
+  return [[self alloc] initWithPayload:payload sourceID:sourceID];
 }
 
 #pragma mark - Private
 
-+ (instancetype)unknownError {
-  return
-      [[LinkToTextResponse alloc] initWithError:LinkGenerationError::kUnknown];
++ (instancetype)linkToTextResponseWithUnknownError {
+  return [[self alloc] initWithError:LinkGenerationError::kUnknown
+                            sourceID:ukm::kInvalidSourceId];
+}
+
++ (instancetype)linkToTextResponseWithUnknownErrorAndSourceID:
+    (ukm::SourceId)sourceID {
+  return [[self alloc] initWithError:LinkGenerationError::kUnknown
+                            sourceID:sourceID];
 }
 
 @end
diff --git a/ios/chrome/browser/link_to_text/link_to_text_tab_helper.mm b/ios/chrome/browser/link_to_text/link_to_text_tab_helper.mm
index cbb0698..f978804 100644
--- a/ios/chrome/browser/link_to_text/link_to_text_tab_helper.mm
+++ b/ios/chrome/browser/link_to_text/link_to_text_tab_helper.mm
@@ -59,7 +59,8 @@
     LinkToTextCallback callback,
     const base::Value* response) {
   if (callback) {
-    callback([LinkToTextResponse createFromValue:response webState:web_state_]);
+    callback([LinkToTextResponse linkToTextResponseWithValue:response
+                                                    webState:web_state_]);
   }
 }
 
diff --git a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
index ac036faa..1be5f61 100644
--- a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
@@ -114,6 +114,7 @@
     _metricsRecorder =
         [[InfobarMetricsRecorder alloc] initWithType:infobarType];
     _presentsModal = presentsModal;
+    _useIconBackgroundTint = YES;
   }
   return self;
 }
@@ -147,6 +148,12 @@
   // Icon setup.
   UIView* iconContainerView = nil;
   if (self.iconImage) {
+    // If the icon image requires a background tint, ignore the original color
+    // information and draw the image as a template image.
+    if (self.useIconBackgroundTint) {
+      self.iconImage = [self.iconImage
+          imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+    }
     UIImageView* iconImageView =
         [[UIImageView alloc] initWithImage:self.iconImage];
     iconImageView.contentMode = UIViewContentModeScaleAspectFit;
@@ -155,15 +162,10 @@
     UIView* backgroundIconView =
         [[UIView alloc] initWithFrame:iconImageView.frame];
     backgroundIconView.layer.cornerRadius = kIconCornerRadius;
-    backgroundIconView.translatesAutoresizingMaskIntoConstraints = NO;
-
-    // If the icon image requires a background tint, ignore the original color
-    // information and draw the image as a template image.
     if (self.useIconBackgroundTint) {
-      self.iconImage = [self.iconImage
-          imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
       backgroundIconView.backgroundColor = [UIColor colorNamed:kBlueHaloColor];
     }
+    backgroundIconView.translatesAutoresizingMaskIntoConstraints = NO;
 
     iconContainerView = [[UIView alloc] init];
     [iconContainerView addSubview:backgroundIconView];
diff --git a/ios/chrome/browser/ui/link_to_text/BUILD.gn b/ios/chrome/browser/ui/link_to_text/BUILD.gn
index ef619ad..5086f90 100644
--- a/ios/chrome/browser/ui/link_to_text/BUILD.gn
+++ b/ios/chrome/browser/ui/link_to_text/BUILD.gn
@@ -28,6 +28,8 @@
     "//base",
     "//base/test:test_support",
     "//components/shared_highlighting/core/common",
+    "//components/ukm:test_support",
+    "//components/ukm/ios:ukm_url_recorder",
     "//ios/chrome/browser/link_to_text",
     "//ios/chrome/browser/link_to_text:internal",
     "//ios/chrome/browser/ui:feature_flags",
diff --git a/ios/chrome/browser/ui/link_to_text/link_to_text_mediator.mm b/ios/chrome/browser/ui/link_to_text/link_to_text_mediator.mm
index 38f24eda..4968d8d 100644
--- a/ios/chrome/browser/ui/link_to_text/link_to_text_mediator.mm
+++ b/ios/chrome/browser/ui/link_to_text/link_to_text_mediator.mm
@@ -66,8 +66,12 @@
 - (void)receivedLinkToTextResponse:(LinkToTextResponse*)response {
   DCHECK(response);
   if (response.error.has_value()) {
-    [self linkGenerationFailedWithError:response.error.value()];
+    LinkGenerationError error = response.error.value();
+    shared_highlighting::LogLinkGeneratedErrorUkmEvent(response.sourceID,
+                                                       error);
+    [self linkGenerationFailedWithError:error];
   } else {
+    shared_highlighting::LogLinkGeneratedSuccessUkmEvent(response.sourceID);
     [self shareLinkToText:response.payload];
   }
 }
diff --git a/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm b/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm
index 3fd940d..4adc93c2a 100644
--- a/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm
@@ -16,6 +16,8 @@
 #import "base/values.h"
 #import "components/shared_highlighting/core/common/shared_highlighting_metrics.h"
 #import "components/shared_highlighting/core/common/text_fragment.h"
+#import "components/ukm/ios/ukm_url_recorder.h"
+#import "components/ukm/test_ukm_recorder.h"
 #import "ios/chrome/browser/link_to_text/link_generation_outcome.h"
 #import "ios/chrome/browser/link_to_text/link_to_text_payload.h"
 #import "ios/chrome/browser/link_to_text/link_to_text_tab_helper.h"
@@ -24,12 +26,14 @@
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
+#import "ios/web/public/test/fakes/fake_navigation_context.h"
 #import "ios/web/public/test/fakes/fake_web_frame.h"
 #import "ios/web/public/test/fakes/fake_web_frames_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "ios/web/public/test/web_task_environment.h"
 #import "ios/web/public/ui/crw_web_view_proxy.h"
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
+#import "services/metrics/public/cpp/ukm_builders.h"
 #import "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
@@ -51,6 +55,9 @@
 const char kTestBaseURL[] = "https://www.chromium.org/";
 const TextFragment kTestTextFragment = TextFragment("selected text");
 
+const char kSuccessUkmMetric[] = "Success";
+const char kErrorUkmMetric[] = "Error";
+
 class TestWebStateListDelegate : public WebStateListDelegate {
   void WillAddWebState(web::WebState* web_state) override {}
   void WebStateDetached(web::WebState* web_state) override {}
@@ -90,6 +97,14 @@
     web_state_->SetWebViewProxy(mocked_webview_proxy);
     web_state_->SetCurrentURL(GURL(kTestBaseURL));
 
+    // Fake Navigation End for UKM setup.
+    ukm::InitializeSourceUrlRecorderForWebState(web_state_);
+    web::FakeNavigationContext context;
+    context.SetHasCommitted(true);
+    context.SetIsSameDocument(false);
+    web_state_->OnNavigationStarted(&context);
+    web_state_->OnNavigationFinished(&context);
+
     LinkToTextTabHelper::CreateForWebState(web_state_);
 
     mediator_ =
@@ -139,11 +154,33 @@
     return response_value;
   }
 
+  void ValidateLinkGeneratedSuccessUkm() {
+    auto entries = ukm_recorder_.GetEntriesByName(
+        ukm::builders::SharedHighlights_LinkGenerated::kEntryName);
+    ASSERT_EQ(1u, entries.size());
+    const ukm::mojom::UkmEntry* entry = entries[0];
+    EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+    ukm_recorder_.ExpectEntryMetric(entry, kSuccessUkmMetric, true);
+    EXPECT_FALSE(ukm_recorder_.GetEntryMetric(entry, kErrorUkmMetric));
+  }
+
+  void ValidateLinkGeneratedErrorUkm(LinkGenerationError error) {
+    auto entries = ukm_recorder_.GetEntriesByName(
+        ukm::builders::SharedHighlights_LinkGenerated::kEntryName);
+    ASSERT_EQ(1u, entries.size());
+    const ukm::mojom::UkmEntry* entry = entries[0];
+    EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+    ukm_recorder_.ExpectEntryMetric(entry, kSuccessUkmMetric, false);
+    ukm_recorder_.ExpectEntryMetric(entry, kErrorUkmMetric,
+                                    static_cast<int64_t>(error));
+  }
+
   web::WebTaskEnvironment task_environment_;
   base::test::ScopedFeatureList feature_list_;
   TestWebStateListDelegate web_state_list_delegate_;
   WebStateList web_state_list_;
   TestWebState* web_state_;
+  ukm::TestAutoSetUkmRecorder ukm_recorder_;
   web::FakeWebFramesManager* web_frames_manager_;
   web::FakeWebFrame* main_frame_;
   UIView* fake_view_;
@@ -196,6 +233,7 @@
   // Make sure the correct metric were recorded.
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", true,
                                       1);
+  ValidateLinkGeneratedSuccessUkm();
 }
 
 // Tests that the shareHighlight command is triggered with the right parameters
@@ -238,6 +276,7 @@
   // Make sure the correct metric were recorded.
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", true,
                                       1);
+  ValidateLinkGeneratedSuccessUkm();
 }
 
 // Tests that the consumer is informed of a failure to generate a link when an
@@ -264,11 +303,12 @@
   [mocked_consumer_ verify];
 
   // Make sure the correct metric were recorded.
+  LinkGenerationError error = LinkGenerationError::kIncorrectSelector;
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", false,
                                       1);
   histogram_tester.ExpectBucketCount("SharedHighlights.LinkGenerated.Error",
-                                     LinkGenerationError::kIncorrectSelector,
-                                     1);
+                                     error, 1);
+  ValidateLinkGeneratedErrorUkm(error);
 }
 
 // Tests that the consumer is informed of a failure to generate a link when an
@@ -294,10 +334,12 @@
   [mocked_consumer_ verify];
 
   // Make sure the correct metric were recorded.
+  LinkGenerationError error = LinkGenerationError::kUnknown;
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", false,
                                       1);
   histogram_tester.ExpectBucketCount("SharedHighlights.LinkGenerated.Error",
-                                     LinkGenerationError::kUnknown, 1);
+                                     error, 1);
+  ValidateLinkGeneratedErrorUkm(error);
 }
 
 // Tests that the consumer is informed of a failure to generate a link when an
@@ -325,10 +367,12 @@
   [mocked_consumer_ verify];
 
   // Make sure the correct metric were recorded.
+  LinkGenerationError error = LinkGenerationError::kUnknown;
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", false,
                                       1);
   histogram_tester.ExpectBucketCount("SharedHighlights.LinkGenerated.Error",
-                                     LinkGenerationError::kUnknown, 1);
+                                     error, 1);
+  ValidateLinkGeneratedErrorUkm(error);
 }
 
 // Tests that the consumer is informed of a failure to generate a link when an
@@ -355,10 +399,12 @@
   [mocked_consumer_ verify];
 
   // Make sure the correct metric were recorded.
+  LinkGenerationError error = LinkGenerationError::kUnknown;
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", false,
                                       1);
   histogram_tester.ExpectBucketCount("SharedHighlights.LinkGenerated.Error",
-                                     LinkGenerationError::kUnknown, 1);
+                                     error, 1);
+  ValidateLinkGeneratedErrorUkm(error);
 }
 
 // Tests that the consumer is informed of a failure to generate a link when a
@@ -385,8 +431,10 @@
   [mocked_consumer_ verify];
 
   // Make sure the correct metric were recorded.
+  LinkGenerationError error = LinkGenerationError::kUnknown;
   histogram_tester.ExpectUniqueSample("SharedHighlights.LinkGenerated", false,
                                       1);
   histogram_tester.ExpectBucketCount("SharedHighlights.LinkGenerated.Error",
-                                     LinkGenerationError::kUnknown, 1);
+                                     error, 1);
+  ValidateLinkGeneratedErrorUkm(error);
 }
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 55cfc3dcd..fe6b8703 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -1609,6 +1609,9 @@
       };
     case START_QR_CODE_SCANNER:
       return ^{
+        if (!self.currentInterface.browser) {
+          return;
+        }
         id<QRScannerCommands> QRHandler = HandlerForProtocol(
             self.currentInterface.browser->GetCommandDispatcher(),
             QRScannerCommands);
@@ -1616,6 +1619,9 @@
       };
     case FOCUS_OMNIBOX:
       return ^{
+        if (!self.currentInterface.browser) {
+          return;
+        }
         id<OmniboxCommands> focusHandler = HandlerForProtocol(
             self.currentInterface.browser->GetCommandDispatcher(),
             OmniboxCommands);
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 70b1d9c..460e2d5 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -316,7 +316,6 @@
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/sync:test_support",
     "//ios/chrome/browser/translate",
-    "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/icons",
     "//ios/chrome/browser/ui/settings:constants",
diff --git a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
index 4879895..33e40b8 100644
--- a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
@@ -13,7 +13,6 @@
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_root_table_constants.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
@@ -124,9 +123,6 @@
     self.styler.tableViewBackgroundColor =
         UIColor.cr_systemGroupedBackgroundColor;
   [super viewDidLoad];
-  if (base::FeatureList::IsEnabled(kSettingsRefresh)) {
-    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
-  }
   self.styler.cellBackgroundColor =
       UIColor.cr_secondarySystemGroupedBackgroundColor;
   self.styler.cellTitleColor = UIColor.cr_labelColor;
@@ -207,18 +203,6 @@
   return kDefaultHeaderFooterHeight;
 }
 
-#pragma mark - UITableViewDataSource
-
-- (UITableViewCell*)tableView:(UITableView*)tableView
-        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
-  if (base::FeatureList::IsEnabled(kSettingsRefresh)) {
-    TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
-    item.useCustomSeparator = YES;
-  }
-
-  return [super tableView:tableView cellForRowAtIndexPath:indexPath];
-}
-
 #pragma mark - TableViewLinkHeaderFooterItemDelegate
 
 - (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(GURL)URL {
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
index 36d3b16a..3f70c8a 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.mm
@@ -5,6 +5,8 @@
 #import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
 
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/string_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
@@ -16,12 +18,20 @@
 #endif
 
 namespace {
+
 // Padding used on the leading and trailing edges of the cell.
 const CGFloat kHorizontalPadding = 16;
 
 // Padding used on the top and bottom edges of the cell.
 const CGFloat kVerticalPadding = 8;
 
+// Returns a padding according to the width of the current device.
+CGFloat HorizontalPadding() {
+  if (base::FeatureList::IsEnabled(kSettingsRefresh) && !IsSmallDevice())
+    return 0;
+  return kHorizontalPadding;
+}
+
 }  // namespace
 
 @implementation TableViewLinkHeaderFooterItem
@@ -88,10 +98,10 @@
                          constant:-kVerticalPadding],
       [_textView.trailingAnchor
           constraintEqualToAnchor:self.contentView.trailingAnchor
-                         constant:-kHorizontalPadding],
+                         constant:-HorizontalPadding()],
       [_textView.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
-                         constant:kHorizontalPadding],
+                         constant:HorizontalPadding()],
     ]];
   }
   return self;
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index d99ad49..691aee0 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -232,6 +232,8 @@
     ":web",
     "//base",
     "//base/test:test_support",
+    "//components/shared_highlighting/core/common",
+    "//components/ukm:test_support",
     "//components/url_formatter",
     "//ios/net",
     "//ios/testing:ocmock_support",
@@ -253,6 +255,7 @@
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
     "//ios/web/web_state/ui:web_view_handler",
     "//net:test_support",
+    "//services/metrics/public/cpp:ukm_builders",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/ocmock",
diff --git a/ios/web/DEPS b/ios/web/DEPS
index 3eb9d1c..61c5722 100644
--- a/ios/web/DEPS
+++ b/ios/web/DEPS
@@ -15,6 +15,7 @@
 
   # For tests.
   "+components/crash/core/common/objc_zombie.h",
+  "+components/ukm/test_ukm_recorder.h",
   "+ios/testing",
 
   # TODO(crbug.com/1101077): Disallow direct import of eg2 headers.
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn
index 2403edd6..3935920 100644
--- a/ios/web/navigation/BUILD.gn
+++ b/ios/web/navigation/BUILD.gn
@@ -37,6 +37,7 @@
     "//ios/web/web_state/ui:web_view_handler",
     "//ios/web/web_view:util",
     "//net",
+    "//services/metrics/public/cpp:metrics_cpp",
     "//ui/base",
     "//url",
   ]
diff --git a/ios/web/navigation/crw_text_fragments_handler.mm b/ios/web/navigation/crw_text_fragments_handler.mm
index 8e54273..9a4caeb 100644
--- a/ios/web/navigation/crw_text_fragments_handler.mm
+++ b/ios/web/navigation/crw_text_fragments_handler.mm
@@ -15,6 +15,7 @@
 #import "ios/web/public/navigation/referrer.h"
 #import "ios/web/web_state/ui/crw_web_view_handler_delegate.h"
 #import "ios/web/web_state/web_state_impl.h"
+#import "services/metrics/public/cpp/ukm_source_id.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -39,6 +40,14 @@
 // Returns the WebStateImpl from self.delegate.
 @property(nonatomic, readonly, assign) web::WebStateImpl* webStateImpl;
 
+// Cached value of the source ID representing the last navigation to have text
+// fragments.
+@property(nonatomic, assign) ukm::SourceId latestSourceId;
+
+// Cached value of the latest referrer's URL to have triggered a navigation
+// with text fragments.
+@property(nonatomic, assign) GURL latestReferrerURL;
+
 @end
 
 @implementation CRWTextFragmentsHandler
@@ -80,9 +89,13 @@
     return;
   }
 
+  // Log metrics and cache Referrer for UKM logging.
   shared_highlighting::LogTextFragmentSelectorCount(
       parsedFragments.GetList().size());
   shared_highlighting::LogTextFragmentLinkOpenSource(referrer.url);
+  self.latestSourceId = ukm::ConvertToSourceId(
+      context->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
+  self.latestReferrerURL = referrer.url;
 
   std::string fragmentParam;
   base::JSONWriter::Write(parsedFragments, &fragmentParam);
@@ -158,6 +171,10 @@
   shared_highlighting::LogTextFragmentMatchRate(successCount, fragmentCount);
   shared_highlighting::LogTextFragmentAmbiguousMatch(
       /*ambiguous_match=*/successCount != fragmentCount);
+
+  shared_highlighting::LogLinkOpenedUkmEvent(
+      self.latestSourceId, self.latestReferrerURL,
+      /*success=*/successCount == fragmentCount);
 }
 
 @end
diff --git a/ios/web/navigation/crw_text_fragments_handler_unittest.mm b/ios/web/navigation/crw_text_fragments_handler_unittest.mm
index d49a06a..969156a 100644
--- a/ios/web/navigation/crw_text_fragments_handler_unittest.mm
+++ b/ios/web/navigation/crw_text_fragments_handler_unittest.mm
@@ -7,6 +7,8 @@
 #import "base/strings/utf_string_conversions.h"
 #import "base/test/metrics/histogram_tester.h"
 #import "base/test/scoped_feature_list.h"
+#import "components/shared_highlighting/core/common/shared_highlighting_metrics.h"
+#import "components/ukm/test_ukm_recorder.h"
 #import "ios/web/common/features.h"
 #import "ios/web/public/navigation/referrer.h"
 #import "ios/web/public/test/fakes/fake_navigation_context.h"
@@ -14,6 +16,7 @@
 #import "ios/web/public/test/web_test.h"
 #import "ios/web/web_state/ui/crw_web_view_handler_delegate.h"
 #import "ios/web/web_state/web_state_impl.h"
+#import "services/metrics/public/cpp/ukm_builders.h"
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -26,6 +29,7 @@
 using web::Referrer;
 using ::testing::_;
 using ::testing::ReturnRefOfCopy;
+using shared_highlighting::TextFragmentLinkOpenSource;
 
 namespace {
 
@@ -42,6 +46,9 @@
 const char kSearchEngineURL[] = "https://google.com";
 const char kNonSearchEngineURL[] = "https://notasearchengine.com";
 
+const char kSuccessUkmMetric[] = "Success";
+const char kSourceUkmMetric[] = "Source";
+
 }  // namespace
 
 class MockWebStateImpl : public web::WebStateImpl {
@@ -130,6 +137,25 @@
                                     referrer:GetSearchEngineReferrer()];
   }
 
+  void ValidateLinkOpenedUkm(const ukm::TestAutoSetUkmRecorder& recorder,
+                             bool success,
+                             TextFragmentLinkOpenSource source) {
+    auto entries = recorder.GetEntriesByName(
+        ukm::builders::SharedHighlights_LinkOpened::kEntryName);
+    ASSERT_EQ(1u, entries.size());
+    const ukm::mojom::UkmEntry* entry = entries[0];
+    EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+    recorder.ExpectEntryMetric(entry, kSuccessUkmMetric, success);
+    recorder.ExpectEntryMetric(entry, kSourceUkmMetric,
+                               static_cast<int64_t>(source));
+  }
+
+  void ValidateNoLinkOpenedUkm(const ukm::TestAutoSetUkmRecorder& recorder) {
+    auto entries = recorder.GetEntriesByName(
+        ukm::builders::SharedHighlights_LinkOpened::kEntryName);
+    EXPECT_EQ(0u, entries.size());
+  }
+
   web::FakeNavigationContext context_;
   MockWebStateImpl* web_state_;
   base::test::ScopedFeatureList feature_list_;
@@ -283,6 +309,7 @@
 TEST_F(CRWTextFragmentsHandlerTest,
        NoMetricsRecordedIfNoFragmentPresentWithFragmentId) {
   base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
 
   // Set a URL without text fragments, but with an id fragment.
   SetLastURL(GURL("https://www.chromium.org/#FragmentID"));
@@ -303,6 +330,7 @@
 // from a search engine.
 TEST_F(CRWTextFragmentsHandlerTest, LinkSourceMetricSearchEngine) {
   base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
   SetLastURL(GURL(kValidFragmentsURL));
 
   CRWTextFragmentsHandler* handler = CreateDefaultHandler();
@@ -318,6 +346,7 @@
 // come from a search engine.
 TEST_F(CRWTextFragmentsHandlerTest, LinkSourceMetricNonSearchEngine) {
   base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
   SetLastURL(GURL(kValidFragmentsURL));
 
   CRWTextFragmentsHandler* handler = CreateDefaultHandler();
@@ -375,6 +404,7 @@
   // 100% rate case.
   {
     base::HistogramTester histogram_tester;
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
 
     base::DictionaryValue js_response = base::DictionaryValue();
     js_response.SetKey("command", base::Value("textFragments.response"));
@@ -387,11 +417,15 @@
     histogram_tester.ExpectUniqueSample("TextFragmentAnchor.AmbiguousMatch", 0,
                                         1);
     histogram_tester.ExpectUniqueSample("TextFragmentAnchor.MatchRate", 100, 1);
+
+    ValidateLinkOpenedUkm(ukm_recorder, /*success=*/true,
+                          TextFragmentLinkOpenSource::kSearchEngine);
   }
 
   // 50% rate case.
   {
     base::HistogramTester histogram_tester;
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
 
     base::DictionaryValue js_response = base::DictionaryValue();
     js_response.SetKey("command", base::Value("textFragments.response"));
@@ -404,11 +438,15 @@
     histogram_tester.ExpectUniqueSample("TextFragmentAnchor.AmbiguousMatch", 1,
                                         1);
     histogram_tester.ExpectUniqueSample("TextFragmentAnchor.MatchRate", 50, 1);
+
+    ValidateLinkOpenedUkm(ukm_recorder, /*success=*/false,
+                          TextFragmentLinkOpenSource::kSearchEngine);
   }
 
   // 0% rate case.
   {
     base::HistogramTester histogram_tester;
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
 
     base::DictionaryValue js_response = base::DictionaryValue();
     js_response.SetKey("command", base::Value("textFragments.response"));
@@ -421,11 +459,15 @@
     histogram_tester.ExpectUniqueSample("TextFragmentAnchor.AmbiguousMatch", 1,
                                         1);
     histogram_tester.ExpectUniqueSample("TextFragmentAnchor.MatchRate", 0, 1);
+
+    ValidateLinkOpenedUkm(ukm_recorder, /*success=*/false,
+                          TextFragmentLinkOpenSource::kSearchEngine);
   }
 
   // Invalid values case - negative numbers.
   {
     base::HistogramTester histogram_tester;
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
 
     base::DictionaryValue js_response = base::DictionaryValue();
     js_response.SetKey("command", base::Value("textFragments.response"));
@@ -437,11 +479,14 @@
 
     histogram_tester.ExpectTotalCount("TextFragmentAnchor.AmbiguousMatch", 0);
     histogram_tester.ExpectTotalCount("TextFragmentAnchor.MatchRate", 0);
+
+    ValidateNoLinkOpenedUkm(ukm_recorder);
   }
 
   // Invalid values case - not numbers.
   {
     base::HistogramTester histogram_tester;
+    ukm::TestAutoSetUkmRecorder ukm_recorder;
 
     base::DictionaryValue js_response = base::DictionaryValue();
     js_response.SetKey("command", base::Value("textFragments.response"));
@@ -453,5 +498,7 @@
 
     histogram_tester.ExpectTotalCount("TextFragmentAnchor.AmbiguousMatch", 0);
     histogram_tester.ExpectTotalCount("TextFragmentAnchor.MatchRate", 0);
+
+    ValidateNoLinkOpenedUkm(ukm_recorder);
   }
 }
diff --git a/services/device/geolocation/core_location_provider_unittest.mm b/services/device/geolocation/core_location_provider_unittest.mm
index 43218df..cc5af46 100644
--- a/services/device/geolocation/core_location_provider_unittest.mm
+++ b/services/device/geolocation/core_location_provider_unittest.mm
@@ -112,7 +112,8 @@
   provider_.reset();
 }
 
-TEST_F(CoreLocationProviderTest, DontStartUpdatingIfPermissionDenied) {
+// crbug.com/1153412: disabled due to flakiness.
+TEST_F(CoreLocationProviderTest, DISABLED_DontStartUpdatingIfPermissionDenied) {
   InitializeProvider();
   [fake_location_manager_ fakeUpdatePermission:kCLAuthorizationStatusDenied];
   provider_->StartProvider(/*high_accuracy=*/true);
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index d7d1d2aa..0286bad 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -452,18 +452,6 @@
     if (tainted_) {
       request_.headers.SetHeader(net::HttpRequestHeaders::kOrigin,
                                  url::Origin().Serialize());
-    } else if (
-        base::FeatureList::IsEnabled(
-            features::
-                kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess) &&
-        !request_.isolated_world_origin && HasSpecialAccessToDestination()) {
-      DCHECK(!fetch_cors_flag_);
-      // When request's origin has an access to the destination URL (via
-      // |origin_access_list_| and |factory_bound_origin_access_list_|), we
-      // attach destination URL's origin instead of request's origin to the
-      // "origin" request header.
-      request_.headers.SetHeader(net::HttpRequestHeaders::kOrigin,
-                                 url::Origin::Create(request_.url).Serialize());
     } else {
       request_.headers.SetHeader(net::HttpRequestHeaders::kOrigin,
                                  request_.request_initiator->Serialize());
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index 14e7347..c6867eac 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -227,7 +227,8 @@
       DCHECK(devtools_request_id_);
       network_service_client->OnCorsPreflightRequest(
           process_id_, original_request_.render_frame_id, *devtools_request_id_,
-          *preflight_request, original_request_.url);
+          *preflight_request, original_request_.url,
+          original_request_.devtools_request_id.value_or(""));
     }
     loader_ =
         SimpleURLLoader::Create(std::move(preflight_request), annotation_tag);
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc
index 59ad51ae..e01e76f9 100644
--- a/services/network/cors/preflight_controller_unittest.cc
+++ b/services/network/cors/preflight_controller_unittest.cc
@@ -284,6 +284,9 @@
       const {
     return preflight_status_;
   }
+  const std::string& initiator_devtools_request_id() const {
+    return initiator_devtools_request_id_;
+  }
 
  private:
   // mojom::NetworkServiceClient:
@@ -305,12 +308,15 @@
       const base::Optional<std::string>& raw_response_headers) override {
     on_raw_response_called_ = true;
   }
-  void OnCorsPreflightRequest(int32_t process_id,
-                              int32_t routing_id,
-                              const base::UnguessableToken& devtool_request_id,
-                              const network::ResourceRequest& request,
-                              const GURL& initiator_url) override {
+  void OnCorsPreflightRequest(
+      int32_t process_id,
+      int32_t routing_id,
+      const base::UnguessableToken& devtool_request_id,
+      const network::ResourceRequest& request,
+      const GURL& initiator_url,
+      const std::string& initiator_devtools_request_id) override {
     preflight_request_ = request;
+    initiator_devtools_request_id_ = initiator_devtools_request_id;
   }
   void OnCorsPreflightResponse(
       int32_t process_id,
@@ -338,6 +344,7 @@
   base::Optional<network::ResourceRequest> preflight_request_;
   network::mojom::URLResponseHeadPtr preflight_response_;
   base::Optional<network::URLLoaderCompletionStatus> preflight_status_;
+  std::string initiator_devtools_request_id_;
 };
 
 class PreflightControllerTest : public testing::Test {
@@ -660,6 +667,7 @@
       network_service_client->preflight_response()->headers->response_code());
   ASSERT_TRUE(network_service_client->preflight_status().has_value());
   EXPECT_EQ(net::OK, network_service_client->preflight_status()->error_code);
+  EXPECT_EQ("TEST", network_service_client->initiator_devtools_request_id());
 }
 
 }  // namespace
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 4ac478d3..d936ceb 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -146,12 +146,6 @@
 const base::Feature kDisableKeepaliveFetch{"DisableKeepaliveFetch",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Attach the origin of the destination URL to the "origin" header
-const base::Feature
-    kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess{
-        "DeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess",
-        base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Controls whether a |request_initiator| that mismatches
 // |request_initiator_origin_lock| leads to 1) failing the HTTP request and 2)
 // calling mojo::ReportBadMessage (on desktop platforms, where NetworkService
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index b4899fcb..fe8b2c4 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -56,9 +56,6 @@
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kDisableKeepaliveFetch;
 COMPONENT_EXPORT(NETWORK_CPP)
-extern const base::Feature
-    kDeriveOriginFromUrlForNeitherGetNorHeadRequestWhenHavingSpecialAccess;
-COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kRequestInitiatorSiteLockEnfocement;
 COMPONENT_EXPORT(NETWORK_CPP)
 extern const base::Feature kCertVerifierService;
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 3d2a5703..6e5025c 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -88,7 +88,8 @@
     int32 render_frame_id,
     mojo_base.mojom.UnguessableToken devtool_request_id,
     URLRequest request,
-    url.mojom.Url initiator_url);
+    url.mojom.Url initiator_url,
+    string initiator_devtool_request_id);
 
   // Called to send the CORS preflight response information. Only called when
   // |devtool_request_id| is available on the original request.
diff --git a/services/network/test/test_network_service_client.cc b/services/network/test/test_network_service_client.cc
index db13adc..5a6fef8a 100644
--- a/services/network/test/test_network_service_client.cc
+++ b/services/network/test/test_network_service_client.cc
@@ -50,7 +50,8 @@
     int32_t routing_id,
     const base::UnguessableToken& devtools_request_id,
     const network::ResourceRequest& request,
-    const GURL& initiator_url) {}
+    const GURL& initiator_url,
+    const std::string& initiator_devtools_request_id) {}
 
 void TestNetworkServiceClient::OnCorsPreflightResponse(
     int32_t process_id,
diff --git a/services/network/test/test_network_service_client.h b/services/network/test/test_network_service_client.h
index 1b7b8d0..a277f71 100644
--- a/services/network/test/test_network_service_client.h
+++ b/services/network/test/test_network_service_client.h
@@ -46,11 +46,13 @@
       const net::CookieAndLineAccessResultList& cookies_with_access_result,
       std::vector<network::mojom::HttpRawHeaderPairPtr> headers,
       const base::Optional<std::string>& raw_response_headers) override;
-  void OnCorsPreflightRequest(int32_t process_id,
-                              int32_t routing_id,
-                              const base::UnguessableToken& devtool_request_id,
-                              const network::ResourceRequest& request,
-                              const GURL& initiator_url) override;
+  void OnCorsPreflightRequest(
+      int32_t process_id,
+      int32_t routing_id,
+      const base::UnguessableToken& devtool_request_id,
+      const network::ResourceRequest& request,
+      const GURL& initiator_url,
+      const std::string& initiator_devtools_request_id) override;
   void OnCorsPreflightResponse(
       int32_t process_id,
       int32_t routing_id,
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 234a63e..b5856e9 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -10562,22 +10562,297 @@
     ]
   },
   "Linux FYI Release (AMD RX 5500 XT)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "angle_end2end_tests",
+          "--gtest_filter=-*Vulkan_SwiftShader*",
+          "--bot-mode",
+          "--max-processes=4"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "angle_end2end_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_end2end_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "angle_unittests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*",
+          "--no-xvfb"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests",
+          "--no-xvfb"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=validating",
+          "--use-gpu-in-tests",
+          "--no-xvfb"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_validating",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--no-xvfb"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gles2_conform_test",
+        "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "swiftshader_unittests",
+        "test_id_prefix": "ninja://third_party/swiftshader/tests/GLESUnitTests:swiftshader_unittests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
+          "angle_perftests",
+          "--non-telemetry=true",
+          "--gtest-benchmark-name=angle_perftests",
+          "-v",
+          "--one-frame-only",
+          "--test-timeout=100",
+          "--batch-size=1",
+          "--bot-mode",
+          "--max-processes=1",
+          "--print-test-stdout"
+        ],
+        "isolate_name": "angle_perftests",
+        "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "angle_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_perftests/"
+      },
+      {
+        "args": [
+          "context_lost",
           "--show-stdout",
           "--browser=release",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "context_lost_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -10594,6 +10869,618 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "depth_capture_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--expected-vendor-id",
+          "1002",
+          "--expected-device-id",
+          "7340"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_gl_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_gl_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   },
@@ -30378,22 +31265,447 @@
     ]
   },
   "Win10 FYI x64 Release (AMD RX 5500 XT)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "angle_end2end_tests",
+          "--gtest_filter=-*Vulkan_SwiftShader*",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "angle_end2end_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_end2end_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "angle_unittests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gles2_conform_test",
+        "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-angle=d3d9"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gles2_conform_d3d9_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gles2_conform_test",
+        "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-angle=gl",
+          "--disable-gpu-sandbox"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gles2_conform_gl_test",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gles2_conform_test",
+        "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "swiftshader_unittests",
+        "test_id_prefix": "ninja://third_party/swiftshader/tests/GLESUnitTests:swiftshader_unittests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "vulkan_tests",
+        "test_id_prefix": "ninja://gpu/vulkan:vulkan_tests/"
+      },
+      {
+        "args": [
+          "--ignore-runtime-requirements=*"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "xr_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "xr_browser_tests",
+        "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
+          "angle_perftests",
+          "--non-telemetry=true",
+          "--gtest-benchmark-name=angle_perftests",
+          "-v",
+          "--one-frame-only",
+          "--test-timeout=100",
+          "--batch-size=1",
+          "--bot-mode",
+          "--max-processes=1",
+          "--print-test-stdout"
+        ],
+        "isolate_name": "angle_perftests",
+        "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "angle_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_perftests/"
+      },
+      {
+        "args": [
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_angle_revision}"
+        ],
+        "isolate_name": "angle_restricted_trace_gold_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "angle_restricted_trace_gold_tests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/angle/src/tests/restricted_traces:angle_restricted_trace_gold_tests/"
+      },
+      {
+        "args": [
+          "context_lost",
           "--show-stdout",
           "--browser=release_x64",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "context_lost_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -30410,6 +31722,484 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "depth_capture",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "depth_capture_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--expected-vendor-id",
+          "1002",
+          "--expected-device-id",
+          "7340"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "../../tools/perf/run_benchmark",
+          "--benchmarks=rendering.desktop",
+          "--browser=release_x64"
+        ],
+        "isolate_name": "rendering_representative_perf_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "rendering_representative_perf_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:rendering_representative_perf_tests/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d11 --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=d3d9 --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d9_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=vulkan --use-cmd-decoder=passthrough --force_high_performance_gpu"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_vulkan_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 3c9f115..8aa751ba 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2436,6 +2436,11 @@
           '--browser=release_x64',
         ],
       },
+      'Win10 FYI x64 Release (AMD RX 5500 XT)': {
+        'args': [
+          '--browser=release_x64',
+        ],
+      },
       'Win10 FYI x64 Release (Intel HD 630)': {
         'args': [
           '--browser=release_x64',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index c709c9b..140e73b 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5718,7 +5718,7 @@
     ],
 
     # TODO(crbug.com/1080424): Merge with an existing set of tests such as
-    # gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests once all CrOS tests
+    # gpu_fyi_linux_release_telemetry_tests once all CrOS tests
     # have been enabled.
     'gpu_fyi_chromeos_release_telemetry_tests': [
       'gpu_common_and_optional_telemetry_tests',
@@ -5733,6 +5733,15 @@
       # 'gpu_webgl_conformance_gl_passthrough_telemetry_tests',
     ],
 
+    'gpu_fyi_linux_amd_r7_240_release_telemetry_tests': [
+      'gpu_common_and_optional_telemetry_tests',
+      'gpu_passthrough_telemetry_tests',
+      'gpu_validating_telemetry_tests',
+      'gpu_webgl2_conformance_validating_telemetry_tests',
+      'gpu_webgl_conformance_gl_passthrough_telemetry_tests',
+      'gpu_webgl_conformance_validating_telemetry_tests',
+    ],
+
     'gpu_fyi_linux_debug_gtests': [
       'gpu_angle_end2end_gtests',
       'gpu_angle_unit_gtests',
@@ -5743,12 +5752,10 @@
       'gpu_swiftshader_gtests',
     ],
 
-    'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests': [
+    'gpu_fyi_linux_debug_telemetry_tests': [
       'gpu_common_and_optional_telemetry_tests',
       'gpu_passthrough_telemetry_tests',
       'gpu_validating_telemetry_tests',
-      'gpu_webgl2_conformance_gl_passthrough_telemetry_tests',
-      'gpu_webgl2_conformance_validating_telemetry_tests',
       'gpu_webgl_conformance_gl_passthrough_telemetry_tests',
       'gpu_webgl_conformance_validating_telemetry_tests',
     ],
@@ -5787,19 +5794,12 @@
       'gpu_common_and_optional_telemetry_tests',
       'gpu_passthrough_telemetry_tests',
       'gpu_validating_telemetry_tests',
+      'gpu_webgl2_conformance_gl_passthrough_telemetry_tests',
       'gpu_webgl2_conformance_validating_telemetry_tests',
       'gpu_webgl_conformance_gl_passthrough_telemetry_tests',
       'gpu_webgl_conformance_validating_telemetry_tests',
     ],
 
-    'gpu_fyi_linux_telemetry_tests': [
-      'gpu_common_and_optional_telemetry_tests',
-      'gpu_passthrough_telemetry_tests',
-      'gpu_validating_telemetry_tests',
-      'gpu_webgl_conformance_gl_passthrough_telemetry_tests',
-      'gpu_webgl_conformance_validating_telemetry_tests',
-    ],
-
     'gpu_fyi_mac_debug_gtests': [
       'gpu_angle_end2end_gtests',
       'gpu_angle_unit_gtests',
@@ -5887,6 +5887,15 @@
       'gpu_swiftshader_gtests',
     ],
 
+    'gpu_fyi_win_amd_release_telemetry_tests': [
+      'gpu_common_and_optional_telemetry_tests',
+      'gpu_passthrough_telemetry_tests',
+      'gpu_webgl2_conformance_d3d11_passthrough_telemetry_tests',
+      'gpu_webgl_conformance_d3d11_passthrough_telemetry_tests',
+      'gpu_webgl_conformance_d3d9_passthrough_telemetry_tests',
+      'gpu_webgl_conformance_vulkan_passthrough_telemetry_tests',
+    ],
+
     'gpu_fyi_win_debug_telemetry_tests': [
       'gpu_common_and_optional_telemetry_tests',
       'gpu_passthrough_telemetry_tests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index f2b5b5ef3..8e6b8ad 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3725,7 +3725,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'gpu_fyi_linux_debug_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_linux_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_debug_telemetry_tests',
         },
       },
       'Linux FYI Experimental Release (Intel HD 630)': {
@@ -3755,7 +3755,7 @@
         'test_suites': {
           'gtest_tests': 'gpu_fyi_linux_release_and_dawn_gtests',
           'isolated_scripts': 'gpu_angle_perf_smoke_isolated_scripts',
-          'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_release_telemetry_tests',
         },
       },
       'Linux FYI GPU TSAN Release': {
@@ -3795,7 +3795,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'gpu_fyi_linux_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_linux_release_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_amd_r7_240_release_telemetry_tests',
         },
       },
       'Linux FYI Release (AMD RX 5500 XT)': {
@@ -3809,7 +3809,9 @@
           'linux_amd_rx_5500_xt',
         ],
         'test_suites': {
-          'gpu_telemetry_tests': 'gpu_noop_sleep_telemetry_test',
+          'gtest_tests': 'gpu_fyi_linux_release_gtests',
+          'isolated_scripts': 'gpu_angle_perf_smoke_isolated_scripts',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_release_telemetry_tests',
         },
       },
       'Linux FYI Release (Intel HD 630)': {
@@ -3820,7 +3822,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'gpu_fyi_linux_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_release_telemetry_tests',
           'isolated_scripts': 'gpu_angle_restricted_trace_gold_isolated_scripts',
         },
       },
@@ -3832,7 +3834,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'gpu_fyi_linux_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_release_telemetry_tests',
           'isolated_scripts': 'gpu_angle_restricted_trace_gold_isolated_scripts',
         },
       },
@@ -3844,7 +3846,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'gpu_fyi_linux_release_gtests',
-          'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fyi_linux_release_telemetry_tests',
           'isolated_scripts': 'gpu_angle_isolated_scripts',
         },
       },
@@ -4266,7 +4268,9 @@
           'win10_amd_rx_5500_xt',
         ],
         'test_suites': {
-          'gpu_telemetry_tests': 'gpu_noop_sleep_telemetry_test',
+          'gtest_tests': 'gpu_fyi_win_gtests',
+          'isolated_scripts': 'gpu_angle_and_desktop_representative_perf_fyi_isolated_scripts',
+          'gpu_telemetry_tests': 'gpu_fyi_win_amd_release_telemetry_tests',
         },
       },
       'Win10 FYI x64 Release (Intel HD 630)': {
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index d68e347..cbf5b3db 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -233,6 +233,12 @@
 const base::Feature kWebRtcHideLocalIpsWithMdns{
     "WebRtcHideLocalIpsWithMdns", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Causes WebRTC to not set the color space of video frames on the receive side
+// in case it's unspecified. Otherwise we will guess that the color space is
+// BT709. http://crbug.com/1129243
+const base::Feature kWebRtcIgnoreUnspecifiedColorSpace{
+    "WebRtcIgnoreUnspecifiedColorSpace", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // When enabled, wake ups from throttleable TaskQueues are limited to 1 per
 // minute in a page that has been backgrounded for 5 minutes.
 //
diff --git a/third_party/blink/common/origin_trials/trial_token.cc b/third_party/blink/common/origin_trials/trial_token.cc
index ce88371..8bf0ad8 100644
--- a/third_party/blink/common/origin_trials/trial_token.cc
+++ b/third_party/blink/common/origin_trials/trial_token.cc
@@ -238,13 +238,10 @@
       is_third_party = is_third_party_value->GetBool();
     }
 
-    // The |usage| field is optional and can only be set if |isThirdParty| flag
-    // is true. If found, ensure its value is either empty or "subset".
+    // The |usage| field is optional. If found, ensure its value is either empty
+    // or "subset".
     std::string* usage_value = datadict->FindStringKey("usage");
     if (usage_value) {
-      if (!is_third_party) {
-        return nullptr;
-      }
       if (usage_value->empty()) {
         usage = UsageRestriction::kNone;
       } else if (*usage_value == kUsageSubset) {
diff --git a/third_party/blink/common/origin_trials/trial_token_unittest.cc b/third_party/blink/common/origin_trials/trial_token_unittest.cc
index 7f7e4a1..1cecd950 100644
--- a/third_party/blink/common/origin_trials/trial_token_unittest.cc
+++ b/third_party/blink/common/origin_trials/trial_token_unittest.cc
@@ -263,6 +263,14 @@
     "{\"origin\": \"https://example.com:443\", \"isSubdomain\": true, "
     "\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
 
+const char kUsageEmptyTokenJSON[] =
+    "{\"origin\": \"https://valid.example.com:443\", \"usage\": \"\", "
+    "\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
+
+const char kUsageSubsetTokenJSON[] =
+    "{\"origin\": \"https://valid.example.com:443\", \"usage\": \"subset\", "
+    "\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
+
 const char kSampleNonThirdPartyTokenJSON[] =
     "{\"origin\": \"https://valid.example.com:443\", \"isThirdParty\": false, "
     "\"feature\": \"Frobulate\", \"expiry\": 1458766277}";
@@ -327,10 +335,6 @@
     "{\"origin\": \"https://a.a\", \"isThirdParty\": true, \"usage\": "
     "\"cycle\", \"feature\": \"a\", "
     "\"expiry\": 1458766277}",
-    // usage in non third party token
-    "{\"origin\": \"https://a.a\", \"isThirdParty\": false, \"usage\": "
-    "\"subset\", \"feature\": \"a\", "
-    "\"expiry\": 1458766277}",
 };
 
 // Valid token JSON. The feature name matches matches kExpectedLongFeatureName
@@ -994,6 +998,7 @@
   EXPECT_FALSE(token->match_subdomains());
   EXPECT_EQ(expected_origin_, token->origin());
   EXPECT_EQ(expected_expiry_, token->expiry_time());
+  EXPECT_EQ(TrialToken::UsageRestriction::kNone, token->usage_restriction());
 }
 
 TEST_P(TrialTokenParseTest, ParseValidNonSubdomainToken) {
@@ -1135,6 +1140,26 @@
   EXPECT_EQ(expected_expiry_, token->expiry_time());
 }
 
+TEST_F(TrialTokenTest, ParseValidUsageEmptyToken) {
+  std::unique_ptr<TrialToken> token = Parse(kUsageEmptyTokenJSON, kVersion3);
+  ASSERT_TRUE(token);
+  EXPECT_EQ(kExpectedFeatureName, token->feature_name());
+  EXPECT_FALSE(token->is_third_party());
+  EXPECT_EQ(TrialToken::UsageRestriction::kNone, token->usage_restriction());
+  EXPECT_EQ(expected_origin_, token->origin());
+  EXPECT_EQ(expected_expiry_, token->expiry_time());
+}
+
+TEST_F(TrialTokenTest, ParseValidUsageSubsetToken) {
+  std::unique_ptr<TrialToken> token = Parse(kUsageSubsetTokenJSON, kVersion3);
+  ASSERT_TRUE(token);
+  EXPECT_EQ(kExpectedFeatureName, token->feature_name());
+  EXPECT_FALSE(token->is_third_party());
+  EXPECT_EQ(TrialToken::UsageRestriction::kSubset, token->usage_restriction());
+  EXPECT_EQ(expected_origin_, token->origin());
+  EXPECT_EQ(expected_expiry_, token->expiry_time());
+}
+
 TEST_F(TrialTokenTest, ParseValidThirdPartyUsageSubsetToken) {
   std::unique_ptr<TrialToken> token =
       Parse(kSampleThirdPartyTokenUsageSubsetJSON, kVersion3);
diff --git a/third_party/blink/common/origin_trials/trial_token_validator.cc b/third_party/blink/common/origin_trials/trial_token_validator.cc
index 99fafa3..76948825 100644
--- a/third_party/blink/common/origin_trials/trial_token_validator.cc
+++ b/third_party/blink/common/origin_trials/trial_token_validator.cc
@@ -107,8 +107,7 @@
   if (policy->IsTokenDisabled(trial_token->signature()))
     return TrialTokenResult(OriginTrialTokenStatus::kTokenDisabled);
 
-  if (trial_token->is_third_party() &&
-      trial_token->usage_restriction() ==
+  if (trial_token->usage_restriction() ==
           TrialToken::UsageRestriction::kSubset &&
       policy->IsFeatureDisabledForUser(trial_token->feature_name()))
     return TrialTokenResult(OriginTrialTokenStatus::kFeatureDisabledForUser);
diff --git a/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc b/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc
index 8433c9e..8cc3ab2 100644
--- a/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc
+++ b/third_party/blink/common/origin_trials/trial_token_validator_unittest.cc
@@ -167,6 +167,17 @@
     "InN1YnNldCIsICJmZWF0dXJlIjogIkZyb2J1bGF0ZVRoaXJkUGFydHkiLCAiZXhw"
     "aXJ5IjogMjAwMDAwMDAwMH0=";
 
+// Well-formed token, for first party, with usage set to user subset exclusion.
+// Generate this token with the command (in tools/origin_trials):
+// generate_token.py valid.example.com FrobulateThirdParty
+//  --version 3 --usage-restriction subset --expire-timestamp=2000000000
+const char kUsageSubsetToken[] =
+    "Axi0wjIp8gaGr/"
+    "pTPzwrHqeWXnmhCiZhE2edsJ9fHX25GV6A8zg1fCv27qhBNnbxjqDpU0a+"
+    "xKScEiqKK1MS3QUAAAB2eyJvcmlnaW4iOiAiaHR0cHM6Ly92YWxpZC5leGFtcGxlLmNvbTo0ND"
+    "MiLCAidXNhZ2UiOiAic3Vic2V0IiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlVGhpcmRQYXJ0eSIs"
+    "ICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==";
+
 // This timestamp is set to a time after the expiry timestamp of kExpiredToken,
 // but before the expiry timestamp of kValidToken.
 double kNowTimestamp = 1500000000;
@@ -372,8 +383,24 @@
             validator_.ValidateToken(kSampleToken, appropriate_origin_, Now())
                 .status);
 }
+TEST_F(TrialTokenValidatorTest,
+       ValidatorRespectsDisabledFeaturesForUserWithFirstPartyToken) {
+  // Token should be valid if the feature is not disabled for user.
+  TrialTokenResult result =
+      validator_.ValidateToken(kUsageSubsetToken, appropriate_origin_, Now());
+  EXPECT_EQ(blink::OriginTrialTokenStatus::kSuccess, result.status);
+  EXPECT_EQ(kAppropriateThirdPartyFeatureName, result.feature_name);
+  EXPECT_EQ(kSampleTokenExpiryTime, result.expiry_time);
+  // Token should be invalid when the feature is disabled for user.
+  DisableFeatureForUser(kAppropriateThirdPartyFeatureName);
+  EXPECT_EQ(
+      blink::OriginTrialTokenStatus::kFeatureDisabledForUser,
+      validator_.ValidateToken(kUsageSubsetToken, appropriate_origin_, Now())
+          .status);
+}
 
-TEST_F(TrialTokenValidatorTest, ValidatorRespectsDisabledFeaturesForUser) {
+TEST_F(TrialTokenValidatorTest,
+       ValidatorRespectsDisabledFeaturesForUserWithThirdPartyToken) {
   // Token should be valid if the feature is not disabled for user.
   TrialTokenResult result = validator_.ValidateToken(
       kThirdPartyUsageSubsetToken, inappropriate_origin_, &appropriate_origin_,
diff --git a/third_party/blink/perf_tests/service_worker/cold-fetch.html b/third_party/blink/perf_tests/service_worker/cold-fetch.html
new file mode 100644
index 0000000..3199b06f
--- /dev/null
+++ b/third_party/blink/perf_tests/service_worker/cold-fetch.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/runner.js'></script>
+</head>
+<body>
+<script>
+// serviceWorkerPerfTools is defined in service_worker_perf.js.
+serviceWorkerPerfTools.enable();
+
+let isDone = false;
+function testDone() {
+  isDone = true;
+  serviceWorkerPerfTools.quit();
+}
+
+function addIframe(url) {
+  return new Promise((resolve) => {
+    var frame = document.createElement('iframe');
+    frame.src = url;
+    frame.onload = () => { resolve(frame); };
+    document.body.appendChild(frame);
+  });
+}
+
+function waitForWorkerActive(worker) {
+  return new Promise((resolve) => {
+    worker.addEventListener('statechange', () => {
+      if (worker.state == 'activated')
+        resolve();
+    });
+  });
+}
+
+async function runTest() {
+  const script = '/service_worker/resources/fetch-service-worker.js';
+  const scope = '/service_worker/resources/';
+  const client = '/service_worker/resources/simple.html';
+  const resource = '/service_worker/resources/data/1K_0.txt';
+
+  const registration = await navigator.serviceWorker.register(
+      script, { scope: scope });
+  await waitForWorkerActive(registration.installing);
+  const frame = await addIframe(client);
+
+  while (!isDone) {
+    await serviceWorkerPerfTools.stopWorkers();
+    const startTime = performance.now();
+    const response = await frame.contentWindow.fetch(resource);
+    PerfTestRunner.measureValueAsync(performance.now() - startTime);
+  }
+  return;
+}
+
+PerfTestRunner.startMeasureValuesAsync({
+  description: 'Measure the performance of fetch with service worker' +
+      ' not started.',
+  unit: 'ms',
+  run: runTest,
+  done: testDone,
+  iterationCount: 30
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/perf_tests/service_worker/hot-fetch.html b/third_party/blink/perf_tests/service_worker/hot-fetch.html
new file mode 100644
index 0000000..562091b
--- /dev/null
+++ b/third_party/blink/perf_tests/service_worker/hot-fetch.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/runner.js'></script>
+</head>
+<body>
+<script>
+let isDone = false;
+function testDone() {
+  isDone = true;
+}
+
+function addIframe(url) {
+  return new Promise((resolve) => {
+    var frame = document.createElement('iframe');
+    frame.src = url;
+    frame.onload = () => { resolve(frame); };
+    document.body.appendChild(frame);
+  });
+}
+
+function waitForWorkerActive(worker) {
+  return new Promise((resolve) => {
+    worker.addEventListener('statechange', () => {
+      if (worker.state == 'activated')
+        resolve();
+    });
+  });
+}
+
+async function runTest() {
+  const script = '/service_worker/resources/fetch-service-worker.js';
+  const scope = '/service_worker/resources/';
+  const client = '/service_worker/resources/simple.html';
+  const resource = '/service_worker/resources/data/1K_0.txt';
+
+  const registration = await navigator.serviceWorker.register(
+      script, { scope: scope });
+  await waitForWorkerActive(registration.installing);
+  const frame = await addIframe(client);
+
+  while (!isDone) {
+    const startTime = performance.now();
+    const response = await frame.contentWindow.fetch(resource);
+    PerfTestRunner.measureValueAsync(performance.now() - startTime);
+  }
+  return;
+}
+
+PerfTestRunner.startMeasureValuesAsync({
+  description: 'Measure the performance of fetch with live service worker.',
+  unit: 'ms',
+  run: runTest,
+  done: testDone,
+  iterationCount: 30
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/perf_tests/service_worker/register-and-unregister.html b/third_party/blink/perf_tests/service_worker/register-and-unregister.html
new file mode 100644
index 0000000..20161a5
--- /dev/null
+++ b/third_party/blink/perf_tests/service_worker/register-and-unregister.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/runner.js'></script>
+</head>
+<body>
+<script>
+let isDone = false;
+function testDone() {
+  isDone = true;
+}
+
+async function runTest() {
+  let index = 0;
+  while (!isDone) {
+    const script =
+        `/service_worker/resources/service-worker-${index}.generated.js`;
+    const scope = `/service_worker/resources/scope_${index}`;
+
+    const startTime = performance.now();
+    const registration = await navigator.serviceWorker.register(
+        script, { scope: scope });
+    await registration.unregister();
+    PerfTestRunner.measureValueAsync(performance.now() - startTime);
+    index++;
+  }
+  return;
+}
+
+PerfTestRunner.startMeasureValuesAsync({
+  description: 'Measure performance of register and unregister service worker.',
+  unit: 'ms',
+  run: runTest,
+  done: testDone,
+  iterationCount: 30
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/perf_tests/service_worker/resources/fetch-service-worker.js b/third_party/blink/perf_tests/service_worker/resources/fetch-service-worker.js
new file mode 100644
index 0000000..c6ee2d3
--- /dev/null
+++ b/third_party/blink/perf_tests/service_worker/resources/fetch-service-worker.js
@@ -0,0 +1,15 @@
+self.addEventListener('install', (event) => {
+  event.waitUntil(caches.open('test_cache').then((cache) => {
+    return cache.add('/service_worker/resources/data/1K_0.txt');
+  }));
+});
+
+self.addEventListener('fetch', (event) => {
+  event.respondWith(async function() {
+    const cachedResponse = await caches.match(event.request);
+    if (cachedResponse) {
+      return cachedResponse;
+    }
+    return fetch(event.request);
+  }());
+});
diff --git a/third_party/blink/perf_tests/service_worker/resources/simple.html b/third_party/blink/perf_tests/service_worker/resources/simple.html
new file mode 100644
index 0000000..0c3e3e7
--- /dev/null
+++ b/third_party/blink/perf_tests/service_worker/resources/simple.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<title>Simple</title>
+Here's a simple html file.
diff --git a/third_party/blink/perf_tests/service_worker/update.html b/third_party/blink/perf_tests/service_worker/update.html
new file mode 100644
index 0000000..489b4d9
--- /dev/null
+++ b/third_party/blink/perf_tests/service_worker/update.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../resources/runner.js'></script>
+</head>
+<body>
+<script>
+let isDone = false;
+function testDone() {
+  isDone = true;
+}
+
+function waitForWorkerActive(worker) {
+  return new Promise((resolve) => {
+    worker.addEventListener('statechange', () => {
+      if (worker.state == 'activated')
+        resolve();
+    });
+  });
+}
+
+async function runTest() {
+  const script =
+      `/service_worker/resources/changing-service-worker.generated.js`;
+  const scope = `/service_worker/resources/`;
+  const registration = await navigator.serviceWorker.register(
+      script, { scope: scope });
+  await waitForWorkerActive(registration.installing);
+
+  while (!isDone) {
+    const registrations = await navigator.serviceWorker.getRegistrations();
+    const startTime = performance.now();
+    await registrations[0].update();
+    PerfTestRunner.measureValueAsync(performance.now() - startTime);
+  }
+  return;
+}
+
+PerfTestRunner.startMeasureValuesAsync({
+  description: 'Measure performance of update service worker.',
+  unit: 'ms',
+  run: runTest,
+  done: testDone,
+  iterationCount: 30
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index abd3314..db5d622 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -67,6 +67,8 @@
     kWebMeasureMemoryViaPerformanceManager;
 BLINK_COMMON_EXPORT extern const base::Feature kWebRtcMultiplexCodec;
 BLINK_COMMON_EXPORT extern const base::Feature kWebRtcHideLocalIpsWithMdns;
+BLINK_COMMON_EXPORT extern const base::Feature
+    kWebRtcIgnoreUnspecifiedColorSpace;
 
 BLINK_COMMON_EXPORT extern const base::Feature kIntensiveWakeUpThrottling;
 BLINK_COMMON_EXPORT extern const char
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 3cbe7ebd..e8e390f 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -4276,6 +4276,7 @@
       SignedExchange
       Ping
       CSPViolationReport
+      Preflight
       Other
 
   # Unique loader identifier.
@@ -4682,6 +4683,7 @@
         script
         preload
         SignedExchange
+        preflight
         other
       # Initiator JavaScript stack trace, set for Script only.
       optional Runtime.StackTrace stack
@@ -4693,6 +4695,8 @@
       # Initiator column number, set for Parser type or for Script type (when script is importing
       # module) (0-based).
       optional number columnNumber
+      # Set if another request triggered this request (e.g. preflight).
+      optional RequestId requestId
 
   # Cookie object
   type Cookie extends object
@@ -5680,6 +5684,8 @@
       optional BoxStyle rowGapSpace
       # Style of empty space caused by columns gaps (gap/column-gap).
       optional BoxStyle columnGapSpace
+      # Style of the self-alignment line (align-items).
+      optional LineStyle crossAlignment
 
   # Style information for drawing a line.
   type LineStyle extends object
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni
index d824770d..7bc97a2 100644
--- a/third_party/blink/renderer/bindings/generated_in_core.gni
+++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -185,14 +185,12 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_keyframe_animation_options.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_keyframe_effect_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_keyframe_effect_options.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_measure_memory.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_measure_memory.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_measure_memory_breakdown.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_measure_memory_breakdown.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_media_query_list_event_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_media_query_list_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_attribution.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_attribution.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_attribution_container.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_attribution_container.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_breakdown_entry.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_breakdown_entry.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_memory_measurement.cc",
diff --git a/third_party/blink/renderer/core/css/document_style_sheet_collection.cc b/third_party/blink/renderer/core/css/document_style_sheet_collection.cc
index ab9f78d..bb6ff07 100644
--- a/third_party/blink/renderer/core/css/document_style_sheet_collection.cc
+++ b/third_party/blink/renderer/core/css/document_style_sheet_collection.cc
@@ -30,7 +30,6 @@
 
 #include "third_party/blink/renderer/core/css/document_style_sheet_collector.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
-#include "third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_sheet_candidate.h"
@@ -127,22 +126,4 @@
   ApplyActiveStyleSheetChanges(*collection);
 }
 
-void DocumentStyleSheetCollection::CollectViewportRules(
-    ViewportStyleResolver& viewport_resolver) {
-  for (Node* node : style_sheet_candidate_nodes_) {
-    StyleSheetCandidate candidate(*node);
-
-    if (candidate.IsImport())
-      continue;
-    StyleSheet* sheet = candidate.Sheet();
-    if (!sheet)
-      continue;
-    if (!candidate.CanBeActivated(
-            GetDocument().GetStyleEngine().PreferredStylesheetSetName()))
-      continue;
-    viewport_resolver.CollectViewportRulesFromAuthorSheet(
-        To<CSSStyleSheet>(*sheet));
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/document_style_sheet_collection.h b/third_party/blink/renderer/core/css/document_style_sheet_collection.h
index 6abcc04..d234c2c8 100644
--- a/third_party/blink/renderer/core/css/document_style_sheet_collection.h
+++ b/third_party/blink/renderer/core/css/document_style_sheet_collection.h
@@ -37,7 +37,6 @@
 class DocumentStyleSheetCollector;
 class StyleEngine;
 class TreeScope;
-class ViewportStyleResolver;
 
 class DocumentStyleSheetCollection final
     : public TreeScopeStyleSheetCollection {
@@ -49,7 +48,6 @@
 
   void UpdateActiveStyleSheets(StyleEngine&);
   void CollectStyleSheets(StyleEngine&, DocumentStyleSheetCollector&);
-  void CollectViewportRules(ViewportStyleResolver&);
 
   void Trace(Visitor* visitor) const override {
     TreeScopeStyleSheetCollection::Trace(visitor);
diff --git a/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.cc b/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.cc
index 978975c..1501af0 100644
--- a/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.cc
@@ -72,10 +72,7 @@
 }
 
 void ViewportStyleResolver::Reset() {
-  viewport_dependent_media_query_results_.clear();
-  device_dependent_media_query_results_.clear();
   property_set_ = nullptr;
-  has_author_style_ = false;
   has_viewport_units_ = false;
   DCHECK(initial_style_);
   initial_style_->SetHasViewportUnits(false);
@@ -101,92 +98,38 @@
       break;
   }
   if (viewport_contents)
-    CollectViewportChildRules(viewport_contents->ChildRules(),
-                              kUserAgentOrigin);
+    CollectViewportRules(viewport_contents->ChildRules());
 
   if (document_->IsMobileDocument()) {
-    CollectViewportChildRules(
-        default_style_sheets.EnsureXHTMLMobileProfileStyleSheet()->ChildRules(),
-        kUserAgentOrigin);
+    CollectViewportRules(
+        default_style_sheets.EnsureXHTMLMobileProfileStyleSheet()
+            ->ChildRules());
   }
   DCHECK(!default_style_sheets.DefaultStyleSheet()->HasViewportRule());
 }
 
-void ViewportStyleResolver::CollectViewportChildRules(
-    const HeapVector<Member<StyleRuleBase>>& rules,
-    Origin origin) {
+void ViewportStyleResolver::CollectViewportRules(
+    const HeapVector<Member<StyleRuleBase>>& rules) {
   for (auto& rule : rules) {
-    if (auto* viewport_rule = DynamicTo<StyleRuleViewport>(rule.Get())) {
-      AddViewportRule(*viewport_rule, origin);
-    } else if (auto* media_rule = DynamicTo<StyleRuleMedia>(rule.Get())) {
-      if (!media_rule->MediaQueries() ||
-          initial_viewport_medium_->Eval(
-              *media_rule->MediaQueries(),
-              &viewport_dependent_media_query_results_,
-              &device_dependent_media_query_results_))
-        CollectViewportChildRules(media_rule->ChildRules(), origin);
-    } else if (auto* supports_rule = DynamicTo<StyleRuleSupports>(rule.Get())) {
-      if (supports_rule->ConditionIsSupported())
-        CollectViewportChildRules(supports_rule->ChildRules(), origin);
-    }
+    if (auto* viewport_rule = DynamicTo<StyleRuleViewport>(rule.Get()))
+      AddViewportRule(*viewport_rule);
   }
 }
 
-void ViewportStyleResolver::CollectViewportRulesFromImports(
-    StyleSheetContents& contents) {
-  for (const auto& import_rule : contents.ImportRules()) {
-    if (!import_rule->GetStyleSheet())
-      continue;
-    if (!import_rule->GetStyleSheet()->HasViewportRule())
-      continue;
-    if (import_rule->MediaQueries() &&
-        initial_viewport_medium_->Eval(*import_rule->MediaQueries(),
-                                       &viewport_dependent_media_query_results_,
-                                       &device_dependent_media_query_results_))
-      CollectViewportRulesFromAuthorSheetContents(
-          *import_rule->GetStyleSheet());
-  }
-}
-
-void ViewportStyleResolver::CollectViewportRulesFromAuthorSheetContents(
-    StyleSheetContents& contents) {
-  CollectViewportRulesFromImports(contents);
-  if (contents.HasViewportRule())
-    CollectViewportChildRules(contents.ChildRules(), kAuthorOrigin);
-}
-
-void ViewportStyleResolver::CollectViewportRulesFromAuthorSheet(
-    const CSSStyleSheet& sheet) {
-  DCHECK(sheet.Contents());
-  StyleSheetContents& contents = *sheet.Contents();
-  if (!contents.HasViewportRule() && contents.ImportRules().IsEmpty())
-    return;
-  if (sheet.MediaQueries() &&
-      !initial_viewport_medium_->Eval(*sheet.MediaQueries(),
-                                      &viewport_dependent_media_query_results_,
-                                      &device_dependent_media_query_results_))
-    return;
-  CollectViewportRulesFromAuthorSheetContents(contents);
-}
-
-void ViewportStyleResolver::AddViewportRule(StyleRuleViewport& viewport_rule,
-                                            Origin origin) {
+void ViewportStyleResolver::AddViewportRule(StyleRuleViewport& viewport_rule) {
   CSSPropertyValueSet& property_set = viewport_rule.MutableProperties();
 
   unsigned property_count = property_set.PropertyCount();
   if (!property_count)
     return;
 
-  if (origin == kAuthorOrigin)
-    has_author_style_ = true;
-
   if (!property_set_) {
     property_set_ = property_set.MutableCopy();
     return;
   }
 
-  // We cannot use mergeAndOverrideOnConflict() here because it doesn't
-  // respect the !important declaration (but addRespectingCascade() does).
+  // We cannot use MergeAndOverrideOnConflict() here because it doesn't
+  // respect the !important declaration (but AddRespectingCascade() does).
   for (unsigned i = 0; i < property_count; ++i) {
     CSSPropertyValueSet::PropertyReference property =
         property_set.PropertyAt(i);
@@ -202,9 +145,7 @@
     return;
   }
 
-  ViewportDescription description(
-      has_author_style_ ? ViewportDescription::kAuthorStyleSheet
-                        : ViewportDescription::kUserAgentStyleSheet);
+  ViewportDescription description(ViewportDescription::kUserAgentStyleSheet);
 
   description.user_zoom = ViewportArgumentValue(CSSPropertyID::kUserZoom);
   description.zoom = ViewportArgumentValue(CSSPropertyID::kZoom);
@@ -357,11 +298,6 @@
     return;
   if (has_viewport_units_)
     needs_update_ = kResolve;
-
-  if (initial_viewport_medium_->DidResultsChange(
-          viewport_dependent_media_query_results_)) {
-    needs_update_ = kCollectRules;
-  }
   if (needs_update_ == kNoUpdate)
     return;
   document_->ScheduleLayoutTreeUpdateIfNeeded();
diff --git a/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h b/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h
index 4ce4c71f..f3a84a9 100644
--- a/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h
@@ -63,15 +63,11 @@
   void Reset();
   void Resolve();
 
-  enum Origin { kUserAgentOrigin, kAuthorOrigin };
   enum UpdateType { kNoUpdate, kResolve, kCollectRules };
 
   void CollectViewportRulesFromUASheets();
-  void CollectViewportChildRules(const HeapVector<Member<StyleRuleBase>>&,
-                                 Origin);
-  void CollectViewportRulesFromImports(StyleSheetContents&);
-  void CollectViewportRulesFromAuthorSheetContents(StyleSheetContents&);
-  void AddViewportRule(StyleRuleViewport&, Origin);
+  void CollectViewportRules(const HeapVector<Member<StyleRuleBase>>&);
+  void AddViewportRule(StyleRuleViewport&);
 
   float ViewportArgumentValue(CSSPropertyID) const;
   Length ViewportLengthValue(CSSPropertyID);
@@ -81,9 +77,6 @@
   Member<MutableCSSPropertyValueSet> property_set_;
   Member<MediaQueryEvaluator> initial_viewport_medium_;
   scoped_refptr<ComputedStyle> initial_style_;
-  MediaQueryResultList viewport_dependent_media_query_results_;
-  MediaQueryResultList device_dependent_media_query_results_;
-  bool has_author_style_ = false;
   bool has_viewport_units_ = false;
   UpdateType needs_update_ = kCollectRules;
 };
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_line.cc b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
index 8111b2a..7ee6536 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_line.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
@@ -164,7 +164,7 @@
           GetRootInlineBox().BlockDirectionPointInLine());
     }
     const PhysicalOffset physical_offset =
-        cursor_.Current().OffsetInContainerBlock();
+        cursor_.Current().OffsetInContainerFragment();
     return cursor_.Current().Style().IsHorizontalWritingMode()
                ? physical_offset.top
                : physical_offset.left;
@@ -213,7 +213,7 @@
 
       const LogicalRect fragment_logical_rect =
           line.Current().ConvertChildToLogical(
-              cursor.Current().RectInContainerBlock());
+              cursor.Current().RectInContainerFragment());
       const LayoutUnit inline_min = fragment_logical_rect.offset.inline_offset;
       const LayoutUnit inline_max = fragment_logical_rect.offset.inline_offset +
                                     fragment_logical_rect.size.inline_size;
diff --git a/third_party/blink/renderer/core/fetch/body_stream_buffer.h b/third_party/blink/renderer/core/fetch/body_stream_buffer.h
index adfede4..cf949e9 100644
--- a/third_party/blink/renderer/core/fetch/body_stream_buffer.h
+++ b/third_party/blink/renderer/core/fetch/body_stream_buffer.h
@@ -43,7 +43,7 @@
       ScriptState*,
       BytesConsumer* consumer,
       AbortSignal* signal,
-      ScriptCachedMetadataHandler* cached_meatadata_handler,
+      ScriptCachedMetadataHandler* cached_metadata_handler,
       scoped_refptr<BlobDataHandle> side_data_blob = nullptr);
 
   // Create() should be used instead of calling this constructor directly.
@@ -51,12 +51,12 @@
                    ScriptState*,
                    BytesConsumer* consumer,
                    AbortSignal* signal,
-                   ScriptCachedMetadataHandler* cached_meatadata_handler,
+                   ScriptCachedMetadataHandler* cached_metadata_handler,
                    scoped_refptr<BlobDataHandle> side_data_blob);
 
   BodyStreamBuffer(ScriptState*,
                    ReadableStream* stream,
-                   ScriptCachedMetadataHandler* cached_meatadata_handler,
+                   ScriptCachedMetadataHandler* cached_metadata_handler,
                    scoped_refptr<BlobDataHandle> side_data_blob = nullptr);
 
   ReadableStream* Stream() { return stream_; }
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index aecbc12..f3de72c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -426,6 +426,8 @@
                        "rowGapSpace");
   AppendBoxStyleConfig(flex_config.column_gap_space, flex_config_info,
                        "columnGapSpace");
+  AppendLineStyleConfig(flex_config.cross_alignment, flex_config_info,
+                        "crossAlignment");
 
   return flex_config_info;
 }
@@ -923,6 +925,8 @@
     const InspectorFlexContainerHighlightConfig&
         flex_container_highlight_config,
     float scale) {
+  CSSComputedStyleDeclaration* style =
+      MakeGarbageCollected<CSSComputedStyleDeclaration>(node, true);
   LocalFrameView* containing_view = node->GetDocument().View();
   LayoutObject* layout_object = node->GetLayoutObject();
   auto* layout_box = To<LayoutBox>(layout_object);
@@ -964,6 +968,9 @@
   flex_info->setValue("containerBorder", container_builder.Release());
   flex_info->setArray("lines", std::move(lines_info));
   flex_info->setBoolean("isHorizontalFlow", is_horizontal);
+  flex_info->setString(
+      "alignItemsStyle",
+      style->GetPropertyCSSValue(CSSPropertyID::kAlignItems)->CssText());
   flex_info->setValue(
       "flexContainerHighlightConfig",
       BuildFlexContainerHighlightConfigInfo(flex_container_highlight_config));
@@ -1903,6 +1910,8 @@
       base::Optional<BoxStyle>(InspectorHighlight::DefaultBoxStyle());
   config.column_gap_space =
       base::Optional<BoxStyle>(InspectorHighlight::DefaultBoxStyle());
+  config.cross_alignment =
+      base::Optional<LineStyle>(InspectorHighlight::DefaultLineStyle());
   return config;
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.h b/third_party/blink/renderer/core/inspector/inspector_highlight.h
index b3f8eff..2a40351e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.h
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.h
@@ -98,6 +98,7 @@
   base::Optional<BoxStyle> cross_distributed_space;
   base::Optional<BoxStyle> row_gap_space;
   base::Optional<BoxStyle> column_gap_space;
+  base::Optional<LineStyle> cross_alignment;
 };
 
 struct CORE_EXPORT InspectorHighlightConfig {
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 4d15d05..fdff389 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -1541,6 +1541,8 @@
       InspectorOverlayAgent::ToBoxStyle(config->getRowGapSpace(nullptr));
   highlight_config->column_gap_space =
       InspectorOverlayAgent::ToBoxStyle(config->getColumnGapSpace(nullptr));
+  highlight_config->cross_alignment =
+      InspectorOverlayAgent::ToLineStyle(config->getCrossAlignment(nullptr));
 
   return highlight_config;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
index 66725a3..8b23bf7 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -2447,7 +2447,7 @@
             continue;
           PhysicalRect child_rect = child->InkOverflow();
           if (!child_rect.IsEmpty()) {
-            child_rect.offset += child->OffsetInContainerBlock();
+            child_rect.offset += child->OffsetInContainerFragment();
             AddContentsVisualOverflow(child_rect);
           }
         }
@@ -2511,7 +2511,7 @@
             continue;
           const NGFragmentItem& child = *cursor.CurrentItem();
           LogicalRect logical_rect =
-              fragment->ConvertChildToLogical(child.RectInContainerBlock());
+              fragment->ConvertChildToLogical(child.RectInContainerFragment());
           logical_rect.size.inline_size += 1;
           AddLayoutOverflow(
               fragment->ConvertChildToPhysical(logical_rect).ToLayoutRect());
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index 8f52e6d..e07879f 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -492,7 +492,7 @@
     cursor.MoveTo(*this);
     if (cursor) {
       caret_rect.MoveBy(
-          cursor.Current().OffsetInContainerBlock().ToLayoutPoint());
+          cursor.Current().OffsetInContainerFragment().ToLayoutPoint());
     }
   }
 
@@ -1126,19 +1126,18 @@
     // inline may not start in the first fragment generated for the inline
     // formatting context.
     if (target_fragment_idx != -1)
-      target_fragment_idx += cursor.CurrentContainerFragmentIndex();
+      target_fragment_idx += cursor.ContainerFragmentIndex();
 
     for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
       if (target_fragment_idx != -1 &&
-          wtf_size_t(target_fragment_idx) !=
-              cursor.CurrentContainerFragmentIndex())
+          wtf_size_t(target_fragment_idx) != cursor.ContainerFragmentIndex())
         continue;
       if (const NGPaintFragment* paint_fragment =
               cursor.Current().PaintFragment()) {
         // NGBoxFragmentPainter::NodeAtPoint() takes an offset that is
         // accumulated up to the fragment itself. Compute this offset.
         const PhysicalOffset child_offset =
-            accumulated_offset + paint_fragment->OffsetInContainerBlock();
+            accumulated_offset + paint_fragment->OffsetInContainerFragment();
         if (NGBoxFragmentPainter(*paint_fragment)
                 .NodeAtPoint(result, hit_test_location, child_offset,
                              hit_test_action))
@@ -1152,7 +1151,7 @@
       // NGBoxFragmentPainter::NodeAtPoint() takes an offset that is accumulated
       // up to the fragment itself. Compute this offset.
       const PhysicalOffset child_offset =
-          accumulated_offset + item.OffsetInContainerBlock();
+          accumulated_offset + item.OffsetInContainerFragment();
       if (NGBoxFragmentPainter(cursor, item, *box_fragment)
               .NodeAtPoint(result, hit_test_location, child_offset,
                            accumulated_offset, hit_test_action)) {
@@ -1202,7 +1201,7 @@
     NGInlineCursor cursor(*parent_cursor);
     cursor.MoveToIncludingCulledInline(*this);
     for (; cursor; cursor.MoveToNextForSameLayoutObject())
-      yield(cursor.Current().RectInContainerBlock());
+      yield(cursor.Current().RectInContainerFragment());
   } else {
     DCHECK(!ContainingNGBlockFlow());
     CollectCulledLineBoxRects(yield);
@@ -1256,7 +1255,7 @@
     cursor.MoveToIncludingCulledInline(*this);
     PhysicalRect bounding_box;
     for (; cursor; cursor.MoveToNextForSameLayoutObject())
-      bounding_box.UniteIfNonZero(cursor.Current().RectInContainerBlock());
+      bounding_box.UniteIfNonZero(cursor.Current().RectInContainerFragment());
     return bounding_box;
   }
 
@@ -1409,7 +1408,7 @@
     cursor.MoveToIncludingCulledInline(*this);
     for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
       PhysicalRect child_rect = cursor.Current().InkOverflow();
-      child_rect.offset += cursor.Current().OffsetInContainerBlock();
+      child_rect.offset += cursor.Current().OffsetInContainerFragment();
       result.Unite(child_rect);
     }
     return result;
@@ -1525,7 +1524,7 @@
     NGInlineCursor cursor;
     cursor.MoveTo(*this);
     if (cursor)
-      return cursor.Current().RectInContainerBlock();
+      return cursor.Current().RectInContainerFragment();
   }
   if (const InlineFlowBox* flow_box = FirstLineBox())
     return FlipForWritingMode(flow_box->FrameRect());
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc
index 2de36b6..93d30845 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.cc
+++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -969,9 +969,9 @@
     const ComputedStyle& line_style = line_box.Current().Style();
     const auto writing_direction = line_style.GetWritingDirection();
     const WritingModeConverter converter(writing_direction,
-                                         line_box.BoxFragment().Size());
+                                         line_box.ContainerFragment().Size());
     const LogicalRect logical_rect =
-        converter.ToLogical(line_box.Current().RectInContainerBlock());
+        converter.ToLogical(line_box.Current().RectInContainerFragment());
     return {logical_rect.offset.block_offset, logical_rect.BlockEndOffset()};
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 12dfd14..04c716c5 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -433,7 +433,8 @@
         // Compute rect of the legacy text box.
         LayoutRect rect =
             cursor.CurrentLocalRect(clamped_start, clamped_end).ToLayoutRect();
-        rect.MoveBy(cursor.Current().OffsetInContainerBlock().ToLayoutPoint());
+        rect.MoveBy(
+            cursor.Current().OffsetInContainerFragment().ToLayoutPoint());
 
         // Compute start of the legacy text box.
         if (unit.AssociatedNode()) {
@@ -570,7 +571,7 @@
         if (cursor.Current().IsHiddenForPaint())
           continue;
       }
-      yield(cursor.Current().RectInContainerBlock());
+      yield(cursor.Current().RectInContainerFragment());
     }
     return;
   }
@@ -869,7 +870,7 @@
     // All/LayoutViewHitTestTest.HitTestVerticalRL/*
     NGInlineCursor cursor;
     for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) {
-      if (!EnclosingIntRect(cursor.Current().RectInContainerBlock())
+      if (!EnclosingIntRect(cursor.Current().RectInContainerFragment())
                .Contains(FlooredIntPoint(point)))
         continue;
       if (auto position_with_affinity = cursor.PositionForPointInChild(point)) {
@@ -1727,7 +1728,7 @@
       return PhysicalOffset();
     NGInlineCursor cursor;
     cursor.MoveTo(*this);
-    return cursor ? cursor.Current().OffsetInContainerBlock()
+    return cursor ? cursor.Current().OffsetInContainerFragment()
                   : PhysicalOffset();
   }
   if (const auto* text_box = FirstTextBox()) {
@@ -1750,10 +1751,11 @@
     cursor.MoveTo(*this);
     if (!cursor)
       return;
-    PhysicalOffset physical_offset = cursor.Current().OffsetInContainerBlock();
+    PhysicalOffset physical_offset =
+        cursor.Current().OffsetInContainerFragment();
     if (StyleRef().GetWritingDirection().IsHorizontalLtr()) {
       cursor.MoveToLastForSameLayoutObject();
-      logical_height = cursor.Current().RectInContainerBlock().Bottom() -
+      logical_height = cursor.Current().RectInContainerFragment().Bottom() -
                        physical_offset.top;
       logical_starting_point = {physical_offset.left, physical_offset.top};
       return;
@@ -1762,7 +1764,8 @@
     logical_starting_point = physical_offset.ConvertToLogical(
         StyleRef().GetWritingDirection(), outer_size, cursor.Current().Size());
     cursor.MoveToLastForSameLayoutObject();
-    PhysicalRect last_physical_rect = cursor.Current().RectInContainerBlock();
+    PhysicalRect last_physical_rect =
+        cursor.Current().RectInContainerFragment();
     LogicalOffset logical_ending_point =
         WritingModeConverter(StyleRef().GetWritingDirection(), outer_size)
             .ToLogical(last_physical_rect)
@@ -2330,7 +2333,7 @@
       if (status.start == status.end)
         continue;
       PhysicalRect item_rect = ComputeLocalSelectionRectForText(cursor, status);
-      item_rect.offset += cursor.Current().OffsetInContainerBlock();
+      item_rect.offset += cursor.Current().OffsetInContainerFragment();
       rect.Unite(item_rect);
     }
     return rect;
diff --git a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
index a84ae32..3d9ec79 100644
--- a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
@@ -456,7 +456,7 @@
     const NGFragment fragment(paint_fragment->Style().GetWritingDirection(),
                               *physical_text_fragment);
     WriteTextFragment(ts, paint_fragment->GetLayoutObject(),
-                      paint_fragment->RectInContainerBlock(),
+                      paint_fragment->RectInContainerFragment(),
                       paint_fragment->Style(), physical_text_fragment->Text(),
                       fragment.InlineSize());
     return;
@@ -467,7 +467,7 @@
          item.Type() == NGFragmentItem::kGeneratedText);
   const LayoutUnit inline_size =
       item.IsHorizontal() ? item.Size().width : item.Size().height;
-  WriteTextFragment(ts, item.GetLayoutObject(), item.RectInContainerBlock(),
+  WriteTextFragment(ts, item.GetLayoutObject(), item.RectInContainerFragment(),
                     item.Style(), item.Text(cursor.Items()), inline_size);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
index fb3349f..1aecb8a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
@@ -217,7 +217,7 @@
   const NGInlineCursor& cursor = GetCursor();
   if (!cursor)
     return LayoutRect();
-  return cursor.Current().RectInContainerBlock().ToLayoutRect();
+  return cursor.Current().RectInContainerFragment().ToLayoutRect();
 }
 
 unsigned NGAbstractInlineTextBox::Len() const {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc
index 55fb454d..41f54b3 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc
@@ -22,8 +22,8 @@
   line_box.MoveToContainingLine();
   DCHECK(line_box);
   const PhysicalOffset offset_to_line_box =
-      cursor.Current().OffsetInContainerBlock() -
-      line_box.Current().OffsetInContainerBlock();
+      cursor.Current().OffsetInContainerFragment() -
+      line_box.Current().OffsetInContainerFragment();
   LayoutUnit caret_height = is_horizontal ? line_box.Current().Size().height
                                           : line_box.Current().Size().width;
   LayoutUnit caret_top =
@@ -100,14 +100,14 @@
 
   // Adjust the location to be relative to the inline formatting context.
   PhysicalOffset caret_location = PhysicalOffset(caret_left, caret_top) +
-                                  cursor.Current().OffsetInContainerBlock();
+                                  cursor.Current().OffsetInContainerFragment();
   const PhysicalSize caret_size(caret_width, caret_height);
 
-  const NGPhysicalBoxFragment& fragment = cursor.BoxFragment();
+  const NGPhysicalBoxFragment& fragment = cursor.ContainerFragment();
   NGInlineCursor line_box(cursor);
   line_box.MoveToContainingLine();
   const PhysicalOffset line_box_offset =
-      line_box.Current().OffsetInContainerBlock();
+      line_box.Current().OffsetInContainerFragment();
   const PhysicalRect line_box_rect(line_box_offset, line_box.Current().Size());
 
   const NGInlineBreakToken* break_token = line_box.Current().InlineBreakToken();
@@ -190,10 +190,10 @@
 
   PhysicalRect rect = caret_rect.rect;
   if (caret_position.cursor.Current().Style().IsHorizontalWritingMode()) {
-    rect.SetY(line_box.Current().OffsetInContainerBlock().top);
+    rect.SetY(line_box.Current().OffsetInContainerFragment().top);
     rect.SetHeight(line_box.Current().Size().height);
   } else {
-    rect.SetX(line_box.Current().OffsetInContainerBlock().left);
+    rect.SetX(line_box.Current().OffsetInContainerFragment().left);
     rect.SetHeight(line_box.Current().Size().width);
   }
   return {caret_rect.layout_object, rect};
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
index 4aea022..1a066b6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -496,7 +496,7 @@
     if (UNLIKELY(item.IsHiddenForPaint()))
       continue;
     PhysicalRect child_visual_rect = item.SelfInkOverflow();
-    child_visual_rect.offset += item.OffsetInContainerBlock();
+    child_visual_rect.offset += item.OffsetInContainerFragment();
     visual_rect.Unite(child_visual_rect);
   }
   return visual_rect;
@@ -526,7 +526,7 @@
     PhysicalRect child_rect;
     item->GetMutableForPainting().RecalcInkOverflow(*cursor, &child_rect);
     if (!child_rect.IsEmpty()) {
-      child_rect.offset += item->OffsetInContainerBlock();
+      child_rect.offset += item->OffsetInContainerFragment();
       contents_ink_overflow.Unite(child_rect);
     }
   }
@@ -584,7 +584,7 @@
 
   // |contents_rect| is relative to the inline formatting context. Make it
   // relative to |this|.
-  contents_rect.offset -= OffsetInContainerBlock();
+  contents_rect.offset -= OffsetInContainerFragment();
 
   if (Type() == kLine) {
     // Line boxes don't have self overflow. Compute content overflow only.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 96457d5a..ef61b80 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -160,8 +160,10 @@
   }
   void SetDeltaToNextForSameLayoutObject(wtf_size_t delta) const;
 
-  const PhysicalRect& RectInContainerBlock() const { return rect_; }
-  const PhysicalOffset& OffsetInContainerBlock() const { return rect_.offset; }
+  const PhysicalRect& RectInContainerFragment() const { return rect_; }
+  const PhysicalOffset& OffsetInContainerFragment() const {
+    return rect_.offset;
+  }
   const PhysicalSize& Size() const { return rect_.size; }
   PhysicalRect LocalRect() const { return {PhysicalOffset(), Size()}; }
   void SetOffset(const PhysicalOffset& offset) { rect_.offset = offset; }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
index c1cdf0b..56039f1b 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
@@ -175,14 +175,14 @@
   const NGFragmentItem& text1 = *items_for_text[0];
   EXPECT_EQ(text1.Type(), NGFragmentItem::kText);
   EXPECT_EQ(text1.GetLayoutObject(), layout_text);
-  EXPECT_EQ(text1.OffsetInContainerBlock(), PhysicalOffset());
+  EXPECT_EQ(text1.OffsetInContainerFragment(), PhysicalOffset());
   EXPECT_TRUE(text1.IsFirstForNode());
   EXPECT_FALSE(text1.IsLastForNode());
 
   const NGFragmentItem& text2 = *items_for_text[1];
   EXPECT_EQ(text2.Type(), NGFragmentItem::kText);
   EXPECT_EQ(text2.GetLayoutObject(), layout_text);
-  EXPECT_EQ(text2.OffsetInContainerBlock(), PhysicalOffset(0, 10));
+  EXPECT_EQ(text2.OffsetInContainerFragment(), PhysicalOffset(0, 10));
   EXPECT_FALSE(text2.IsFirstForNode());
   EXPECT_TRUE(text2.IsLastForNode());
 }
@@ -282,12 +282,12 @@
   EXPECT_TRUE(items_for_span1[0]->IsFirstForNode());
   EXPECT_FALSE(items_for_span1[0]->IsLastForNode());
   EXPECT_EQ(PhysicalOffset(40, 0),
-            items_for_span1[0]->OffsetInContainerBlock());
+            items_for_span1[0]->OffsetInContainerFragment());
   EXPECT_EQ(PhysicalRect(0, 0, 40, 10), items_for_span1[0]->InkOverflow());
   EXPECT_FALSE(items_for_span1[1]->IsFirstForNode());
   EXPECT_TRUE(items_for_span1[1]->IsLastForNode());
   EXPECT_EQ(PhysicalOffset(0, 10),
-            items_for_span1[1]->OffsetInContainerBlock());
+            items_for_span1[1]->OffsetInContainerFragment());
   EXPECT_EQ(PhysicalRect(0, 0, 40, 10), items_for_span1[1]->InkOverflow());
 
   // "span2" doesn't wrap, produces only one fragment.
@@ -298,7 +298,7 @@
   EXPECT_TRUE(items_for_span2[0]->IsFirstForNode());
   EXPECT_TRUE(items_for_span2[0]->IsLastForNode());
   EXPECT_EQ(PhysicalOffset(0, 20),
-            items_for_span2[0]->OffsetInContainerBlock());
+            items_for_span2[0]->OffsetInContainerFragment());
   EXPECT_EQ(PhysicalRect(0, 0, 80, 10), items_for_span2[0]->InkOverflow());
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
index b38cd85..38d9dea 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
@@ -246,7 +246,7 @@
     DCHECK(!item.IsDirty());
 
     const LogicalOffset item_offset =
-        converter.ToLogical(item.OffsetInContainerBlock(), item.Size());
+        converter.ToLogical(item.OffsetInContainerFragment(), item.Size());
 
     if (item.Type() == NGFragmentItem::kLine) {
       DCHECK(item.LineBoxFragment());
@@ -276,7 +276,7 @@
       }
 
       items_.emplace_back(item_offset, item);
-      const PhysicalRect line_box_bounds = item.RectInContainerBlock();
+      const PhysicalRect line_box_bounds = item.RectInContainerFragment();
       line_converter.SetOuterSize(line_box_bounds.size);
       for (NGInlineCursor line = cursor.CursorForDescendants(); line;
            line.MoveToNext()) {
@@ -284,7 +284,7 @@
         DCHECK(line_child.CanReuse());
         items_.emplace_back(
             line_converter.ToLogical(
-                line_child.OffsetInContainerBlock() - line_box_bounds.offset,
+                line_child.OffsetInContainerFragment() - line_box_bounds.offset,
                 line_child.Size()),
             line_child);
       }
@@ -340,7 +340,7 @@
       unsigned descendants_count = item->DescendantsCount();
       DCHECK(descendants_count);
       if (descendants_count) {
-        const PhysicalRect line_box_bounds = item->RectInContainerBlock();
+        const PhysicalRect line_box_bounds = item->RectInContainerFragment();
         line_converter.SetOuterSize(line_box_bounds.size);
         while (--descendants_count) {
           ++iter;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index a7416ca..8ba4f0f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -529,11 +529,11 @@
   return PhysicalRect();
 }
 
-const PhysicalOffset NGInlineCursorPosition::OffsetInContainerBlock() const {
+const PhysicalOffset NGInlineCursorPosition::OffsetInContainerFragment() const {
   if (paint_fragment_)
-    return paint_fragment_->OffsetInContainerBlock();
+    return paint_fragment_->OffsetInContainerFragment();
   if (item_)
-    return item_->OffsetInContainerBlock();
+    return item_->OffsetInContainerFragment();
   NOTREACHED();
   return PhysicalOffset();
 }
@@ -547,12 +547,13 @@
   return PhysicalSize();
 }
 
-const PhysicalRect NGInlineCursorPosition::RectInContainerBlock() const {
+const PhysicalRect NGInlineCursorPosition::RectInContainerFragment() const {
   if (paint_fragment_) {
-    return {paint_fragment_->OffsetInContainerBlock(), paint_fragment_->Size()};
+    return {paint_fragment_->OffsetInContainerFragment(),
+            paint_fragment_->Size()};
   }
   if (item_)
-    return item_->RectInContainerBlock();
+    return item_->RectInContainerFragment();
   NOTREACHED();
   return PhysicalRect();
 }
@@ -654,12 +655,12 @@
 }
 
 PhysicalRect NGInlineCursor::CurrentRectInBlockFlow() const {
-  PhysicalRect rect = Current().RectInContainerBlock();
+  PhysicalRect rect = Current().RectInContainerFragment();
   // We'll now convert the offset from being relative to the containing fragment
   // to being relative to the containing LayoutBlockFlow. For writing modes that
   // don't flip the block direction, this is easy: just add the block-size
   // consumed in previous fragments.
-  auto writing_direction = BoxFragment().Style().GetWritingDirection();
+  auto writing_direction = ContainerFragment().Style().GetWritingDirection();
   switch (writing_direction.GetWritingMode()) {
     default:
       rect.offset.top += previously_consumed_block_size_;
@@ -676,9 +677,9 @@
       const LayoutBlock* containing_block =
           Current().GetLayoutObject()->ContainingBlock();
       DCHECK_EQ(containing_block->StyleRef().GetWritingDirection(),
-                BoxFragment().Style().GetWritingDirection());
+                ContainerFragment().Style().GetWritingDirection());
       LogicalOffset logical_offset = rect.offset.ConvertToLogical(
-          writing_direction, BoxFragment().Size(), rect.size);
+          writing_direction, ContainerFragment().Size(), rect.size);
       LogicalOffset logical_offset_in_flow_thread(
           logical_offset.inline_offset,
           logical_offset.block_offset + previously_consumed_block_size_);
@@ -754,7 +755,7 @@
       }
       // Try to resolve if |point| falls in a line box in block direction.
       const LayoutUnit child_block_offset =
-          child_item->OffsetInContainerBlock()
+          child_item->OffsetInContainerFragment()
               .ConvertToLogical(writing_direction, container_size,
                                 child_item->Size())
               .block_offset;
@@ -879,7 +880,7 @@
       continue;
     }
     const LayoutUnit child_inline_offset =
-        child_item->OffsetInContainerBlock()
+        child_item->OffsetInContainerFragment()
             .ConvertToLogical(writing_direction, container_size,
                               child_item->Size())
             .inline_offset;
@@ -945,7 +946,7 @@
     const PhysicalOffset& point_in_container) const {
   if (auto* paint_fragment = CurrentPaintFragment()) {
     const PhysicalOffset point_in_child =
-        point_in_container - paint_fragment->OffsetInContainerBlock();
+        point_in_container - paint_fragment->OffsetInContainerFragment();
     return paint_fragment->PositionForPoint(point_in_child);
   }
   DCHECK(CurrentItem());
@@ -953,7 +954,7 @@
   switch (child_item.Type()) {
     case NGFragmentItem::kText:
       return child_item.PositionForPointInText(
-          point_in_container - child_item.OffsetInContainerBlock(), *this);
+          point_in_container - child_item.OffsetInContainerFragment(), *this);
     case NGFragmentItem::kGeneratedText:
       break;
     case NGFragmentItem::kBox:
@@ -965,7 +966,7 @@
           // Example: <b style="display:inline-block"><div>b</div></b>
           // [1] NGInlineCursorTest.PositionForPointInChildBlockChildren
           return child_item.GetLayoutObject()->PositionForPoint(
-              point_in_container - child_item.OffsetInContainerBlock());
+              point_in_container - child_item.OffsetInContainerFragment());
         }
       } else {
         // |LayoutInline| used to be culled.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
index 0bde454..a0fd6be 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -123,8 +123,8 @@
   const NGInlineBreakToken* InlineBreakToken() const;
 
   // The offset relative to the root of the inline formatting context.
-  const PhysicalRect RectInContainerBlock() const;
-  const PhysicalOffset OffsetInContainerBlock() const;
+  const PhysicalRect RectInContainerFragment() const;
+  const PhysicalOffset OffsetInContainerFragment() const;
   const PhysicalSize Size() const;
 
   // InkOverflow of itself, including contents if they contribute to the ink
@@ -246,19 +246,19 @@
   }
 
   // Returns the |NGPhysicalBoxFragment| that owns |Items|.
-  const NGPhysicalBoxFragment& BoxFragment() const {
+  const NGPhysicalBoxFragment& ContainerFragment() const {
     DCHECK(root_box_fragment_);
     return *root_box_fragment_;
   }
 
+  // Return the index of the current physical box fragment of the containing
+  // block. An inline formatting context may be block fragmented.
+  wtf_size_t ContainerFragmentIndex() const { return fragment_index_; }
+
   // Returns the |LayoutBlockFlow| containing this cursor.
   // When |this| is a column box, returns the multicol container.
   const LayoutBlockFlow* GetLayoutBlockFlow() const;
 
-  // Return the index of the current physical box fragment of the containing
-  // block. An inline formatting context may be block fragmented.
-  wtf_size_t CurrentContainerFragmentIndex() const { return fragment_index_; }
-
   //
   // Functions to query the current position.
   //
@@ -320,8 +320,8 @@
   // first column, and the last three lines will end up in the second column. So
   // we get two box fragments generated for #container - one for each column.
   //
-  // The offsets returned from these methods will be (OffsetInContainerBlock()
-  // values in parentheses):
+  // The offsets returned from these methods will be
+  // (OffsetInContainerFragment() values in parentheses):
   //
   // line1: 0,0   (0,0)
   // line2: 0,20  (0,20)
@@ -334,8 +334,8 @@
   // engine to calculate offsets relatively to some ancestor.
   PhysicalRect CurrentRectInBlockFlow() const;
   PhysicalOffset CurrentOffsetInBlockFlow() const {
-    DCHECK_EQ(Current().OffsetInContainerBlock(),
-              Current().RectInContainerBlock().offset);
+    DCHECK_EQ(Current().OffsetInContainerFragment(),
+              Current().RectInContainerFragment().offset);
     return CurrentRectInBlockFlow().offset;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
index cb8d0476..a7bfad1f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
@@ -769,12 +769,12 @@
   const auto& text = *To<Text>(GetElementById("root")->firstChild());
   ASSERT_TRUE(cursor.Current().IsLineBox());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
-            cursor.Current().RectInContainerBlock());
+            cursor.Current().RectInContainerFragment());
 
   cursor.MoveTo(*text.GetLayoutObject());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 15), PhysicalSize(20, 10)),
-            cursor.Current().RectInContainerBlock());
-  const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+            cursor.Current().RectInContainerFragment());
+  const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
 
   EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
             cursor.PositionForPointInChild(left_top + PhysicalOffset(-5, 0)));
@@ -806,12 +806,12 @@
       *To<Text>(GetElementById("root")->firstChild()->firstChild());
   ASSERT_TRUE(cursor.Current().IsLineBox());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(754, 10), PhysicalSize(20, 20)),
-            cursor.Current().RectInContainerBlock());
+            cursor.Current().RectInContainerFragment());
 
   cursor.MoveTo(*text.GetLayoutObject());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(754, 15), PhysicalSize(20, 10)),
-            cursor.Current().RectInContainerBlock());
-  const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+            cursor.Current().RectInContainerFragment());
+  const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
 
   EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
             cursor.PositionForPointInChild(left_top + PhysicalOffset(-5, 0)));
@@ -842,12 +842,12 @@
   const auto& text = *To<Text>(GetElementById("root")->firstChild());
   ASSERT_TRUE(cursor.Current().IsLineBox());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
-            cursor.Current().RectInContainerBlock());
+            cursor.Current().RectInContainerFragment());
 
   cursor.MoveTo(*text.GetLayoutObject());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(15, 10), PhysicalSize(10, 20)),
-            cursor.Current().RectInContainerBlock());
-  const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+            cursor.Current().RectInContainerFragment());
+  const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
 
   EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
             cursor.PositionForPointInChild(left_top + PhysicalOffset(0, -5)));
@@ -879,12 +879,12 @@
       *To<Text>(GetElementById("root")->firstChild()->firstChild());
   ASSERT_TRUE(cursor.Current().IsLineBox());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
-            cursor.Current().RectInContainerBlock());
+            cursor.Current().RectInContainerFragment());
 
   cursor.MoveTo(*text.GetLayoutObject());
   EXPECT_EQ(PhysicalRect(PhysicalOffset(15, 10), PhysicalSize(10, 20)),
-            cursor.Current().RectInContainerBlock());
-  const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+            cursor.Current().RectInContainerFragment());
+  const PhysicalOffset left_top = cursor.Current().OffsetInContainerFragment();
 
   EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
             cursor.PositionForPointInChild(left_top + PhysicalOffset(0, -5)));
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
index b9641ae..4007ac8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
@@ -181,7 +181,7 @@
   for (const NGPaintFragment* fragment :
        NGPaintFragment::InlineFragmentsFor(layout_object)) {
     result.push_back(Result{&fragment->PhysicalFragment(),
-                            fragment->OffsetInContainerBlock()});
+                            fragment->OffsetInContainerFragment()});
   }
   return result;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc
index 8a23339..5a18b00 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc
@@ -229,7 +229,7 @@
   EXPECT_EQ(LayoutUnit(96), cursor.Current().Size().height);
   cursor.MoveToNext();
   ASSERT_TRUE(cursor);
-  EXPECT_EQ(LayoutUnit(0), cursor.Current().OffsetInContainerBlock().top)
+  EXPECT_EQ(LayoutUnit(0), cursor.Current().OffsetInContainerFragment().top)
       << "Offset top of <img> should be zero.";
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index 0bfa525..a3286db 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -132,8 +132,8 @@
   // Make sure we include the inline-size of the line-box in the overflow.
   // Note, the bottom half-leading should not be included. crbug.com/996847
   const WritingMode container_writing_mode = container_style.GetWritingMode();
-  AddInlineSizeToOverflow(line.RectInContainerBlock(), container_writing_mode,
-                          &overflow);
+  AddInlineSizeToOverflow(line.RectInContainerFragment(),
+                          container_writing_mode, &overflow);
 
   return overflow;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 6c849a8..c9ab641 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -1423,7 +1423,7 @@
         continue;
       if (auto* layout_box = DynamicTo<LayoutBox>(layout_object)) {
         PhysicalOffset maybe_flipped_offset =
-            cursor.Current().OffsetInContainerBlock();
+            cursor.Current().OffsetInContainerFragment();
         if (initial_container_is_flipped) {
           maybe_flipped_offset.left = container.Size().width -
                                       child->Size().width -
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 0e4d54f..f6f52bd 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -117,7 +117,7 @@
     DCHECK(containing_block_geometry.has_value() ||
            !containing_lineboxes.first);
 
-    PhysicalRect fragment_rect = item->RectInContainerBlock();
+    PhysicalRect fragment_rect = item->RectInContainerFragment();
     fragment_rect.offset += box_offset;
     if (containing_lineboxes.first == linebox) {
       // Unite the start rect with the fragment's rect.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
index 86c79ff9..cc432b217 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -321,8 +321,33 @@
     scoped_refptr<const NGBlockBreakToken> content_break_token,
     LogicalSize adjusted_padding_box_size,
     bool has_legend) {
+  // If the following conditions meet, the content should be laid out with
+  // a block-size limitation:
+  // - The FIELDSET block-size is indefinite.
+  // - It has max-block-size.
+  // - The intrinsic block-size of the content is larger than the
+  //   max-block-size.
+  if (adjusted_padding_box_size.block_size == kIndefiniteSize) {
+    LayoutUnit max_content_block_size = ResolveMaxBlockLength(
+        ConstraintSpace(), Style(), BorderPadding(), Style().LogicalMaxHeight(),
+        LengthResolvePhase::kLayout);
+    if (max_content_block_size != LayoutUnit::Max()) {
+      max_content_block_size -= BorderPadding().BlockSum();
+
+      auto child_measure_space = CreateConstraintSpaceForFieldsetContent(
+          fieldset_content, adjusted_padding_box_size, intrinsic_block_size_,
+          NGCacheSlot::kMeasure);
+      LayoutUnit intrinsic_content_block_size =
+          fieldset_content
+              .Layout(child_measure_space, content_break_token.get())
+              ->IntrinsicBlockSize();
+      if (intrinsic_content_block_size > max_content_block_size)
+        adjusted_padding_box_size.block_size = max_content_block_size;
+    }
+  }
   auto child_space = CreateConstraintSpaceForFieldsetContent(
-      fieldset_content, adjusted_padding_box_size, intrinsic_block_size_);
+      fieldset_content, adjusted_padding_box_size, intrinsic_block_size_,
+      NGCacheSlot::kLayout);
   auto result = fieldset_content.Layout(child_space, content_break_token.get());
 
   // TODO(layout-dev): Handle abortions caused by block fragmentation.
@@ -418,11 +443,13 @@
 NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForFieldsetContent(
     NGBlockNode fieldset_content,
     LogicalSize padding_box_size,
-    LayoutUnit block_offset) {
+    LayoutUnit block_offset,
+    NGCacheSlot slot) {
   DCHECK(fieldset_content.CreatesNewFormattingContext());
   NGConstraintSpaceBuilder builder(
       ConstraintSpace(), fieldset_content.Style().GetWritingDirection(),
       /* is_new_fc */ true);
+  builder.SetCacheSlot(slot);
   builder.SetAvailableSize(padding_box_size);
   builder.SetStretchInlineSizeIfAuto(true);
   // We pass the container's PercentageResolutionSize because percentage
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
index d8f42ef2..1e79c03f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
@@ -51,7 +51,8 @@
   const NGConstraintSpace CreateConstraintSpaceForFieldsetContent(
       NGBlockNode fieldset_content,
       LogicalSize padding_box_size,
-      LayoutUnit block_offset);
+      LayoutUnit block_offset,
+      NGCacheSlot slot);
   bool IsFragmentainerOutOfSpace(LayoutUnit block_offset) const;
 
   const WritingDirectionMode writing_direction_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
index 649bbeb1..4102938 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc
@@ -377,7 +377,7 @@
   offset:unplaced size:1000x53
     offset:0,0 size:126x53
       offset:13,0 size:30x30
-      offset:3,30 size:120x220
+      offset:3,30 size:120x0
         offset:10,10 size:100x200
 )DUMP";
   EXPECT_EQ(expectation, dump);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc b/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
index aa65979..49ed2125 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
@@ -156,7 +156,7 @@
     current_.link_.fragment = nullptr;
     return;
   }
-  current_.link_ = {item->BoxFragment(), item->OffsetInContainerBlock()};
+  current_.link_ = {item->BoxFragment(), item->OffsetInContainerFragment()};
 }
 
 void NGFragmentChildIterator::SkipToBoxFragment() {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
index dde2465..755df23 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
@@ -154,7 +154,7 @@
   for (const auto& item : items) {
     if (const auto* line_box = item->LineBoxFragment()) {
       has_hanging = line_box->HasHanging();
-      line_rect = item->RectInContainerBlock();
+      line_rect = item->RectInContainerFragment();
 
       if (line_rect.IsEmpty())
         continue;
@@ -172,7 +172,7 @@
     }
 
     if (item->IsText()) {
-      PhysicalRect child_overflow = item->RectInContainerBlock();
+      PhysicalRect child_overflow = item->RectInContainerFragment();
 
       // Adjust the text's overflow if the line-box has hanging.
       if (UNLIKELY(has_hanging))
@@ -186,7 +186,7 @@
       // Use the default box-fragment overflow logic.
       PhysicalRect child_overflow =
           LayoutOverflowForPropagation(*child_box_fragment);
-      child_overflow.offset += item->OffsetInContainerBlock();
+      child_overflow.offset += item->OffsetInContainerFragment();
 
       // Only inline-boxes (not atomic-inlines) should be adjusted if the
       // line-box has hanging.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc
index 5bddc9d..c8556ec1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc
@@ -44,11 +44,11 @@
   // fragment. To do this, return |true| if |this| is the first inline fragment
   // of a block fragment.
   while (true) {
-    wtf_size_t fragment_index = cursor.CurrentContainerFragmentIndex();
+    wtf_size_t fragment_index = cursor.ContainerFragmentIndex();
     cursor.MoveToNextForSameLayoutObject();
     DCHECK(cursor);
     if (cursor.Current().BoxFragment() == &physical_fragment)
-      return fragment_index != cursor.CurrentContainerFragmentIndex();
+      return fragment_index != cursor.ContainerFragmentIndex();
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 022f956..aabfb7b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -559,7 +559,7 @@
       DCHECK_EQ(&child, cursor.CurrentItem());
       DCHECK_EQ(child.Type(), NGFragmentItem::kLine);
       if (padding_strut)
-        AddLineBoxRect(child.RectInContainerBlock());
+        AddLineBoxRect(child.RectInContainerFragment());
       const NGPhysicalLineBoxFragment* line_box = child.LineBoxFragment();
       DCHECK(line_box);
       PhysicalRect child_scrollable_overflow =
@@ -609,7 +609,7 @@
               item->PostLayoutBoxFragment()) {
         if (child_box->IsFloatingOrOutOfFlowPositioned()) {
           context.AddFloatingOrOutOfFlowPositionedChild(
-              *child_box, item->OffsetInContainerBlock());
+              *child_box, item->OffsetInContainerFragment());
         }
       }
     }
@@ -759,26 +759,26 @@
   NGInlineCursor cursor;
   cursor.MoveTo(*layout_object);
   DCHECK(cursor);
-  wtf_size_t fragment_index = cursor.CurrentContainerFragmentIndex();
+  wtf_size_t fragment_index = cursor.ContainerFragmentIndex();
   bool has_this_fragment = false;
   for (;; cursor.MoveToNextForSameLayoutObject()) {
     if (!cursor) {
       DCHECK(has_this_fragment);
       break;
     }
-    if (fragment_index != cursor.CurrentContainerFragmentIndex()) {
+    if (fragment_index != cursor.ContainerFragmentIndex()) {
       // If this block fragment has |this|, exit the loop.
       if (has_this_fragment)
         break;
       // Otherwise clear the result and continue to the next block fragment.
-      fragment_index = cursor.CurrentContainerFragmentIndex();
+      fragment_index = cursor.ContainerFragmentIndex();
       rects->Shrink(initial_rects_size);
     }
 
     const NGInlineCursorPosition& current = cursor.Current();
     has_this_fragment = has_this_fragment || current.BoxFragment() == this;
     if (!current.Size().IsZero())
-      rects->push_back(current.RectInContainerBlock());
+      rects->push_back(current.RectInContainerFragment());
 
     // Add descendants if any, in the container-relative coordinate.
     if (!current.HasChildren())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
index 9c63063..dd037ab 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
@@ -191,7 +191,7 @@
       continue;
     if (item.Type() == NGFragmentItem::kLine) {
       AddOutlineRectsForDescendant(
-          {item.LineBoxFragment(), item.OffsetInContainerBlock()},
+          {item.LineBoxFragment(), item.OffsetInContainerFragment()},
           outline_rects, additional_offset, outline_type, containing_block);
       continue;
     }
@@ -199,7 +199,7 @@
       if (outline_type == NGOutlineType::kDontIncludeBlockVisualOverflow)
         continue;
       outline_rects->push_back(
-          PhysicalRect(additional_offset + item.OffsetInContainerBlock(),
+          PhysicalRect(additional_offset + item.OffsetInContainerFragment(),
                        item.Size().ToLayoutSize()));
       continue;
     }
@@ -207,9 +207,9 @@
       if (const NGPhysicalBoxFragment* child_box =
               item.PostLayoutBoxFragment()) {
         DCHECK(!child_box->IsOutOfFlowPositioned());
-        AddOutlineRectsForDescendant({child_box, item.OffsetInContainerBlock()},
-                                     outline_rects, additional_offset,
-                                     outline_type, containing_block);
+        AddOutlineRectsForDescendant(
+            {child_box, item.OffsetInContainerFragment()}, outline_rects,
+            additional_offset, outline_type, containing_block);
       }
       continue;
     }
@@ -240,14 +240,14 @@
       continue;
     }
     if (item->IsText()) {
-      PhysicalRect child_scroll_overflow = item->RectInContainerBlock();
+      PhysicalRect child_scroll_overflow = item->RectInContainerFragment();
       if (height_type == TextHeightType::kEmHeight) {
         child_scroll_overflow = AdjustTextRectForEmHeight(
             child_scroll_overflow, item->Style(), item->TextShapeResult(),
             container_writing_mode);
       }
       if (UNLIKELY(has_hanging)) {
-        AdjustScrollableOverflowForHanging(line.RectInContainerBlock(),
+        AdjustScrollableOverflowForHanging(line.RectInContainerFragment(),
                                            container_writing_mode,
                                            &child_scroll_overflow);
       }
@@ -261,7 +261,7 @@
       PhysicalRect child_scroll_overflow;
       if (height_type == TextHeightType::kNormalHeight ||
           (child_box->BoxType() != kInlineBox && !IsRubyBox()))
-        child_scroll_overflow = item->RectInContainerBlock();
+        child_scroll_overflow = item->RectInContainerFragment();
       if (child_box->IsInlineBox()) {
         child_box->AddScrollableOverflowForInlineChild(
             container, container_style, line, has_hanging, descendants,
@@ -269,14 +269,14 @@
         child_box->AdjustScrollableOverflowForPropagation(
             container, height_type, &child_scroll_overflow);
         if (UNLIKELY(has_hanging)) {
-          AdjustScrollableOverflowForHanging(line.RectInContainerBlock(),
+          AdjustScrollableOverflowForHanging(line.RectInContainerFragment(),
                                              container_writing_mode,
                                              &child_scroll_overflow);
         }
       } else {
         child_scroll_overflow =
             child_box->ScrollableOverflowForPropagation(container, height_type);
-        child_scroll_overflow.offset += item->OffsetInContainerBlock();
+        child_scroll_overflow.offset += item->OffsetInContainerFragment();
       }
       overflow->Unite(child_scroll_overflow);
       descendants.MoveToNextSkippingChildren();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index 140c2e6..a7611dc 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -196,7 +196,7 @@
       const NGInlineCursorPosition& current = cursor->Current();
       if (const NGPhysicalBoxFragment* box = current.BoxFragment()) {
         if (!box->IsInlineBox()) {
-          Append(box, current.OffsetInContainerBlock(), indent);
+          Append(box, current.OffsetInContainerFragment(), indent);
           continue;
         }
       }
@@ -209,7 +209,7 @@
 
       if (flags_ & NGPhysicalFragment::DumpOffset) {
         builder_->Append(" offset:");
-        builder_->Append(current.OffsetInContainerBlock().ToString());
+        builder_->Append(current.OffsetInContainerFragment().ToString());
       }
       if (flags_ & NGPhysicalFragment::DumpSize) {
         builder_->Append(" size:");
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
index 812e89b..ca0814bd 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc
@@ -122,7 +122,7 @@
 void LayoutSVGShape::UpdateShapeFromElement() {
   NOT_DESTROYED();
   CreatePath();
-  fill_bounding_box_ = GetPath().BoundingRect();
+  fill_bounding_box_ = GetPath().TightBoundingRect();
 
   if (HasNonScalingStroke()) {
     // NonScalingStrokeTransform may depend on LocalTransform which in turn may
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
index 3580a09..0d011b4 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
@@ -64,12 +64,8 @@
 
 ApplicationCacheHost::ApplicationCacheHost(
     const BrowserInterfaceBrokerProxy& interface_broker_proxy,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    ContextLifecycleNotifier* notifier)
-    : backend_host_(notifier),
-      receiver_(this, notifier),
-      backend_remote_(notifier),
-      task_runner_(std::move(task_runner)),
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : task_runner_(std::move(task_runner)),
       interface_broker_proxy_(interface_broker_proxy) {}
 
 ApplicationCacheHost::~ApplicationCacheHost() = default;
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.h b/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
index aa23aeb9..9481bfb 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
@@ -41,7 +41,6 @@
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
@@ -59,8 +58,7 @@
  public:
   ApplicationCacheHost(
       const BrowserInterfaceBrokerProxy& interface_broker_proxy,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      ContextLifecycleNotifier* notifier);
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   ~ApplicationCacheHost() override;
   virtual void Detach();
 
@@ -114,9 +112,9 @@
   virtual void Trace(Visitor*) const;
 
  protected:
-  HeapMojoRemote<mojom::blink::AppCacheHost,
-                 HeapMojoWrapperMode::kForceWithoutContextObserver>
-      backend_host_;
+  // ApplicationCacheHost will be instantiated before ExecutionContext becomes
+  // available. So we can't supply ExecutionContext to HeapMojoRemote.
+  HeapMojoRemote<mojom::blink::AppCacheHost> backend_host_{nullptr};
   mojom::blink::AppCacheStatus status_ =
       mojom::blink::AppCacheStatus::APPCACHE_STATUS_UNCACHED;
 
@@ -134,13 +132,13 @@
 
   void GetAssociatedCacheInfo(CacheInfo* info);
 
-  HeapMojoReceiver<mojom::blink::AppCacheFrontend,
-                   ApplicationCacheHost,
-                   HeapMojoWrapperMode::kForceWithoutContextObserver>
-      receiver_;
-  HeapMojoRemote<mojom::blink::AppCacheBackend,
-                 HeapMojoWrapperMode::kForceWithoutContextObserver>
-      backend_remote_;
+  // ApplicationCacheHost will be instantiated before ExecutionContext becomes
+  // available. So we can't supply ExecutionContext to HeapMojoReceiver.
+  HeapMojoReceiver<mojom::blink::AppCacheFrontend, ApplicationCacheHost>
+      receiver_{this, nullptr};
+  // ApplicationCacheHost will be instantiated before ExecutionContext becomes
+  // available. So we can't supply ExecutionContext to HeapMojoRemote.
+  HeapMojoRemote<mojom::blink::AppCacheBackend> backend_remote_{nullptr};
 
   base::UnguessableToken host_id_;
   mojom::blink::AppCacheInfo cache_info_;
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.cc
index 68663e9..0e0f290 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.cc
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.cc
@@ -47,9 +47,7 @@
     const BrowserInterfaceBrokerProxy& interface_broker_proxy,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     const base::UnguessableToken& appcache_host_id)
-    : ApplicationCacheHost(interface_broker_proxy,
-                           std::move(task_runner),
-                           document_loader->GetFrame()->DomWindow()),
+    : ApplicationCacheHost(interface_broker_proxy, std::move(task_runner)),
       local_frame_(document_loader->GetFrame()),
       document_loader_(document_loader) {
   // PlzNavigate: The browser passes the ID to be used.
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.cc
index f046a63c..9cb4e9f 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.cc
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.cc
@@ -9,11 +9,8 @@
 ApplicationCacheHostForWorker::ApplicationCacheHostForWorker(
     const base::UnguessableToken& appcache_host_id,
     const BrowserInterfaceBrokerProxy& interface_broker_proxy,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    ContextLifecycleNotifier* notifier)
-    : ApplicationCacheHost(interface_broker_proxy,
-                           std::move(task_runner),
-                           notifier) {
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : ApplicationCacheHost(interface_broker_proxy, std::move(task_runner)) {
   SetHostID(appcache_host_id ? appcache_host_id
                              : base::UnguessableToken::Create());
   BindBackend();
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.h b/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.h
index ff632793..5a51ec7 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.h
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.h
@@ -14,8 +14,7 @@
   ApplicationCacheHostForWorker(
       const base::UnguessableToken& appcache_host_id,
       const BrowserInterfaceBrokerProxy& interface_broker_proxy,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      ContextLifecycleNotifier* notifier);
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   ~ApplicationCacheHostForWorker() override;
 };
 
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
index 4568bbd..9fd55179 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
@@ -1193,16 +1193,88 @@
   EXPECT_FALSE(inner_element_layer->subtree_property_changed());
 }
 
-TEST_P(CompositingSimTest, SafeOpaqueBackgroundColorGetsSet) {
-  // TODO(crbug.com/765003): CAP may make different layerization decisions and
-  // we cannot guarantee that both divs will be composited in this test. When
-  // CAP gets closer to launch, this test should be updated to pass.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return;
-
+TEST_P(CompositingSimTest, SafeOpaqueBackgroundColor) {
   InitializeWithHTML(R"HTML(
-      <!DOCTYPE html>
-      <style>
+    <!DOCTYPE html>
+    <style>
+      body { background: yellow; }
+      div {
+        position: absolute;
+        z-index: 1;
+        width: 20px;
+        height: 20px;
+        will-change: transform; /* Composited */
+      }
+      #opaque-color {
+        background: blue;
+      }
+      #opaque-image, #opaque-image-translucent-color {
+        background: linear-gradient(blue, green);
+      }
+      #partly-opaque div {
+        width: 15px;
+        height: 15px;
+        background: blue;
+        will-change: initial;
+      }
+      #translucent, #opaque-image-translucent-color div {
+        background: rgba(0, 255, 255, 0.5);
+        will-change: initial;
+      }
+    </style>
+    <div id="opaque-color"></div>
+    <div id="opaque-image"></div>
+    <div id="opaque-image-translucent-color">
+      <div></div>
+    </div>
+    <div id="partly-opaque">
+      <div></div>
+    </div>
+    <div id="translucent"></div>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  auto* opaque_color = CcLayerByDOMElementId("opaque-color");
+  EXPECT_TRUE(opaque_color->contents_opaque());
+  EXPECT_EQ(SK_ColorBLUE, opaque_color->background_color());
+  EXPECT_EQ(SK_ColorBLUE, opaque_color->SafeOpaqueBackgroundColor());
+
+  auto* opaque_image = CcLayerByDOMElementId("opaque-image");
+  EXPECT_TRUE(opaque_image->contents_opaque());
+  EXPECT_EQ(SK_ColorTRANSPARENT, opaque_image->background_color());
+  // Fallback to use the viewport background.
+  EXPECT_EQ(SK_ColorYELLOW, opaque_image->SafeOpaqueBackgroundColor());
+
+  const SkColor kTranslucentCyan = SkColorSetARGB(128, 0, 255, 255);
+  auto* opaque_image_translucent_color =
+      CcLayerByDOMElementId("opaque-image-translucent-color");
+  EXPECT_TRUE(opaque_image_translucent_color->contents_opaque());
+  EXPECT_EQ(kTranslucentCyan,
+            opaque_image_translucent_color->background_color());
+  // Use background_color() with the alpha channel forced to be opaque.
+  EXPECT_EQ(SK_ColorCYAN,
+            opaque_image_translucent_color->SafeOpaqueBackgroundColor());
+
+  auto* partly_opaque = CcLayerByDOMElementId("partly-opaque");
+  EXPECT_FALSE(partly_opaque->contents_opaque());
+  EXPECT_EQ(SK_ColorBLUE, partly_opaque->background_color());
+  // SafeOpaqueBackgroundColor() returns SK_ColorTRANSPARENT when
+  // background_color() is opaque and contents_opaque() is false.
+  EXPECT_EQ(SK_ColorTRANSPARENT, partly_opaque->SafeOpaqueBackgroundColor());
+
+  auto* translucent = CcLayerByDOMElementId("translucent");
+  EXPECT_FALSE(translucent->contents_opaque());
+  EXPECT_EQ(kTranslucentCyan, translucent->background_color());
+  // SafeOpaqueBackgroundColor() returns background_color() if it's not opaque
+  // and contents_opaque() is false.
+  EXPECT_EQ(kTranslucentCyan, translucent->SafeOpaqueBackgroundColor());
+}
+
+TEST_P(CompositingSimTest, SquashingLayerSafeOpaqueBackgroundColor) {
+  InitializeWithHTML(R"HTML(
+    <!DOCTYPE html>
+    <style>
       div {
         position: absolute;
         z-index: 1;
@@ -1223,52 +1295,32 @@
       #bottomright {
         top: 24px;
         left: 24px;
+        width: 100px;
+        height: 100px;
         background: cyan;
       }
-      </style>
-      <div id="behind"></div>
-      <div id="topleft"></div>
-      <div id="bottomright"></div>
+    </style>
+    <div id="behind"></div>
+    <div id="topleft"></div>
+    <div id="bottomright"></div>
   )HTML");
 
   Compositor().BeginFrame();
 
-  auto* behind_element = GetElementById("behind");
-  auto* behind_layer = CcLayerByDOMElementId("behind");
-  EXPECT_EQ(behind_layer->element_id(),
-            CompositorElementIdFromUniqueObjectId(
-                behind_element->GetLayoutObject()->UniqueId(),
-                CompositorElementIdNamespace::kPrimary));
-  EXPECT_EQ(behind_layer->SafeOpaqueBackgroundColor(), SK_ColorBLUE);
-
-  auto* grouped_mapping =
-      GetElementById("topleft")->GetLayoutBox()->Layer()->GroupedMapping();
-  ASSERT_TRUE(grouped_mapping);
-  ASSERT_TRUE(grouped_mapping->NonScrollingSquashingLayer());
-  auto& squashing_layer =
-      grouped_mapping->NonScrollingSquashingLayer()->CcLayer();
+  auto* squashing_layer = CcLayerByDOMElementId("topleft");
+  ASSERT_TRUE(squashing_layer);
+  EXPECT_EQ(gfx::Size(124, 124), squashing_layer->bounds());
 
   // Top left and bottom right are squashed.
   // This squashed layer should not be opaque, as it is squashing two squares
   // with some gaps between them.
-  EXPECT_FALSE(squashing_layer.contents_opaque());
-  // This shouldn't DCHECK.
-  squashing_layer.SafeOpaqueBackgroundColor();
-  // Because contents_opaque is false, the SafeOpaqueBackgroundColor() getter
-  // will return SK_ColorTRANSPARENT. So we need to grab the actual color,
-  // to make sure it's right.
-  SkColor squashed_bg_color =
-      squashing_layer.ActualSafeOpaqueBackgroundColorForTesting();
-  // The squashed layer should have a non-transparent safe opaque background
-  // color, that isn't blue. Exactly which color it is depends on heuristics,
-  // but it should be one of the two colors of the elements that created it.
-  EXPECT_NE(squashed_bg_color, SK_ColorBLUE);
-  EXPECT_EQ(SkColorGetA(squashed_bg_color), SK_AlphaOPAQUE);
-  // #behind is blue, which is SK_ColorBLUE
-  // #topleft is lime, which is SK_ColorGREEN
-  // #bottomright is cyan, which is SK_ColorCYAN
-  EXPECT_TRUE((squashed_bg_color == SK_ColorGREEN) ||
-              (squashed_bg_color == SK_ColorCYAN));
+  EXPECT_FALSE(squashing_layer->contents_opaque());
+  // The background color of #bottomright is used as the background color
+  // because it covers the most significant area of the squashing layer.
+  EXPECT_EQ(squashing_layer->background_color(), SK_ColorCYAN);
+  // SafeOpaqueBackgroundColor() returns SK_ColorTRANSPARENT when
+  // background_color() is opaque and contents_opaque() is false.
+  EXPECT_EQ(squashing_layer->SafeOpaqueBackgroundColor(), SK_ColorTRANSPARENT);
 }
 
 // Test that a pleasant checkerboard color is used in the presence of blending.
@@ -1283,6 +1335,7 @@
   auto* scrolling_contents = ScrollingContentsCcLayerByScrollElementId(
       RootCcLayer(),
       MainFrame().GetFrameView()->LayoutViewport()->GetScrollElementId());
+  EXPECT_EQ(scrolling_contents->background_color(), SK_ColorWHITE);
   EXPECT_EQ(scrolling_contents->SafeOpaqueBackgroundColor(), SK_ColorWHITE);
 }
 
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index c9d8315..e1ec4037 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -29,12 +29,12 @@
 // As the image do not have a lot of content, we down scale |visual_size| by the
 // ratio of |intrinsic_image_size|/|displayed_image_size| = 1/10000.
 //
-// * |visual_size| referes to the size of the |displayed_image_size| after
+// * |visual_size| refers to the size of the |displayed_image_size| after
 // clipping and transforming. The size is in the main-frame's coordinate.
-// * |displayed_image_size| refers to the paint size in the image object's
-// coordinate.
 // * |intrinsic_image_size| refers to the the image object's original size
 // before scaling. The size is in the image object's coordinate.
+// * |displayed_image_size| refers to the paint size in the image object's
+// coordinate.
 uint64_t DownScaleIfIntrinsicSizeIsSmaller(
     uint64_t visual_size,
     const uint64_t& intrinsic_image_size,
@@ -154,16 +154,6 @@
   RegisterNotifySwapTime();
 }
 
-void ImagePaintTimingDetector::LayoutObjectWillBeDestroyed(
-    const LayoutObject& object) {
-  if (!is_recording_)
-    return;
-
-  // The visible record removal has been handled by
-  // |NotifyImageRemoved|.
-  records_manager_.RemoveInvisibleRecordIfNeeded(object);
-}
-
 void ImagePaintTimingDetector::NotifyImageRemoved(
     const LayoutObject& object,
     const ImageResourceContent* cached_image) {
@@ -171,6 +161,7 @@
     return;
   RecordId record_id = std::make_pair(&object, cached_image);
   records_manager_.RemoveImageFinishedRecord(record_id);
+  records_manager_.RemoveInvisibleRecordIfNeeded(record_id);
   if (!records_manager_.IsRecordedVisibleImage(record_id))
     return;
   records_manager_.RemoveVisibleRecord(record_id);
@@ -235,10 +226,10 @@
   Node* node = object.GetNode();
   if (!node)
     return;
-  if (records_manager_.IsRecordedInvisibleImage(object))
-    return;
 
   RecordId record_id = std::make_pair(&object, &cached_image);
+  if (records_manager_.IsRecordedInvisibleImage(record_id))
+    return;
   bool is_recorded_visible_image =
       records_manager_.IsRecordedVisibleImage(record_id);
   if (int depth = IgnorePaintTimingScope::IgnoreDepth()) {
@@ -282,7 +273,7 @@
                                             current_paint_chunk_properties,
                                             object, cached_image);
   if (rect_size == 0) {
-    records_manager_.RecordInvisible(object);
+    records_manager_.RecordInvisible(record_id);
   } else {
     records_manager_.RecordVisible(record_id, rect_size);
     if (cached_image.IsLoaded()) {
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
index 2a36fc9..7b07678 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -77,8 +77,8 @@
   ImageRecordsManager& operator=(const ImageRecordsManager&) = delete;
   ImageRecord* FindLargestPaintCandidate() const;
 
-  inline void RemoveInvisibleRecordIfNeeded(const LayoutObject& object) {
-    invisible_images_.erase(&object);
+  inline void RemoveInvisibleRecordIfNeeded(const RecordId& record_id) {
+    invisible_images_.erase(record_id);
   }
 
   inline void RemoveImageFinishedRecord(const RecordId& record_id) {
@@ -106,15 +106,15 @@
     // record will be removed in |AssignPaintTimeToRegisteredQueuedRecords|.
   }
 
-  inline void RecordInvisible(const LayoutObject& object) {
-    invisible_images_.insert(&object);
+  inline void RecordInvisible(const RecordId& record_id) {
+    invisible_images_.insert(record_id);
   }
   void RecordVisible(const RecordId& record_id, const uint64_t& visual_size);
   bool IsRecordedVisibleImage(const RecordId& record_id) const {
     return visible_images_.Contains(record_id);
   }
-  bool IsRecordedInvisibleImage(const LayoutObject& object) const {
-    return invisible_images_.Contains(&object);
+  bool IsRecordedInvisibleImage(const RecordId& record_id) const {
+    return invisible_images_.Contains(record_id);
   }
 
   void NotifyImageFinished(const RecordId& record_id) {
@@ -194,7 +194,7 @@
   }
 
   HashMap<RecordId, std::unique_ptr<ImageRecord>> visible_images_;
-  HashSet<const LayoutObject*> invisible_images_;
+  HashSet<RecordId> invisible_images_;
 
   // This stores the image records, which are ordered by size.
   ImageRecordSet size_ordered_set_;
@@ -259,7 +259,6 @@
                    const IntRect& image_border);
   void NotifyImageFinished(const LayoutObject&, const ImageResourceContent*);
   void OnPaintFinished();
-  void LayoutObjectWillBeDestroyed(const LayoutObject&);
   void NotifyImageRemoved(const LayoutObject&, const ImageResourceContent*);
   // After the method being called, the detector stops to record new entries and
   // node removal. But it still observe the loading status. In other words, if
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 1402910c..79617a86a 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -188,7 +188,7 @@
   // To be passed as |accumulated_offset| to LayoutInline::HitTestCulledInline,
   // where it equals the physical offset of the containing block in paint layer.
   const PhysicalOffset fallback_accumulated_offset =
-      physical_offset - fragment.OffsetInContainerBlock();
+      physical_offset - fragment.OffsetInContainerFragment();
   const LayoutObject* limit_layout_object =
       parent.PhysicalFragment().IsLineBox() ? parent.Parent()->GetLayoutObject()
                                             : parent.GetLayoutObject();
@@ -212,7 +212,7 @@
   // To be passed as |accumulated_offset| to LayoutInline::HitTestCulledInline,
   // where it equals the physical offset of the containing block in paint layer.
   const PhysicalOffset fallback_accumulated_offset =
-      physical_offset - item.OffsetInContainerBlock();
+      physical_offset - item.OffsetInContainerFragment();
   return HitTestCulledInlineAncestors(
       result, parent_cursor, item.GetLayoutObject(),
       // Limit the traversal up to the container fragment, or its container if
@@ -291,7 +291,7 @@
           continue;
         }
 
-        PhysicalRect box_rect(child->OffsetInContainerBlock() + paint_offset,
+        PhysicalRect box_rect(child->OffsetInContainerFragment() + paint_offset,
                               child->Size());
         backplates.AddTextRect(box_rect);
       }
@@ -307,7 +307,7 @@
         }
 
         PhysicalRect box_rect(
-            child_item->OffsetInContainerBlock() + paint_offset,
+            child_item->OffsetInContainerFragment() + paint_offset,
             child_item->Size());
         backplates.AddTextRect(box_rect);
       }
@@ -613,10 +613,10 @@
         DCHECK(box_item_);
         NGInlineCursor descendants = inline_box_cursor_->CursorForDescendants();
         const PhysicalOffset paint_offset_to_inline_formatting_context =
-            paint_offset - box_item_->OffsetInContainerBlock();
+            paint_offset - box_item_->OffsetInContainerFragment();
         PaintInlineItems(paint_info.ForDescendants(),
                          paint_offset_to_inline_formatting_context,
-                         box_item_->OffsetInContainerBlock(), &descendants);
+                         box_item_->OffsetInContainerFragment(), &descendants);
       } else if (items_) {
         if (fragment.IsBlockFlow()) {
           PaintBlockFlowContents(paint_info, paint_offset);
@@ -1580,7 +1580,7 @@
     // because soft-wrap and <br> needs to paint outside of InkOverflow() in
     // inline direction.
     const PhysicalOffset& child_offset =
-        paint_offset + child_item->OffsetInContainerBlock();
+        paint_offset + child_item->OffsetInContainerFragment();
     const PhysicalRect child_rect = child_item->InkOverflow();
     if (is_horizontal) {
       LayoutUnit y = child_rect.offset.top + child_offset.top;
@@ -1603,7 +1603,8 @@
                    line_fragment_id++, paint_info, child_offset);
       NGInlineCursor line_box_cursor = children->CursorForDescendants();
       PaintInlineItems(paint_info, paint_offset,
-                       child_item->OffsetInContainerBlock(), &line_box_cursor);
+                       child_item->OffsetInContainerFragment(),
+                       &line_box_cursor);
       continue;
     }
 
@@ -1745,7 +1746,8 @@
 
   // Skip if this child does not intersect with CullRect.
   if (!paint_info.IntersectsCullRect(
-          item.InkOverflow(), paint_offset + item.OffsetInContainerBlock()) &&
+          item.InkOverflow(),
+          paint_offset + item.OffsetInContainerFragment()) &&
       // Don't skip <br>, it doesn't have ink but need to paint selection.
       !(item.IsLineBreak() && HasSelection(item.GetLayoutObject())))
     return;
@@ -1772,7 +1774,7 @@
 
   // Skip if this child does not intersect with CullRect.
   if (!paint_info.IntersectsCullRect(
-          item.InkOverflow(), paint_offset + item.OffsetInContainerBlock()))
+          item.InkOverflow(), paint_offset + item.OffsetInContainerFragment()))
     return;
 
   if (child_fragment.IsAtomicInline() || child_fragment.IsListMarker()) {
@@ -1804,7 +1806,7 @@
 
   // Skip if this child does not intersect with CullRect.
   if (!paint_info.IntersectsCullRect(
-          item.InkOverflow(), paint_offset + item.OffsetInContainerBlock()))
+          item.InkOverflow(), paint_offset + item.OffsetInContainerFragment()))
     return;
 
   // This |item| is a culled inline box.
@@ -1869,7 +1871,7 @@
   NGInlineBoxFragmentPainter inline_box_painter(*inline_box_cursor_,
                                                 *box_item_);
   PaintTextClipMask(paint_info,
-                    paint_offset - box_item_->OffsetInContainerBlock(),
+                    paint_offset - box_item_->OffsetInContainerFragment(),
                     &inline_box_painter);
 }
 
@@ -2057,14 +2059,14 @@
         if (hit_test.AddNodeToResult(
                 fragment.NodeForHitTest(), /* box_fragment */ nullptr,
                 bounds_rect,
-                physical_offset - box_item_->OffsetInContainerBlock()))
+                physical_offset - box_item_->OffsetInContainerFragment()))
           return true;
       } else if (paint_fragment_ &&
                  paint_fragment_->PhysicalFragment().IsInline()) {
         if (hit_test.AddNodeToResult(
                 fragment.NodeForHitTest(), /* box_fragment */ nullptr,
                 bounds_rect,
-                physical_offset - paint_fragment_->OffsetInContainerBlock()))
+                physical_offset - paint_fragment_->OffsetInContainerFragment()))
           return true;
       } else {
         if (hit_test.AddNodeToResult(fragment.NodeForHitTest(), &box_fragment_,
@@ -2140,7 +2142,7 @@
 
   return hit_test.AddNodeToResult(
       text_fragment.NodeForHitTest(), /* box_fragment */ nullptr, rect,
-      physical_offset - text_paint_fragment->OffsetInContainerBlock());
+      physical_offset - text_paint_fragment->OffsetInContainerFragment());
 }
 
 bool NGBoxFragmentPainter::HitTestTextItem(const HitTestContext& hit_test,
@@ -2154,7 +2156,7 @@
 
   // TODO(layout-dev): Clip to line-top/bottom.
   const PhysicalOffset offset =
-      hit_test.inline_root_offset + text_item.OffsetInContainerBlock();
+      hit_test.inline_root_offset + text_item.OffsetInContainerFragment();
   PhysicalRect border_rect(offset, text_item.Size());
   PhysicalRect rect(PixelSnappedIntRect(border_rect));
   if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() &
@@ -2225,7 +2227,7 @@
 
   return hit_test.AddNodeToResult(
       fragment.NodeForHitTest(), &box_fragment_, bounds_rect,
-      physical_offset - cursor.Current().OffsetInContainerBlock());
+      physical_offset - cursor.Current().OffsetInContainerFragment());
 }
 
 bool NGBoxFragmentPainter::HitTestChildBoxFragment(
@@ -2298,7 +2300,7 @@
 
   if (const NGPhysicalBoxFragment* child_fragment = item.BoxFragment()) {
     const PhysicalOffset child_offset =
-        hit_test.inline_root_offset + item.OffsetInContainerBlock();
+        hit_test.inline_root_offset + item.OffsetInContainerFragment();
     return HitTestChildBoxFragment(hit_test, *child_fragment, cursor,
                                    child_offset);
   }
@@ -2314,7 +2316,7 @@
   if (hit_test.action == kHitTestForeground &&
       IsVisibleToHitTest(item, hit_test.result->GetHitTestRequest())) {
     const PhysicalOffset child_offset =
-        hit_test.inline_root_offset + item.OffsetInContainerBlock();
+        hit_test.inline_root_offset + item.OffsetInContainerFragment();
     PhysicalRect bounds_rect(child_offset, item.Size());
     if (UNLIKELY(hit_test.result->GetHitTestRequest().GetType() &
                  HitTestRequest::kHitTestVisualOverflow)) {
@@ -2505,7 +2507,7 @@
       const NGPhysicalLineBoxFragment* child_fragment = item->LineBoxFragment();
       DCHECK(child_fragment);
       const PhysicalOffset child_offset =
-          hit_test.inline_root_offset + item->OffsetInContainerBlock();
+          hit_test.inline_root_offset + item->OffsetInContainerFragment();
       if (HitTestLineBoxFragment(hit_test, *child_fragment, cursor,
                                  child_offset))
         return true;
@@ -2523,7 +2525,7 @@
       // Hit test culled inline boxes between |fragment| and its parent
       // fragment.
       const PhysicalOffset child_offset =
-          hit_test.inline_root_offset + item->OffsetInContainerBlock();
+          hit_test.inline_root_offset + item->OffsetInContainerFragment();
       if (HitTestCulledInlineAncestors(*hit_test.result, container, children,
                                        *item, cursor.Current(),
                                        hit_test.location, child_offset))
@@ -2631,7 +2633,7 @@
           continue;
 
         const PhysicalOffset child_offset =
-            accumulated_offset + item->OffsetInContainerBlock();
+            accumulated_offset + item->OffsetInContainerFragment();
         if (child_box->IsFloating()) {
           if (HitTestAllPhasesInFragment(*child_box, hit_test.location,
                                          child_offset, hit_test.result))
diff --git a/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
index 3b40844..c30180b 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
@@ -63,7 +63,7 @@
   const PhysicalOffset adjusted_paint_offset =
       paint_offset + (inline_box_paint_fragment_
                           ? inline_box_paint_fragment_->Offset()
-                          : inline_box_item_->OffsetInContainerBlock());
+                          : inline_box_item_->OffsetInContainerFragment());
   if (paint_info.phase == PaintPhase::kForeground)
     PaintBackgroundBorderShadow(paint_info, adjusted_paint_offset);
 
@@ -370,7 +370,7 @@
 
     for (const NGPaintFragment* fragment : fragments) {
       PhysicalOffset child_offset = paint_offset +
-                                    fragment->OffsetInContainerBlock() -
+                                    fragment->OffsetInContainerFragment() -
                                     fragment->Offset();
       DCHECK(fragment->PhysicalFragment().IsBox());
       NGInlineBoxFragmentPainter(*fragment).Paint(paint_info, child_offset);
@@ -386,10 +386,10 @@
   // inline may not start in the first fragment generated for the inline
   // formatting context.
   wtf_size_t target_fragment_idx =
-      cursor.CurrentContainerFragmentIndex() +
+      cursor.ContainerFragmentIndex() +
       paint_info.context.GetPaintController().CurrentFragment();
   for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
-    if (target_fragment_idx != cursor.CurrentContainerFragmentIndex())
+    if (target_fragment_idx != cursor.ContainerFragmentIndex())
       continue;
     const NGFragmentItem* item = cursor.CurrentItem();
     DCHECK(item);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index ee1c786..bc83e34 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -81,8 +81,8 @@
   NGInlineCursor line(cursor);
   line.MoveToContainingLine();
   const PhysicalRect line_physical_rect(
-      line.Current().OffsetInContainerBlock() -
-          cursor.Current().OffsetInContainerBlock(),
+      line.Current().OffsetInContainerFragment() -
+          cursor.Current().OffsetInContainerFragment(),
       line.Current().Size());
   return ExpandSelectionRectToLineHeight(
       rect, cursor.Current().ConvertChildToLogical(line_physical_rect));
@@ -625,7 +625,7 @@
     if (fragment->PhysicalFragment().IsHiddenForPaint())
       continue;
     PhysicalRect child_visual_rect = fragment->SelfInkOverflow();
-    child_visual_rect.offset += fragment->OffsetInContainerBlock();
+    child_visual_rect.offset += fragment->OffsetInContainerFragment();
     visual_rect.Unite(child_visual_rect);
   }
   return visual_rect;
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
index 6d6d083..40d00885 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
@@ -155,12 +155,12 @@
   bool IsDirty() const { return is_dirty_inline_; }
 
   // Returns offset to its container box for inline and line box fragments.
-  const PhysicalOffset& OffsetInContainerBlock() const {
+  const PhysicalOffset& OffsetInContainerFragment() const {
     DCHECK(PhysicalFragment().IsInline() || PhysicalFragment().IsLineBox());
     return inline_offset_to_container_box_;
   }
-  const PhysicalRect RectInContainerBlock() const {
-    return PhysicalRect(OffsetInContainerBlock(), Size());
+  const PhysicalRect RectInContainerFragment() const {
+    return PhysicalRect(OffsetInContainerFragment(), Size());
   }
 
   // InkOverflow of itself, not including contents, in the local coordinate.
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc
index 165c210..c51bbcd 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc
@@ -72,7 +72,7 @@
   results.AppendRange(it.begin(), it.end());
   EXPECT_EQ(1u, results.size());
   EXPECT_EQ(text1, results[0]->GetLayoutObject());
-  EXPECT_EQ(PhysicalOffset(), results[0]->OffsetInContainerBlock());
+  EXPECT_EQ(PhysicalOffset(), results[0]->OffsetInContainerFragment());
 
   results.clear();
   it = NGPaintFragment::InlineFragmentsFor(box);
@@ -82,15 +82,15 @@
   EXPECT_EQ(box, results[1]->GetLayoutObject());
   EXPECT_EQ(box, results[2]->GetLayoutObject());
 
-  EXPECT_EQ(PhysicalOffset(60, 0), results[0]->OffsetInContainerBlock());
+  EXPECT_EQ(PhysicalOffset(60, 0), results[0]->OffsetInContainerFragment());
   EXPECT_EQ("789", To<NGPhysicalTextFragment>(
                        results[0]->FirstChild()->PhysicalFragment())
                        .Text());
-  EXPECT_EQ(PhysicalOffset(0, 10), results[1]->OffsetInContainerBlock());
+  EXPECT_EQ(PhysicalOffset(0, 10), results[1]->OffsetInContainerFragment());
   EXPECT_EQ("123456789", To<NGPhysicalTextFragment>(
                              results[1]->FirstChild()->PhysicalFragment())
                              .Text());
-  EXPECT_EQ(PhysicalOffset(0, 20), results[2]->OffsetInContainerBlock());
+  EXPECT_EQ(PhysicalOffset(0, 20), results[2]->OffsetInContainerFragment());
   EXPECT_EQ("123", To<NGPhysicalTextFragment>(
                        results[2]->FirstChild()->PhysicalFragment())
                        .Text());
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
index b4b14cb..4de1aaa 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
@@ -75,7 +75,7 @@
 inline PhysicalRect ComputeBoxRect(const NGInlineCursor& cursor,
                                    const PhysicalOffset& paint_offset,
                                    const PhysicalOffset& parent_offset) {
-  PhysicalRect box_rect = cursor.CurrentItem()->RectInContainerBlock();
+  PhysicalRect box_rect = cursor.CurrentItem()->RectInContainerFragment();
   box_rect.offset.left += paint_offset.left;
   // We round the y-axis to ensure consistent line heights.
   box_rect.offset.top =
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
index 6eb858c..09479d6 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
@@ -158,9 +158,6 @@
 void PaintTimingDetector::LayoutObjectWillBeDestroyed(
     const LayoutObject& object) {
   text_paint_timing_detector_->LayoutObjectWillBeDestroyed(object);
-
-  if (image_paint_timing_detector_)
-    image_paint_timing_detector_->LayoutObjectWillBeDestroyed(object);
 }
 
 void PaintTimingDetector::NotifyImageRemoved(
diff --git a/third_party/blink/renderer/core/svg/svg_path_element.cc b/third_party/blink/renderer/core/svg/svg_path_element.cc
index 563e821c..82878464 100644
--- a/third_party/blink/renderer/core/svg/svg_path_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_path_element.cc
@@ -157,7 +157,7 @@
 
 FloatRect SVGPathElement::GetBBox() {
   // We want the exact bounds.
-  return SVGPathElement::AsPath().BoundingRect();
+  return SVGPathElement::AsPath().TightBoundingRect();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
index 74e2605..d0bde4c2 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
@@ -68,7 +68,7 @@
       token_(token) {
   appcache_host_ = MakeGarbageCollected<ApplicationCacheHostForWorker>(
       appcache_host_id, GetBrowserInterfaceBroker(),
-      GetTaskRunner(TaskType::kInternalLoading), this);
+      GetTaskRunner(TaskType::kInternalLoading));
 }
 
 SharedWorkerGlobalScope::~SharedWorkerGlobalScope() = default;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.cc
index bd3bf105..6a67606 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.cc
@@ -50,10 +50,7 @@
 void CanvasPath::closePath() {
   if (path_.IsEmpty())
     return;
-
-  FloatRect bound_rect = path_.BoundingRect();
-  if (bound_rect.Width() || bound_rect.Height())
-    path_.CloseSubpath();
+  path_.CloseSubpath();
 }
 
 void CanvasPath::moveTo(double double_x, double double_y) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_sink_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_sink_test.cc
index 113b954..66131a3 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_sink_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_sink_test.cc
@@ -69,8 +69,9 @@
   PushableMediaStreamVideoSource* pushable_video_source_;
 };
 
+// crbug.com/1153092: flaky on several platforms.
 TEST_F(MediaStreamVideoTrackUnderlyingSinkTest,
-       WriteToStreamForwardsToMediaStreamSink) {
+       DISABLED_WriteToStreamForwardsToMediaStreamSink) {
   V8TestingScope v8_scope;
   ScriptState* script_state = v8_scope.GetScriptState();
   auto* underlying_sink = CreateUnderlyingSink(script_state);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source_test.cc
index cb41a5e..3efe5ea 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source_test.cc
@@ -121,7 +121,8 @@
   EXPECT_EQ(video_track->CountSinks(), 0u);
 }
 
-TEST_F(MediaStreamVideoTrackUnderlyingSourceTest, FramesAreDropped) {
+// crbug.com/1153092: flaky on several platforms.
+TEST_F(MediaStreamVideoTrackUnderlyingSourceTest, DISABLED_FramesAreDropped) {
   V8TestingScope v8_scope;
   ScriptState* script_state = v8_scope.GetScriptState();
   auto* source = CreateSource(script_state);
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
index 9008049..2512403 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
@@ -15,6 +15,7 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/webrtc/track_observer.h"
@@ -139,6 +140,10 @@
 
   // Offset between NTP clock and WebRTC clock.
   const int64_t ntp_offset_;
+
+  // Determined from a feature flag; if set WebRTC won't forward an unspecified
+  // color space.
+  const bool ignore_unspecified_color_space_;
 };
 
 MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::
@@ -160,7 +165,9 @@
                          base::TimeDelta::FromMicroseconds(rtc::TimeMicros())),
       clock_(webrtc::Clock::GetRealTimeClock()),
       ntp_offset_(clock_->TimeInMilliseconds() -
-                  clock_->CurrentNtpInMilliseconds()) {}
+                  clock_->CurrentNtpInMilliseconds()),
+      ignore_unspecified_color_space_(base::FeatureList::IsEnabled(
+          features::kWebRtcIgnoreUnspecifiedColorSpace)) {}
 
 MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::
     ~RemoteVideoSourceDelegate() = default;
@@ -275,7 +282,19 @@
         WebRtcToMediaVideoRotation(incoming_frame.rotation());
   }
 
-  if (incoming_frame.color_space()) {
+  // The second clause of the condition is controlled by the feature flag
+  // WebRtcIgnoreUnspecifiedColorSpace. If the feature is enabled we won't try
+  // to guess a color space if the webrtc::ColorSpace is unspecified. If the
+  // feature is disabled (default), an unspecified color space will get
+  // converted into a gfx::ColorSpace set to BT709.
+  if (incoming_frame.color_space() &&
+      !(ignore_unspecified_color_space_ &&
+        incoming_frame.color_space()->primaries() ==
+            webrtc::ColorSpace::PrimaryID::kUnspecified &&
+        incoming_frame.color_space()->transfer() ==
+            webrtc::ColorSpace::TransferID::kUnspecified &&
+        incoming_frame.color_space()->matrix() ==
+            webrtc::ColorSpace::MatrixID::kUnspecified)) {
     video_frame->set_color_space(
         WebRtcToMediaVideoColorSpace(*incoming_frame.color_space())
             .ToGfxColorSpace());
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
index c7864177..0d8e50d 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
@@ -13,8 +13,10 @@
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/scoped_feature_list.h"
 #include "media/base/video_frame.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
@@ -286,6 +288,80 @@
   track->RemoveSink(&sink);
 }
 
+TEST_F(MediaStreamRemoteVideoSourceTest,
+       UnspecifiedColorSpaceIsTreatedAsBt709) {
+  std::unique_ptr<blink::MediaStreamVideoTrack> track(CreateTrack());
+  blink::MockMediaStreamVideoSink sink;
+  track->AddSink(&sink, sink.GetDeliverFrameCB(), false);
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(sink, OnVideoFrame)
+      .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
+  rtc::scoped_refptr<webrtc::I420Buffer> buffer(
+      new rtc::RefCountedObject<webrtc::I420Buffer>(320, 240));
+  webrtc::ColorSpace kColorSpace(webrtc::ColorSpace::PrimaryID::kUnspecified,
+                                 webrtc::ColorSpace::TransferID::kUnspecified,
+                                 webrtc::ColorSpace::MatrixID::kUnspecified,
+                                 webrtc::ColorSpace::RangeID::kLimited);
+  const webrtc::VideoFrame& input_frame =
+      webrtc::VideoFrame::Builder()
+          .set_video_frame_buffer(buffer)
+          .set_timestamp_ms(0)
+          .set_rotation(webrtc::kVideoRotation_0)
+          .set_color_space(kColorSpace)
+          .build();
+  source()->SinkInterfaceForTesting()->OnFrame(input_frame);
+  run_loop.Run();
+
+  EXPECT_EQ(1, sink.number_of_frames());
+  scoped_refptr<media::VideoFrame> output_frame = sink.last_frame();
+  EXPECT_TRUE(output_frame);
+  EXPECT_TRUE(output_frame->ColorSpace() ==
+              gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT709,
+                              gfx::ColorSpace::TransferID::BT709,
+                              gfx::ColorSpace::MatrixID::BT709,
+                              gfx::ColorSpace::RangeID::LIMITED));
+  track->RemoveSink(&sink);
+}
+
+TEST_F(MediaStreamRemoteVideoSourceTest, UnspecifiedColorSpaceIsIgnored) {
+  base::test::ScopedFeatureList scoped_feauture_list;
+  scoped_feauture_list.InitAndEnableFeature(
+      blink::features::kWebRtcIgnoreUnspecifiedColorSpace);
+  std::unique_ptr<blink::MediaStreamVideoTrack> track(CreateTrack());
+  blink::MockMediaStreamVideoSink sink;
+  track->AddSink(&sink, sink.GetDeliverFrameCB(), false);
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(sink, OnVideoFrame)
+      .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
+  rtc::scoped_refptr<webrtc::I420Buffer> buffer(
+      new rtc::RefCountedObject<webrtc::I420Buffer>(320, 240));
+  webrtc::ColorSpace kColorSpace(webrtc::ColorSpace::PrimaryID::kUnspecified,
+                                 webrtc::ColorSpace::TransferID::kUnspecified,
+                                 webrtc::ColorSpace::MatrixID::kUnspecified,
+                                 webrtc::ColorSpace::RangeID::kLimited);
+  const webrtc::VideoFrame& input_frame =
+      webrtc::VideoFrame::Builder()
+          .set_video_frame_buffer(buffer)
+          .set_timestamp_ms(0)
+          .set_rotation(webrtc::kVideoRotation_0)
+          .set_color_space(kColorSpace)
+          .build();
+  source()->SinkInterfaceForTesting()->OnFrame(input_frame);
+  run_loop.Run();
+
+  EXPECT_EQ(1, sink.number_of_frames());
+  scoped_refptr<media::VideoFrame> output_frame = sink.last_frame();
+  EXPECT_TRUE(output_frame);
+  EXPECT_TRUE(output_frame->ColorSpace() ==
+              gfx::ColorSpace(gfx::ColorSpace::PrimaryID::INVALID,
+                              gfx::ColorSpace::TransferID::INVALID,
+                              gfx::ColorSpace::MatrixID::INVALID,
+                              gfx::ColorSpace::RangeID::INVALID));
+  track->RemoveSink(&sink);
+}
+
 // These tests depend on measuring the difference between the internal WebRTC
 // clock and Chromium's clock. Due to this they are performance sensitive and
 // appear to be flaky for builds with ASAN enabled.
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index 9fd095e1..13951f8 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -815,8 +815,6 @@
                                   const EffectPaintPropertyNode& layer_effect,
                                   const PaintChunkSubset& paint_chunks) {
   Vector<Color, 4> background_colors;
-  Color safe_opaque_background_color;
-  float safe_opaque_background_area = 0;
   float min_background_area = kMinBackgroundColorCoverageRatio *
                               layer.bounds().width() * layer.bounds().height();
   for (auto it = paint_chunks.end(); it != paint_chunks.begin();) {
@@ -841,20 +839,12 @@
         break;
       }
     }
-    if (chunk.background_color_area > safe_opaque_background_area) {
-      // This color will be used only if we don't find proper background_color.
-      safe_opaque_background_color = chunk.background_color;
-      safe_opaque_background_area = chunk.background_color_area;
-    }
   }
 
   Color background_color;
   for (Color color : base::Reversed(background_colors))
     background_color = background_color.Blend(color);
   layer.SetBackgroundColor(background_color.Rgb());
-  layer.SetSafeOpaqueBackgroundColor(background_color == Color::kTransparent
-                                         ? safe_opaque_background_color.Rgb()
-                                         : background_color.Rgb());
 }
 
 static void UpdateTouchActionRegion(
diff --git a/third_party/blink/renderer/platform/graphics/path.cc b/third_party/blink/renderer/platform/graphics/path.cc
index 7c9fc54..b3e7bb1 100644
--- a/third_party/blink/renderer/platform/graphics/path.cc
+++ b/third_party/blink/renderer/platform/graphics/path.cc
@@ -109,10 +109,14 @@
       .contains(SkScalar(point.X()), SkScalar(point.Y()));
 }
 
-FloatRect Path::BoundingRect() const {
+FloatRect Path::TightBoundingRect() const {
   return path_.computeTightBounds();
 }
 
+FloatRect Path::BoundingRect() const {
+  return path_.getBounds();
+}
+
 FloatRect Path::StrokeBoundingRect(const StrokeData& stroke_data) const {
   // Skia stroke resolution scale for reduced-precision requirements.
   constexpr float kStrokePrecision = 0.3f;
diff --git a/third_party/blink/renderer/platform/graphics/path.h b/third_party/blink/renderer/platform/graphics/path.h
index 94d8003..84351f1d 100644
--- a/third_party/blink/renderer/platform/graphics/path.h
+++ b/third_party/blink/renderer/platform/graphics/path.h
@@ -98,6 +98,10 @@
                       const AffineTransform&) const;
   SkPath StrokePath(const StrokeData&, const AffineTransform&) const;
 
+  // Tight Bounding calculation is very expensive, but it guarantees the strict
+  // bounding box. It's always included in BoundingRect. For a logical bounding
+  // box (used for clipping or damage) BoundingRect is recommended.
+  FloatRect TightBoundingRect() const;
   FloatRect BoundingRect() const;
   FloatRect StrokeBoundingRect(const StrokeData&) const;
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index d029e7e..25bf0dfb 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -565,7 +565,7 @@
         # https://chromium-review.googlesource.com/c/chromium/src/+/2451504
         description += (
             'Cq-Include-Trybots: luci.chromium.try:linux-wpt-identity-fyi-rel,'
-            'linux-wpt-payments-fyi-rel')
+            'linux-wpt-input-fyi-rel')
 
         return description
 
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index f023cbc..4382c9c 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -404,7 +404,7 @@
             'NOAUTOREVERT=true\n'
             'No-Export: true\n'
             'Cq-Include-Trybots: luci.chromium.try:linux-wpt-identity-fyi-rel,'
-            'linux-wpt-payments-fyi-rel')
+            'linux-wpt-input-fyi-rel')
         print host.executive.calls
         self.assertEqual(host.executive.calls,
                          [MANIFEST_INSTALL_CMD] +
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ecbc1977..f806d4c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5290,6 +5290,8 @@
 
 crbug.com/1134464 http/tests/images/document-policy/document-policy-oversized-images-edge-cases.php [ Pass Timeout ]
 
+crbug.com/1151954 http/tests/permissions-policy/permissions-policy-in-xsl.php [ Failure ]
+
 # Skipping because of unimplemented behaviour
 crbug.com/965495 external/wpt/feature-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html [ Skip ]
 crbug.com/965495 external/wpt/permissions-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html [ Skip ]
@@ -5917,6 +5919,7 @@
 # Sheriff 2020-09-30
 crbug.com/1133821 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/one-custom-property-animation.https.html [ Pass Failure ]
 crbug.com/1133821 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/two-custom-property-animation.https.html [ Pass Failure ]
+crbug.com/1133821 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/two-element-custom-property-animation.https.html [ Pass Failure ]
 
 # Sheriff 2020-10-09
 crbug.com/1136687 external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html [ Pass Failure ]
@@ -6023,3 +6026,7 @@
 crbug.com/1092048 external/wpt/FileAPI/blob/Blob-stream.any.html [ Pass Timeout ]
 crbug.com/1092048 external/wpt/FileAPI/blob/Blob-stream.any.worker.html [ Pass Timeout ]
 crbug.com/1134459 accessibility/aom-click-action.html [ Pass Timeout ]
+
+#Sheriff 2020-11-26
+crbug.com/1032451 virtual/threaded-no-composited-antialiasing/animations/stability/animation-iteration-event-destroy-renderer.html [ Pass Timeout ]
+crbug.com/931646 [ Mac ] http/tests/preload/meta-viewport-link-headers-imagesrcset.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 6b7b2ce..0ab8189 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -157069,6 +157069,19 @@
          {}
         ]
        ],
+       "fieldset-max-block-size.html": [
+        "170dedd6067b0d9663f5b1346a43cbeaf7d8bcf8",
+        [
+         null,
+         [
+          [
+           "/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
        "fieldset-overflow-hidden.html": [
         "cacbdbae00d0f7d6067323c68fc460149c524f35",
         [
@@ -226685,6 +226698,10 @@
         "05b8ca4770b18a91aaf2d9f3038d6fe4162f289f",
         []
        ],
+       "fieldset-max-block-size-ref.html": [
+        "07c9da85b5347bdd2b01f3b52221dc791ed8a6f0",
+        []
+       ],
        "fieldset-overflow-hidden-ref.html": [
         "9fe632f7c209c42d9f24358ad6a2a56abddf97a3",
         []
@@ -232602,9 +232619,45 @@
      "1e219ecd5bead581a7eca478bcefcdd72dc8c9be",
      []
     ],
+    "input-events-exec-command.html.ini": [
+     "cd807c3d4df96ac8c0558fad451840ef29c4d80d",
+     []
+    ],
+    "input-events-get-target-ranges-backspace.tentative.html.ini": [
+     "62a9d9068d7d3ac62354d722b211fe41f9563ba1",
+     []
+    ],
+    "input-events-get-target-ranges-deleting-in-list-items.tentative.html.ini": [
+     "287527c2ac935f40521c50e36bc4b708331bac92",
+     []
+    ],
+    "input-events-get-target-ranges-during-and-after-dispatch.tentative.html.ini": [
+     "b4a346ae68cb4726c82ff7bbdc1617164bcea293",
+     []
+    ],
+    "input-events-get-target-ranges-forwarddelete.tentative.html.ini": [
+     "93057d092c00c6d4295616b3590033c8ca17b313",
+     []
+    ],
+    "input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html.ini": [
+     "3682b5332d210f74721284474c626db0712515fc",
+     []
+    ],
+    "input-events-get-target-ranges-joining-dl-elements.tentative.html.ini": [
+     "d0038c797e84996751fbc50ae9659991d19da1c7",
+     []
+    ],
+    "input-events-get-target-ranges-non-collapsed-selection.tentative.html.ini": [
+     "a01e994f02a6c57f3e1675c0439ab338e2beb650",
+     []
+    ],
     "input-events-get-target-ranges.js": [
      "f9404e07a5564c64a2863a1329728517fedd5ca7",
      []
+    ],
+    "input-events-typing.html.ini": [
+     "9647ef7e893fc208959f3ddbfef78e11f59c4129",
+     []
     ]
    },
    "installedapp": {
@@ -233309,7 +233362,7 @@
      []
     ],
     "webrtc.idl": [
-     "d82cde768f7b5f65e78570227510f2e4f0d4b7c1",
+     "db644f031836bd2dfa8d046f5c78d561186490ec",
      []
     ],
     "webusb.idl": [
@@ -237468,14 +237521,38 @@
      "22bb34a054ec5d0f1ed837b8b23d544110d78912",
      []
     ],
+    "idlharness.window.js.ini": [
+     "2c8f43716403bfd4a8bd8a7bdecaf888a2bcd697",
+     []
+    ],
+    "pointerevent_auxclick_is_a_pointerevent.html.ini": [
+     "e4f7b0ee92d0e4d3b4bdf95c83223be85ca0b0a5",
+     []
+    ],
+    "pointerevent_click_is_a_pointerevent.html.ini": [
+     "3994b407af65d66547f2820b6a72e96f99ed86a7",
+     []
+    ],
     "pointerevent_coalesced_events_attributes-expected.txt": [
      "e9ecdaa4bfe35a4e711c10c960d3d7124c389537",
      []
     ],
+    "pointerevent_iframe-touch-action-none_touch.html.ini": [
+     "bee7254d54849e87c06137d7541c0da5332155c0",
+     []
+    ],
+    "pointerevent_pointercapture-not-lost-in-chorded-buttons.html.ini": [
+     "6b81a4d1d7d37235727f321f0f07f3a4f58b1614",
+     []
+    ],
     "pointerevent_pointercapture_in_frame-expected.txt": [
      "4d3d205c84983950b3a0cdee366d51ca0427f592",
      []
     ],
+    "pointerevent_pointercapture_in_frame.html.ini": [
+     "42b68012c5fa1510245eb54dbf95f3db321b5d03",
+     []
+    ],
     "pointerevent_styles.css": [
      "1ee3b0b3965ff9fb3c5960f606cca6477ce145a8",
      []
@@ -237485,6 +237562,30 @@
      []
     ],
     "pointerlock": {
+     "pointerevent_coordinates_when_locked.html.ini": [
+      "04c0f72c7d99bc37a364e7192e7e65a1e6827f60",
+      []
+     ],
+     "pointerevent_getCoalescedEvents_when_pointerlocked.html.ini": [
+      "9e125839696524cc24cb3f91655765624854e1f8",
+      []
+     ],
+     "pointerevent_movementxy_with_pointerlock.html.ini": [
+      "d22487b5371f040fd3edb99ac345b2a38cc58705",
+      []
+     ],
+     "pointerevent_pointerlock_after_pointercapture.html.ini": [
+      "dc1a38a96f354e88d08d45bfa692583cb4dcc4ac",
+      []
+     ],
+     "pointerevent_pointermove_in_pointerlock.html.ini": [
+      "7c4043d0fa0abe2ae9d15c437aac8b617a3c5512",
+      []
+     ],
+     "pointerevent_pointermove_on_chorded_mouse_button_when_locked.html.ini": [
+      "9d0b178aa192acc5ac1428b67482728b9ee676ee",
+      []
+     ],
      "resources": {
       "pointerevent_movementxy-iframe.html": [
        "627af3b61cad74bb112558169b1e66f6a24b1129",
@@ -359971,6 +360072,15 @@
          }
         ]
        ],
+       "reporting-coop-navigated-opener.https.html": [
+        "858f31b982878b34ebce744b945a7fc7b8925aa3",
+        [
+         null,
+         {
+          "timeout": "long"
+         }
+        ]
+       ],
        "reporting-coop-navigated-popup.https.html": [
         "dde4c40b38a1e541f66c47573fbd642f2e1e8289",
         [
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html
new file mode 100644
index 0000000..858f31b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/reporting/navigation-reporting/reporting-coop-navigated-opener.https.html
@@ -0,0 +1,68 @@
+<title>
+  Reports a browsing context group switch when an opener with COOP navigates.
+</title>
+<meta name=timeout content=long>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/dispatcher.js"></script>
+<script>
+
+const directory = "/html/cross-origin-opener-policy/reporting";
+const executor_path = directory + "/resources/executor.html?pipe=";
+const same_origin = get_host_info().HTTPS_ORIGIN;
+
+let escapeComma = url => url.replace(/,/g, '\\,');
+
+promise_test(async t => {
+  // The test window.
+ const this_window_token = token();
+
+  // The "opener" window.
+  const opener_token = token();
+  const opener_url = same_origin + executor_path + `&uuid=${opener_token}`;
+
+  // The "openee" window.
+  const openee_token = token();
+  const openee_url = same_origin + executor_path + `&uuid=${openee_token}`;
+
+  // The "final" url the opener will navigate to. It has COOP and a reporter.
+  const final_report_token= token();
+  const final_token = token();
+  const final_reportTo = reportToHeaders(final_report_token);
+  const final_url =  same_origin + executor_path + final_reportTo.header +
+    final_reportTo.coopSameOriginHeader +`&uuid=${final_token}`;
+
+  // 1. Create the opener window and ensure it doesn't have an opener.
+  let opener_window_proxy = window.open(opener_url, '_blank', 'noopener');
+  t.add_cleanup(() => send(opener_token, "window.close()"));
+
+  // 2. The opener opens a window.
+  send(opener_token, `
+    openee = window.open('${escapeComma(openee_url)}');
+    send("${this_window_token}", "ACK 1");
+  `);
+
+  // 3. Ensure the openee loads.
+  send(openee_token, `
+    send("${this_window_token}", "ACK 1");
+  `);
+  assert_equals("ACK 1", await receive(this_window_token));
+
+  // 4. The opener navigates.
+  send(opener_token, `
+    location.replace('${escapeComma(final_url)}');
+  `);
+
+  // 5. Check a report was sent to the opener.
+  let report =
+    await receiveReport(final_report_token, "navigation-to-response")
+  assert_equals(report.type, "coop");
+  assert_equals(report.url, final_url.replace(/"/g, '%22'));
+  assert_equals(report.body.disposition, "enforce");
+  assert_equals(report.body.effectivePolicy, "same-origin");
+  assert_equals(report.body.previousResponseURL, opener_url.replace(/"/g, '%22'));
+}, "navigation-report-from-opener-navigation");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html
new file mode 100644
index 0000000..07c9da8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<style>
+.fieldset {
+  border: 2px solid gray;
+  margin: 1em;
+  padding: 0;
+  width: 20em;
+}
+
+.f1 {
+  overflow: auto;
+  max-height: 3em;
+}
+.f2 {
+  max-height: 0;
+}
+</style>
+
+<div class="fieldset f1">
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+</div>
+
+<div class="fieldset f1">
+<div>foo</div>
+</div>
+
+<div class="fieldset f2">
+<div>foo</div>
+</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html
new file mode 100644
index 0000000..170dedd6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-max-block-size.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="http://crbug.com/1151858">
+<link rel="match" href="fieldset-max-block-size-ref.html">
+<style>
+fieldset {
+  border: 2px solid gray;
+  margin: 1em;
+  padding: 0;
+  width: 20em;
+}
+
+.f1 {
+  overflow: auto;
+  max-height: 3em;
+}
+.f2 {
+  max-height: 0;
+}
+</style>
+
+<fieldset class="f1">
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+<div>foo</div>
+</fieldset>
+
+<fieldset class="f1">
+<div>foo</div>
+</fieldset>
+
+<fieldset class="f2">
+<div>foo</div>
+</fieldset>
+
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl b/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
index d82cde7..db644f03 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
@@ -238,7 +238,7 @@
   unsigned short? port;
   DOMString url;
   required unsigned short errorCode;
-  USVString statusText;
+  USVString errorText;
 };
 
 partial interface RTCPeerConnection {
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/placeholder-image.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/placeholder-image.html
new file mode 100644
index 0000000..6a2ce5c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/placeholder-image.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: src change triggers new entry.</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/largest-contentful-paint-helpers.js"></script>
+<img src='/images/green-1x1.png' id='image_id' width="133" height="106"/>
+<script>
+  async_test(function (t) {
+    assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
+    let beforeLoad = performance.now();
+    document.getElementById('image_id').src = '/images/blue.png';
+    const url = window.location.origin + '/images/blue.png';
+    const observer = new PerformanceObserver(
+      t.step_func(function(entryList) {
+        let entries = entryList.getEntries().filter(e => e.url === url);
+        if (entries.length === 0)
+          return;
+        assert_equals(entries.length, 1);
+        const entry = entries[0];
+        // blue.png is 133 by 106.
+        const size = 133 * 106;
+        checkImage(entry, url, 'image_id', size, beforeLoad);
+        t.done();
+      })
+    );
+    observer.observe({type: 'largest-contentful-paint', buffered: true});
+  }, 'Largest Contentful Paint: changing src causes a new entry to be dispatched.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/canvas-api/canvas-scroll-path-into-view-expected.txt b/third_party/blink/web_tests/fast/canvas-api/canvas-scroll-path-into-view-expected.txt
index 08064e72..cdbeb3c 100644
--- a/third_party/blink/web_tests/fast/canvas-api/canvas-scroll-path-into-view-expected.txt
+++ b/third_party/blink/web_tests/fast/canvas-api/canvas-scroll-path-into-view-expected.txt
@@ -31,11 +31,11 @@
 PASS testValue is 636
 PASS testValue is 1136
 PASS testValue is 636
-PASS testValue is 127
-PASS testValue is 627
-PASS testValue is 627
-PASS testValue is 1127
-PASS testValue is 627
+PASS testValue is 106
+PASS testValue is 606
+PASS testValue is 606
+PASS testValue is 1106
+PASS testValue is 606
 PASS testValue is 133
 PASS testValue is 633
 PASS testValue is 633
@@ -77,11 +77,11 @@
 PASS testValue is 636
 PASS testValue is 1136
 PASS testValue is 636
-PASS testValue is 127
-PASS testValue is 627
-PASS testValue is 627
-PASS testValue is 1127
-PASS testValue is 627
+PASS testValue is 106
+PASS testValue is 606
+PASS testValue is 606
+PASS testValue is 1106
+PASS testValue is 606
 PASS testValue is 133
 PASS testValue is 633
 PASS testValue is 633
diff --git a/third_party/blink/web_tests/fast/canvas-api/script-tests/canvas-scroll-path-into-view.js b/third_party/blink/web_tests/fast/canvas-api/script-tests/canvas-scroll-path-into-view.js
index 63674ee..06d0c58 100644
--- a/third_party/blink/web_tests/fast/canvas-api/script-tests/canvas-scroll-path-into-view.js
+++ b/third_party/blink/web_tests/fast/canvas-api/script-tests/canvas-scroll-path-into-view.js
@@ -132,7 +132,7 @@
 
 debug("Test case 2: scrollPathIntoView() / CTM != identity");
 scrollTest(CRect, 20, false, 136);
-scrollTest(CCapsule, 42, false, 127);
+scrollTest(CCapsule, 42, false, 106);
 scrollTest(CCurve, 63, false, 133);
 scrollTest(CStar, 40, false, 160);
 debug("");
@@ -146,7 +146,7 @@
 
 debug("Test case 4: scrollPathIntoView(path2d) / CTM != identity");
 scrollTest(CRect, 20, true, 136);
-scrollTest(CCapsule, 42, true, 127);
+scrollTest(CCapsule, 42, true, 106);
 scrollTest(CCurve, 63, true, 133);
 scrollTest(CStar, 40, true, 160);
 debug("");
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-alignment-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-alignment-expected.txt
new file mode 100644
index 0000000..02d9398
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-alignment-expected.txt
@@ -0,0 +1,858 @@
+This test verifies the flex self-alignment value sent by the backend.
+
+flex-start{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        800,
+        0,
+        "L",
+        800,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "flex-start",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutNGFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "lines": [
+        [
+          [
+            "M",
+            0,
+            0,
+            "L",
+            0,
+            0,
+            "L",
+            0,
+            0,
+            "L",
+            0,
+            0,
+            "Z"
+          ]
+        ]
+      ],
+      "isHorizontalFlow": true,
+      "alignItemsStyle": "flex-start",
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+flex-end{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        800,
+        100,
+        "L",
+        800,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "flex-end",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutNGFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "lines": [
+        [
+          [
+            "M",
+            0,
+            200,
+            "L",
+            0,
+            200,
+            "L",
+            0,
+            200,
+            "L",
+            0,
+            200,
+            "Z"
+          ]
+        ]
+      ],
+      "isHorizontalFlow": true,
+      "alignItemsStyle": "flex-end",
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+center{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        800,
+        200,
+        "L",
+        800,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "center",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutNGFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "lines": [
+        [
+          [
+            "M",
+            0,
+            250,
+            "L",
+            0,
+            250,
+            "L",
+            0,
+            250,
+            "L",
+            0,
+            250,
+            "Z"
+          ]
+        ]
+      ],
+      "isHorizontalFlow": true,
+      "alignItemsStyle": "center",
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+stretch{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        800,
+        300,
+        "L",
+        800,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "stretch",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutNGFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "lines": [
+        [
+          [
+            "M",
+            0,
+            300,
+            "L",
+            0,
+            300,
+            "L",
+            0,
+            400,
+            "L",
+            0,
+            400,
+            "Z"
+          ]
+        ]
+      ],
+      "isHorizontalFlow": true,
+      "alignItemsStyle": "stretch",
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+normal{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        800,
+        400,
+        "L",
+        800,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "normal",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutNGFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "lines": [
+        [
+          [
+            "M",
+            0,
+            400,
+            "L",
+            0,
+            400,
+            "L",
+            0,
+            500,
+            "L",
+            0,
+            500,
+            "Z"
+          ]
+        ]
+      ],
+      "isHorizontalFlow": true,
+      "alignItemsStyle": "normal",
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-alignment.js b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-alignment.js
new file mode 100644
index 0000000..555cdad
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-alignment.js
@@ -0,0 +1,45 @@
+// Copyright 2020 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.
+
+(async function() {
+  TestRunner.addResult(`This test verifies the flex self-alignment value sent by the backend.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <style>
+      html, body {
+        margin: 0;
+      }
+      .container {
+        width: 100px;
+        height: 100px;
+        display: flex;
+      }
+      button {
+        width: 50px;
+        height: 50px;
+        border: 0;
+        padding: 0;
+      }
+      </style>
+      <div id="flex-start" class="container" style="align-items:flex-start"><div class="item"></div></div>
+      <div id="flex-end" class="container" style="align-items:flex-end"><div class="item"></div></div>
+      <div id="center" class="container" style="align-items:center"><div class="item"></div></div>
+      <div id="stretch" class="container" style="align-items:stretch"><div class="item"></div></div>
+      <div id="normal" class="container" style="align-items:normal"><div class="item"></div></div>
+      <p id="description">This test verifies the flex self-alignment value sent by the backend.</p>
+    `);
+
+  function dumpFlexHighlight(id) {
+    return new Promise(resolve => ElementsTestRunner.dumpInspectorHighlightJSON(id, resolve));
+  }
+
+  await dumpFlexHighlight('flex-start');
+  await dumpFlexHighlight('flex-end');
+  await dumpFlexHighlight('center');
+  await dumpFlexHighlight('stretch');
+  await dumpFlexHighlight('normal');
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-expected.txt
index 4840cda..7193a00 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-expected.txt
@@ -162,6 +162,7 @@
         ]
       ],
       "isHorizontalFlow": true,
+      "alignItemsStyle": "normal",
       "flexContainerHighlightConfig": {
         "containerBorder": {
           "color": "rgba(255, 0, 0, 0)",
@@ -190,6 +191,10 @@
         "columnGapSpace": {
           "fillColor": "rgba(255, 0, 0, 0)",
           "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
         }
       }
     }
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-multiline-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-multiline-expected.txt
index 481d128..5f32fb1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-multiline-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-flex-multiline-expected.txt
@@ -209,6 +209,7 @@
         ]
       ],
       "isHorizontalFlow": true,
+      "alignItemsStyle": "normal",
       "flexContainerHighlightConfig": {
         "containerBorder": {
           "color": "rgba(255, 0, 0, 0)",
@@ -237,6 +238,10 @@
         "columnGapSpace": {
           "fillColor": "rgba(255, 0, 0, 0)",
           "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
         }
       }
     }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code-expected.txt
index 7c8a3a4..c444de4 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code-expected.txt
@@ -9,6 +9,8 @@
   Method: OPTIONS
   Url: https://127.0.0.1:8443/inspector-protocol/resources/cors-data.php
   Has extra info: true
+  References get request: true
+  Initiator type: preflight
 GET response:
   Has timing info: true
   Has extra info: true
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js
index 32bf7afcb..11975b6 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js
@@ -15,6 +15,7 @@
       getRequestEventParams = event.params;
     } else if (event.params.request.method === 'OPTIONS') {
       optionsRequestEventParams = event.params;
+      optionsRequestInitiatorRequestId = event.params.initiator.requestId;
     }
   });
 
@@ -53,6 +54,8 @@
   });
 
   async function printResultsAndFinish() {
+    const optionsRequestReferencedGetRequest =
+        optionsRequestInitiatorRequestId === getRequestEventParams.requestId;
     testRunner.log('GET Request:');
     testRunner.log(`  Method: ${getRequestEventParams.request.method}`);
     testRunner.log(`  Url: ${getRequestEventParams.request.url}`);
@@ -62,6 +65,10 @@
     testRunner.log(`  Method: ${optionsRequestEventParams.request.method}`);
     testRunner.log(`  Url: ${optionsRequestEventParams.request.url}`);
     testRunner.log(`  Has extra info: ${optionsRequestExtra}`);
+    testRunner.log(
+        `  References get request: ${optionsRequestReferencedGetRequest}`);
+    testRunner.log(
+        `  Initiator type: ${optionsRequestEventParams.initiator.type}`);
 
     testRunner.log('GET response:');
     testRunner.log(`  Has timing info: ${!!getResponseEventParams.response.timing}`);
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/third-party-first-party-token-in-markup-enabled.html b/third_party/blink/web_tests/http/tests/origin_trials/third-party-first-party-token-in-markup-enabled.html
new file mode 100644
index 0000000..13dc97c9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/third-party-first-party-token-in-markup-enabled.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that trial which supports third-party is enabled by valid first-party token provided in markup</title>
+<!-- Generate this token with the command:
+generate_token.py http://127.0.0.1:8000 FrobulateThirdParty --expire-timestamp=2000000000
+-->
+<meta http-equiv="origin-trial"
+  content="A0TyTDFs2N+ecIcAeQo8DlipLQwIEcD+bJlRGpZj5NfDDGF8VEcEL4zByhPrdadxF1PX8VG4bfd2XZep1O6m3wsAAABbeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRnJvYnVsYXRlVGhpcmRQYXJ0eSIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==" />
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="resources/origintrials.js"></script>
+<script>
+  // The trial should be enabled, as first party tokens can be used normally,
+  // when the trial has third-party support enabled.
+  expect_success_third_party();
+</script>
diff --git a/third_party/blink/web_tests/http/tests/permissions-policy/permissions-policy-in-xsl.php b/third_party/blink/web_tests/http/tests/permissions-policy/permissions-policy-in-xsl.php
new file mode 100644
index 0000000..5e418ba7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/permissions-policy/permissions-policy-in-xsl.php
@@ -0,0 +1,9 @@
+<?php
+header("Permissions-Policy: document-domain=()");
+header("Content-Type: application/xml");
+
+echo '<?xml version="1.0"?>
+<?xml-stylesheet href="resources/permissions-policy-in-xsl.xslt" type="text/xsl"?>
+<page>
+</page>';
+?>
diff --git a/third_party/blink/web_tests/http/tests/permissions-policy/resources/permissions-policy-in-xsl.xslt b/third_party/blink/web_tests/http/tests/permissions-policy/resources/permissions-policy-in-xsl.xslt
new file mode 100644
index 0000000..f0cee19
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/permissions-policy/resources/permissions-policy-in-xsl.xslt
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+              xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="page">
+  <html>
+    <head>
+      <title> Test XSLT </title>
+      <script src="../../resources/testharness.js"></script>
+      <script src="../../resources/testharnessreport.js"></script>
+      <script>
+        test(() => {
+          let feature_allowed;
+          try {
+            document.domain = document.domain;
+            feature_allowed = true;
+          } catch(e) {
+            feature_allowed = false;
+          }
+
+          assert_false(feature_allowed, "Feature(Document Domain) should not be allowed by permissions policy.");
+        });
+      </script>
+    </head>
+    <body bgcolor="#ffffff">
+    </body>
+  </html>
+</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index d34f729..fcc234f 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,10 +1,10 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 2.7.2-62
+Version: 2.7.2-137
 CPEPrefix: cpe:/a:harfbuzz_project:harfbuzz:2.6.4
-Date: 20201007
-Revision: c39ab82c90479341dcf28eaa8174af6f08c0d7ae
+Date: 20201126
+Revision: 53806e5b83cee0e275eac038d0780f95ac56588c
 Security Critical: yes
 License: MIT
 License File: src/COPYING
diff --git a/tools/accessibility/inspect/README.md b/tools/accessibility/inspect/README.md
index 7874ece..b1847ae2 100644
--- a/tools/accessibility/inspect/README.md
+++ b/tools/accessibility/inspect/README.md
@@ -52,7 +52,6 @@
 `--pid=process_id`
 
 Other options:
-`--json` to output a tree in JSON format
 `--filters=absolute_path_to_filters.txt` to filter properties, use where the filters text file has a series of `@ALLOW` and/or `@DENY` lines. See example-tree-filters.txt in tools/accessibility/inspect.
 `--help` for help
 
diff --git a/tools/accessibility/inspect/ax_dump_tree.cc b/tools/accessibility/inspect/ax_dump_tree.cc
index e0239d2..aca2f25 100644
--- a/tools/accessibility/inspect/ax_dump_tree.cc
+++ b/tools/accessibility/inspect/ax_dump_tree.cc
@@ -22,7 +22,6 @@
     "pid";
 #endif
 char kFiltersSwitch[] = "filters";
-char kJsonSwitch[] = "json";
 char kHelpSwitch[] = "help";
 
 // Convert from string to int, whether in 0x hex format or decimal format.
@@ -88,8 +87,6 @@
   base::FilePath filters_path =
       command_line->GetSwitchValuePath(kFiltersSwitch);
 
-  bool use_json = command_line->HasSwitch(kJsonSwitch);
-
   std::string id_str = command_line->GetSwitchValueASCII(kIdSwitch);
   if (!id_str.empty()) {
     unsigned hwnd_or_pid;
@@ -100,14 +97,14 @@
     gfx::AcceleratedWidget widget(CastToAcceleratedWidget(hwnd_or_pid));
 
     std::unique_ptr<content::AXTreeServer> server(
-        new content::AXTreeServer(widget, filters_path, use_json));
+        new content::AXTreeServer(widget, filters_path));
     return 0;
   }
 
   AXTreeSelector selector = tools::TreeSelectorFromCommandLine(command_line);
   if (!selector.empty()) {
     std::unique_ptr<content::AXTreeServer> server(
-        new content::AXTreeServer(selector, filters_path, use_json));
+        new content::AXTreeServer(selector, filters_path));
     return 0;
   }
 
diff --git a/tools/accessibility/inspect/ax_tree_server.cc b/tools/accessibility/inspect/ax_tree_server.cc
index 66b2eb8f..86b5f79 100644
--- a/tools/accessibility/inspect/ax_tree_server.cc
+++ b/tools/accessibility/inspect/ax_tree_server.cc
@@ -10,7 +10,6 @@
 #include "base/at_exit.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
-#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
@@ -41,20 +40,17 @@
 }
 
 AXTreeServer::AXTreeServer(const AXTreeSelector& selector,
-                           const base::FilePath& filters_path,
-                           bool use_json) {
-  Run(base::BindOnce(&BuildTreeForSelector, selector), filters_path, use_json);
+                           const base::FilePath& filters_path) {
+  Run(base::BindOnce(&BuildTreeForSelector, selector), filters_path);
 }
 
 AXTreeServer::AXTreeServer(gfx::AcceleratedWidget widget,
-                           const base::FilePath& filters_path,
-                           bool use_json) {
-  Run(base::BindOnce(&BuildTreeForWindow, widget), filters_path, use_json);
+                           const base::FilePath& filters_path) {
+  Run(base::BindOnce(&BuildTreeForWindow, widget), filters_path);
 }
 
 void AXTreeServer::Run(BuildTree build_tree,
-                       const base::FilePath& filters_path,
-                       bool use_json) {
+                       const base::FilePath& filters_path) {
   std::unique_ptr<AXTreeFormatter> formatter(
       AccessibilityTreeFormatter::Create());
 
@@ -73,8 +69,8 @@
     return;
   }
 
-  // Format the tree.
-  Format(*formatter, dict, use_json);
+  // Write to console.
+  printf("%s", formatter->FormatTree(dict).c_str());
 }
 
 std::vector<ui::AXPropertyFilter> AXTreeServer::GetPropertyFilters(
@@ -118,23 +114,4 @@
   return filters;
 }
 
-void AXTreeServer::Format(const AXTreeFormatter& formatter,
-                          const base::Value& dict,
-                          bool use_json) {
-  std::string accessibility_contents;
-
-  // Format accessibility tree as JSON or text.
-  if (use_json) {
-    const base::Value filtered_dict = formatter.FilterTree(dict);
-    base::JSONWriter::WriteWithOptions(filtered_dict,
-                                       base::JSONWriter::OPTIONS_PRETTY_PRINT,
-                                       &accessibility_contents);
-  } else {
-    accessibility_contents = formatter.FormatTree(dict);
-  }
-
-  // Write to console.
-  printf("%s", accessibility_contents.c_str());
-}
-
 }  // namespace content
diff --git a/tools/accessibility/inspect/ax_tree_server.h b/tools/accessibility/inspect/ax_tree_server.h
index dac139b..fa5906c 100644
--- a/tools/accessibility/inspect/ax_tree_server.h
+++ b/tools/accessibility/inspect/ax_tree_server.h
@@ -20,29 +20,20 @@
 class AXTreeServer final {
  public:
   AXTreeServer(gfx::AcceleratedWidget widget,
-               const base::FilePath& filters_path,
-               bool use_json);
+               const base::FilePath& filters_path);
   AXTreeServer(const ui::AXTreeSelector& selector,
-               const base::FilePath& filters_path,
-               bool use_json);
+               const base::FilePath& filters_path);
 
  private:
   using BuildTree = base::OnceCallback<base::Value(const ui::AXTreeFormatter*)>;
 
   // Builds and formats the accessible tree.
-  void Run(BuildTree build_tree,
-           const base::FilePath& filters_path,
-           bool use_json);
+  void Run(BuildTree build_tree, const base::FilePath& filters_path);
 
   // Generates property filters.
   std::vector<ui::AXPropertyFilter> GetPropertyFilters(
       const base::FilePath& filters_path);
 
-  // Formats and dumps into console the tree.
-  void Format(const ui::AXTreeFormatter& formatter,
-              const base::Value& dict,
-              bool use_json);
-
 #if defined(OS_WIN)
   // Only one COM initializer per thread is permitted.
   base::win::ScopedCOMInitializer com_initializer_;
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index 0597ba9..059af17 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -1387,16 +1387,14 @@
 
 def _OverwriteSymbolSizesWithRelocationCount(raw_symbols, tool_prefix,
                                              elf_path):
+  logging.info('Removing non-native symbols')
+  raw_symbols = [sym for sym in raw_symbols if sym.IsNative()]
+
   logging.info('Overwriting symbol sizes with relocation count')
-  native_symbols = [sym for sym in raw_symbols if sym.IsNative()]
-  symbol_addresses = [0] * (1 + len(native_symbols))
-
-  for i, symbol in enumerate(native_symbols):
-    symbol_addresses[i] = symbol.address
-
   # Last symbol address is the end of the last symbol, so we don't misattribute
   # all relros after the last symbol to that symbol.
-  symbol_addresses[-1] = native_symbols[-1].address + native_symbols[-1].size
+  symbol_addresses = [s.address for s in raw_symbols]
+  symbol_addresses.append(raw_symbols[-1].end_address)
 
   for symbol in raw_symbols:
     symbol.address = 0
@@ -1418,13 +1416,13 @@
   for addr in relro_addresses:
     # Attribute relros to largest symbol start address that precede them.
     idx = bisect.bisect_right(symbol_addresses, addr) - 1
-    if 0 <= idx < len(native_symbols):
-      symbol = native_symbols[idx]
+    if 0 <= idx < len(raw_symbols):
+      symbol = raw_symbols[idx]
       for alias in symbol.aliases or [symbol]:
         alias.size += 1
 
-  logging.info('Removing non-native symbols...')
-  raw_symbols[:] = [sym for sym in raw_symbols if sym.size or sym.IsNative()]
+  raw_symbols = [sym for sym in raw_symbols if sym.size]
+  return raw_symbols
 
 
 def _AddUnattributedSectionSymbols(raw_symbols, section_ranges):
@@ -1528,6 +1526,9 @@
     (section_sizes maps section names to respective sizes).
     raw_symbols is a list of Symbol objects.
   """
+  assert elf_path or not opts.relocations_mode, (
+      '--relocations-mode requires a ELF file')
+
   knobs = knobs or SectionSizeKnobs()
   if apk_path and apk_so_path:
     # Extraction takes around 1 second, so do it in parallel.
@@ -1613,7 +1614,7 @@
 
   pak_symbols_by_id = None
   other_symbols = []
-  if apk_path and size_info_prefix:
+  if apk_path and size_info_prefix and not opts.relocations_mode:
     # Can modify |section_ranges|.
     pak_symbols_by_id = _FindPakSymbolsFromApk(opts, section_ranges, apk_path,
                                                size_info_prefix)
@@ -1688,8 +1689,9 @@
   logging.debug('Connecting nm aliases')
   _ConnectNmAliases(raw_symbols)
 
-  if elf_path and opts.relocations_mode:
-    _OverwriteSymbolSizesWithRelocationCount(raw_symbols, tool_prefix, elf_path)
+  if opts.relocations_mode:
+    raw_symbols = _OverwriteSymbolSizesWithRelocationCount(
+        raw_symbols, tool_prefix, elf_path)
 
   section_sizes = {k: size for k, (address, size) in section_ranges.items()}
   container = models.Container(name=container_name,
@@ -1698,7 +1700,10 @@
   for symbol in raw_symbols:
     symbol.container = container
 
-  file_format.SortSymbols(raw_symbols, check_already_mostly_sorted=True)
+  # Sorting for relocations mode causes .data and .data.rel.ro to be interleaved
+  # due to setting all addresses to 0.
+  if not opts.relocations_mode:
+    file_format.SortSymbols(raw_symbols, check_already_mostly_sorted=True)
 
   return container, raw_symbols
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 9c38f79..5fc617f8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -42273,6 +42273,7 @@
   <int value="-1553477903" label="ash-disable-text-filtering-in-overview-mode"/>
   <int value="-1553280810" label="PromoBrowserCommands:enabled"/>
   <int value="-1552898031" label="SingleTabMode:enabled"/>
+  <int value="-1551197844" label="AssistMultiWord:enabled"/>
   <int value="-1550760918" label="PipRoundedCorners:disabled"/>
   <int value="-1550675387" label="CriticalPersistedTabData:enabled"/>
   <int value="-1550541457" label="DisplayIdentification:disabled"/>
@@ -42465,6 +42466,7 @@
   <int value="-1399753480" label="disable-harfbuzz-rendertext"/>
   <int value="-1399419572" label="enable-app-list"/>
   <int value="-1396974542" label="UserMediaScreenCapturing:enabled"/>
+  <int value="-1396434715" label="AssistMultiWord:disabled"/>
   <int value="-1392689905" label="ServiceWorkerLongRunningMessage:enabled"/>
   <int value="-1392562498" label="disable-origin-chip"/>
   <int value="-1391728260" label="ContextualSearchDefinitions:enabled"/>
@@ -57929,6 +57931,13 @@
   <int value="3" label="REPEATED_IGNORES"/>
 </enum>
 
+<enum name="PermissionPromptDispositionReason">
+  <int value="0" label="User preference in settings"/>
+  <int value="1" label="Safe Browsing verdict"/>
+  <int value="2" label="Prediction service"/>
+  <int value="3" label="Default fallback"/>
+</enum>
+
 <enum name="PermissionRequestType">
   <int value="0" label="PERMISSION_BUBBLE_UNKNOWN"/>
   <int value="1" label="PERMISSION_BUBBLE_MULTIPLE"/>
@@ -59030,6 +59039,15 @@
   <int value="1" label="The pre-connect triggered host was accessed"/>
 </enum>
 
+<enum name="PredictionGrantLikelihood">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Very unlikely"/>
+  <int value="2" label="Unlikely"/>
+  <int value="3" label="Neutral"/>
+  <int value="4" label="Likely"/>
+  <int value="5" label="Very likely"/>
+</enum>
+
 <enum name="PredictionStatus">
   <int value="0" label="No prediction"/>
   <int value="1" label="Successful prediction"/>
@@ -60830,8 +60848,8 @@
   <int value="2" label="Signed in to sync from secondary profile"/>
   <int value="3" label="Customized sync options"/>
   <int value="4" label="Chose what to sync"/>
-  <int value="5" label="Encrypted all data"/>
-  <int value="6" label="Selected a passphrase"/>
+  <int value="5" label="Encrypted data with new passphrase"/>
+  <int value="6" label="Entered existing passphrase"/>
 </enum>
 
 <enum name="ProfileSyncCustomize">
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index e283410..bcbea7b 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -10708,6 +10708,14 @@
       An enum of type ContentSettingsType.
     </summary>
   </metric>
+  <metric name="PredictionsApiResponse.GrantLikelihood"
+      enum="PredictionGrantLikelihood">
+    <summary>
+      An enum of type PermissionUmaUtil::PredictionGrantLikelihood describing
+      the likelihood returned by the Web Permission Predictions Service, if the
+      service was successfully queried.
+    </summary>
+  </metric>
   <metric name="PriorDismissals">
     <summary>
       The number of dismissed prompts for the given (origin, permission) pair,
@@ -10726,6 +10734,13 @@
       permission prompt, if any.
     </summary>
   </metric>
+  <metric name="PromptDispositionReason"
+      enum="PermissionPromptDispositionReason">
+    <summary>
+      An enum of type PermissionPromptDispositionReason. The reason why the
+      particular prompt disposition was used, if any.
+    </summary>
+  </metric>
   <metric name="SatisfiedAdaptiveTriggers">
     <summary>
       A bitmask consisting of flags defined in enum type AdaptiveTriggers. A bit
diff --git a/tools/origin_trials/check_token.py b/tools/origin_trials/check_token.py
index 9fdbef28..a18a4ba8 100755
--- a/tools/origin_trials/check_token.py
+++ b/tools/origin_trials/check_token.py
@@ -222,9 +222,6 @@
   if (usage_restriction is not None and version != VERSION3):
     print("The usage field can only be be set in Version 3 token.")
     sys.exit(1)
-  if (usage_restriction is not None and not is_third_party):
-    print("Only third party token supports alternative usage restriction.")
-    sys.exit(1)
   if (usage_restriction not in USAGE_RESTRICTION):
     print("Only empty string and \"subset\" are supported in the usage field.")
     sys.exit(1)
diff --git a/tools/origin_trials/generate_token.py b/tools/origin_trials/generate_token.py
index 4cf740e..6e49fbf 100755
--- a/tools/origin_trials/generate_token.py
+++ b/tools/origin_trials/generate_token.py
@@ -226,9 +226,6 @@
     if (args.version[0] != 3):
       print("Only version 3 token supports alternative usage restriction.")
       sys.exit(1)
-    if (not args.is_third_party):
-      print("Only third party token supports alternative usage restriction.")
-      sys.exit(1)
     if (args.usage_restriction not in USAGE_RESTRICTION):
       print(
           "Only empty string and \"subset\" are supported in alternative usage "
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 2b6ba32..5119503 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -13,6 +13,7 @@
 
 from telemetry import benchmark
 from telemetry import page as page_module
+from telemetry.core import exceptions
 from telemetry.core import memory_cache_http_server
 from telemetry.page import legacy_page_test
 from telemetry.page import shared_page_state
@@ -46,11 +47,14 @@
     baseName += "_" + query # So that queried page-names don't collide
   return "{b}.{e}".format(b=baseName, e=extension)
 
-def CreateStorySetFromPath(path, skipped_file,
-                           shared_page_state_class=(
-                               shared_page_state.SharedPageState),
-                           append_query=None,
-                           extra_tags=None):
+
+def CreateStorySetFromPath(
+    path,
+    skipped_file,
+    shared_page_state_class=(shared_page_state.SharedPageState),
+    append_query=None,
+    extra_tags=None,
+    page_class=_BlinkPerfPage):
   assert os.path.exists(path)
 
   page_urls = []
@@ -98,13 +102,24 @@
   common_prefix = os.path.dirname(os.path.commonprefix(all_urls))
   for url in sorted(page_urls):
     name = StoryNameFromUrl(url, common_prefix)
-    ps.AddStory(_BlinkPerfPage(
-        url, ps, ps.base_dir,
-        shared_page_state_class=shared_page_state_class,
-        name=name,
-        tags=extra_tags))
+    ps.AddStory(
+        page_class(
+            url,
+            ps,
+            ps.base_dir,
+            shared_page_state_class=shared_page_state_class,
+            name=name,
+            tags=extra_tags))
   return ps
 
+
+def AddScriptToPage(page, script):
+  if page.script_to_evaluate_on_commit is None:
+    page.script_to_evaluate_on_commit = script
+  else:
+    page.script_to_evaluate_on_commit += script
+
+
 def _CreateMergedEventsBoundaries(events, max_start_time):
   """ Merge events with the given |event_name| and return a list of MergedEvent
   objects. All events that are overlapping are megred together. Same as a union
@@ -255,7 +270,7 @@
 
   def WillNavigateToPage(self, page, tab):
     del tab  # unused
-    page.script_to_evaluate_on_commit = self._blink_perf_js
+    AddScriptToPage(page, self._blink_perf_js)
 
   def DidNavigateToPage(self, page, tab):
     tab.WaitForJavaScriptCondition('testRunner.isWaitingForTelemetry')
@@ -396,17 +411,59 @@
     return 'blink_perf.bindings'
 
 
+class _ServiceWorkerPerfPage(page_module.Page):
+  def RunPageInteractions(self, action_runner):
+    action_runner.ExecuteJavaScript('testRunner.scheduleTestRun()')
+
+    # If |serviceWorkerPerfTools| is enabled in the test, some actions are
+    # performed for each iteration.
+    perf_tools_enabled = False
+    try:
+      perf_tools_enabled = action_runner.EvaluateJavaScript(
+          'serviceWorkerPerfTools.enabled')
+    except exceptions.EvaluateException:
+      pass
+
+    if perf_tools_enabled:
+      done = False
+      while not done:
+        action_runner.WaitForJavaScriptCondition(
+            'serviceWorkerPerfTools.actionRequired')
+        action = action_runner.EvaluateJavaScript(
+            'serviceWorkerPerfTools.action')
+        if action == 'stop-workers':
+          action_runner.tab.StopAllServiceWorkers()
+        elif action == 'quit':
+          done = True
+        else:
+          raise Exception(
+              'Not supported ServiceWorkerPerfTools action: {}'.format(action))
+        action_runner.EvaluateJavaScript(
+            'serviceWorkerPerfTools.notifyActionDone()')
+    action_runner.WaitForJavaScriptCondition('testRunner.isDone', timeout=600)
+
+
 class ServiceWorkerRequestHandler(
     memory_cache_http_server.MemoryCacheDynamicHTTPRequestHandler):
   """This handler returns dynamic responses for service worker perf tests.
   """
+  _request_count = 0
   _SIZE_1K = 1024
   _SIZE_10K = 10240
   _SIZE_1M = 1048576
   _FILE_NAME_PATTERN_1K =\
       re.compile('.*/service_worker/resources/data/1K_[0-9]+\\.txt')
+  _WORKER_NAME_PATTERN = re.compile(\
+      '.*/service_worker/resources/service-worker-[0-9]+\\.generated\\.js')
+  _CHANGING_WORKER_NAME_PATTERN = re.compile(\
+      '.*/service_worker/resources/changing-service-worker\\.generated\\.js')
+  _WORKER_BODY = '''
+      self.addEventListener('fetch', (event) => {
+        event.respondWith(new Response('hello'));
+      });'''
 
   def ResponseFromHandler(self, path):
+    self._request_count += 1
     # normalize the path by replacing backslashes with slashes.
     normpath = path.replace('\\', '/')
     if normpath.endswith('/service_worker/resources/data/10K.txt'):
@@ -415,6 +472,12 @@
       return self.MakeResponse('c' * self._SIZE_1M, 'text/plain', False)
     elif self._FILE_NAME_PATTERN_1K.match(normpath):
       return self.MakeResponse('c' * self._SIZE_1K, 'text/plain', False)
+    elif self._WORKER_NAME_PATTERN.match(normpath):
+      return self.MakeResponse(self._WORKER_BODY, 'text/javascript', False)
+    elif self._CHANGING_WORKER_NAME_PATTERN.match(normpath):
+      # Return different script content for each request.
+      new_body = self._WORKER_BODY + '//' + str(self._request_count)
+      return self.MakeResponse(new_body, 'text/javascript', False)
     return None
 
 
@@ -432,8 +495,19 @@
     return 'UNSCHEDULED_blink_perf.service_worker'
 
   def CreateStorySet(self, options):
-    story_set = super(BlinkPerfServiceWorker, self).CreateStorySet(options)
+    path = os.path.join(BLINK_PERF_BASE_DIR, self.SUBDIR)
+    story_set = CreateStorySetFromPath(
+        path,
+        SKIPPED_FILE,
+        extra_tags=self.TAGS,
+        page_class=_ServiceWorkerPerfPage)
     story_set.SetRequestHandlerClass(ServiceWorkerRequestHandler)
+    with open(
+        os.path.join(os.path.dirname(__file__), 'service_worker_perf.js'),
+        'r') as f:
+      service_worker_perf_js = f.read()
+      for page in story_set.stories:
+        AddScriptToPage(page, service_worker_perf_js)
     return story_set
 
 
diff --git a/tools/perf/benchmarks/service_worker_perf.js b/tools/perf/benchmarks/service_worker_perf.js
new file mode 100644
index 0000000..cc057b75
--- /dev/null
+++ b/tools/perf/benchmarks/service_worker_perf.js
@@ -0,0 +1,59 @@
+// Copyright 2020 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.
+
+// This class cooperates with blink_perf.py to perform actions. Currently only
+// stopping service workers is supported.
+class ServiceWorkerPerfTools {
+  constructor() {
+    this.actionDoneCallback = null;
+    this.actionRequired = false;
+    this.action = null;
+    this.enabled = false;
+  }
+
+  // This should be called before other methods.
+  enable() {
+    this.enabled = true;
+  }
+
+  // Call this to stop all service workers. When the returned promise is
+  // resolved, all service workers are stopped.
+  stopWorkers() {
+    return this.performAction('stop-workers');
+  }
+
+  // Call this to notify blink_perf.py to stop waiting for more actions. When
+  // the returned promise is resolved, blink_perf.py has stopped waiting.
+  quit() {
+    return this.performAction('quit');
+  }
+
+  // Called by blink_perf.py after an action has been performed.
+  notifyActionDone() {
+    if (!this.actionDoneCallback)
+      throw new Error('There is no pending action!');
+
+    this.actionDoneCallback();
+    this.actionDoneCallback = null;
+    this.action = null;
+    this.actionRequired = false;
+  }
+
+  performAction(action) {
+    if (!this.enabled) {
+      throw new TypeError('ServiceWorkerPerfTools is not enabled,' +
+          ' call enable() first!');
+    }
+    if (this.actionRequired) {
+      throw new Error('There is already a pending action:', this.action);
+    }
+    const promise = new Promise(resolve => {
+      this.actionDoneCallback = resolve;
+    });
+    this.action = action;
+    this.actionRequired = true;
+    return promise;
+  }
+}
+window.serviceWorkerPerfTools = new ServiceWorkerPerfTools;
\ No newline at end of file
diff --git a/ui/accessibility/platform/inspect/tree_formatter.h b/ui/accessibility/platform/inspect/tree_formatter.h
index 6468c90..41c7b9f 100644
--- a/ui/accessibility/platform/inspect/tree_formatter.h
+++ b/ui/accessibility/platform/inspect/tree_formatter.h
@@ -57,10 +57,6 @@
   // given pattern.
   virtual base::Value BuildTreeForSelector(const AXTreeSelector&) const = 0;
 
-  // Returns a filtered accessibility tree using the current property and node
-  // filters.
-  virtual base::Value FilterTree(const base::Value& dict) const = 0;
-
   // Dumps accessibility tree.
   virtual std::string FormatTree(const base::Value& tree_node) const = 0;
 
diff --git a/ui/gl/direct_composition_surface_win_unittest.cc b/ui/gl/direct_composition_surface_win_unittest.cc
index df97d0b..fc0b280 100644
--- a/ui/gl/direct_composition_surface_win_unittest.cc
+++ b/ui/gl/direct_composition_surface_win_unittest.cc
@@ -1163,6 +1163,10 @@
 TEST_F(DirectCompositionPixelTest, SwapChainImage) {
   if (!surface_)
     return;
+  // Fails on AMD RX 5500 XT. https://crbug.com/1152565.
+  if (context_ && context_->GetVersionInfo() &&
+      context_->GetVersionInfo()->driver_vendor == "ANGLE (AMD)")
+    return;
 
   Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
       QueryD3D11DeviceObjectFromANGLE();
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index 547eee52..553c9d5 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -19,7 +19,6 @@
 /** @const */ var SCREEN_OOBE_DEMO_SETUP = 'demo-setup';
 /** @const */ var SCREEN_OOBE_DEMO_PREFERENCES = 'demo-preferences';
 /** @const */ var SCREEN_OOBE_KIOSK_ENABLE = 'kiosk-enable';
-/** @const */ var SCREEN_OOBE_AUTO_ENROLLMENT_CHECK = 'auto-enrollment-check';
 /** @const */ var SCREEN_PACKAGED_LICENSE = 'packaged-license';
 /** @const */ var SCREEN_GAIA_SIGNIN = 'gaia-signin';
 /** @const */ var SCREEN_ACCOUNT_PICKER = 'account-picker';
@@ -90,7 +89,6 @@
    */
   var RESET_AVAILABLE_SCREEN_GROUP = [
     SCREEN_OOBE_NETWORK,
-    SCREEN_OOBE_AUTO_ENROLLMENT_CHECK,
     SCREEN_GAIA_SIGNIN,
     SCREEN_ACCOUNT_PICKER,
     SCREEN_KIOSK_ENABLE,
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
index 5119c15..39afc1b 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java
@@ -14,7 +14,7 @@
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.external_intents.ExternalNavigationDelegate;
 import org.chromium.components.external_intents.ExternalNavigationDelegate.StartActivityIfNeededResult;
-import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
+import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.components.external_intents.ExternalNavigationParams;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
@@ -89,10 +89,10 @@
 
     // This method should never be invoked as WebLayer does not handle incoming intents.
     @Override
-    public @OverrideUrlLoadingResultType int handleIncognitoIntentTargetingSelf(
+    public OverrideUrlLoadingResult handleIncognitoIntentTargetingSelf(
             final Intent intent, final String referrerUrl, final String fallbackUrl) {
         assert false;
-        return OverrideUrlLoadingResultType.NO_OVERRIDE;
+        return OverrideUrlLoadingResult.forNoOverride();
     }
 
     @Override