diff --git a/DEPS b/DEPS
index 0d29a066..6638734 100644
--- a/DEPS
+++ b/DEPS
@@ -195,11 +195,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': '1860aed6436d5a39021c9f3da1a654893b232e21',
+  'skia_revision': '16fbc2477205aa06efb03e9bdaa0e4a5e94ee865',
   # 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': 'f3d231ec7eb141755c6fd5e81283230fed94c301',
+  'v8_revision': '562c3f952c5d5fb397bbcdf89038febe321a9431',
   # 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.
@@ -211,7 +211,7 @@
   # 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': 'ea6f21a052c97dbfa4890e1d6840db2fe64ba1d8',
+  'swiftshader_revision': '9e46bb61c5d7fd6f839e53a51572fe2a64e32489',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -266,7 +266,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': '426f13e93f99cd03aa07912f84dbfc993a7d1186',
+  'devtools_frontend_revision': '270e6f332c4179801c60551c828c07c69104e594',
   # 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.
@@ -302,7 +302,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.
-  'spv_tools_revision': '680c77fa6c81ba03d19b3315d1cad116fc4e6da2',
+  'spv_tools_revision': '717e7877cac15d393fd3bb1bd872679de8b59add',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -314,11 +314,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'shaderc_revision': '13480cb0ab948e00a5a12794bb6d497a43f9b52c',
+  'shaderc_revision': '6af6e625573fb20bd64fbd36af31ef1420751ddd',
   # 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': 'd0dd661f180503e1da910a6256b42ff8d72ab936',
+  'dawn_revision': '92ebe87b74f1f7e80fb0e2133c9976715c1b6d46',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -368,7 +368,7 @@
   # revisions.
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:d585128cdaf3e6ff7bfd58641965e60c12618eb1',
+  'gn_version': 'git_revision:3028c6a426a4aaf6da91c4ebafe716ae370225fe',
 
   # Also, if you change these, update buildtools/DEPS too. Also update the
   # libc++ svn_revision in //buildtools/deps_revisions.gni.
@@ -1248,7 +1248,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0997f358ce073f83d6a4bba44ffce2d772077577',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '1dcc3cfc0639ebb0636c30a43e69f599f9c1a981',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1326,7 +1326,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'bMBorsHPd-GA42g-lfkvaE5HhiZVCcqkoLNKDzb8ElUC'
+              'version': 'cY2ImNSFGqld2lxLH7Yx4VM1eC-ObygJfhY_bExfcrAC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1476,7 +1476,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'd1e8e6122c754cf178cf918d341d04ca1bb196ef',
+    Var('webrtc_git') + '/src.git' + '@' + 'fa504e744f9be405e7fb00ff7a2f1d72e5a3263e',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1548,7 +1548,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d969ac13d8062b6093f774a85ed74bcb0c3ac624',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@de55e0b546684abb62d89415b7cbd274d80fbec3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f9e4c5bb..845bdc9 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3788,6 +3788,7 @@
     deps = [
       ":base_java",
       "//testing/android/junit:junit_test_support",
+      "//third_party/android_deps:androidx_test_core_java",
       "//third_party/android_deps:androidx_test_monitor_java",
       "//third_party/android_deps:androidx_test_uiautomator_uiautomator_java",
       "//third_party/android_deps:robolectric_all_java",
diff --git a/base/allocator/partition_allocator/checked_ptr_support.h b/base/allocator/partition_allocator/checked_ptr_support.h
index 1322427..2e2c871c 100644
--- a/base/allocator/partition_allocator/checked_ptr_support.h
+++ b/base/allocator/partition_allocator/checked_ptr_support.h
@@ -7,5 +7,6 @@
 
 #define ENABLE_TAG_FOR_CHECKED_PTR2 0
 #define ENABLE_TAG_FOR_MTE_CHECKED_PTR 0
+#define ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR 0
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_CHECKED_PTR_SUPPORT_H_
diff --git a/base/allocator/partition_allocator/partition_alloc.cc b/base/allocator/partition_allocator/partition_alloc.cc
index fc52fca..3d8c32f 100644
--- a/base/allocator/partition_allocator/partition_alloc.cc
+++ b/base/allocator/partition_allocator/partition_alloc.cc
@@ -73,17 +73,25 @@
                   (1UL << 31) + kPageAllocationGranularity,
               "maximum direct mapped allocation");
 // Check that some of our zanier calculations worked out as expected.
-#if !ENABLE_TAG_FOR_MTE_CHECKED_PTR
-static_assert(kGenericSmallestBucket == alignof(std::max_align_t),
+#if ENABLE_TAG_FOR_MTE_CHECKED_PTR
+static_assert(kGenericSmallestBucket >= alignof(std::max_align_t),
               "generic smallest bucket");
 #else
-static_assert(kGenericSmallestBucket >= alignof(std::max_align_t),
+static_assert(kGenericSmallestBucket == alignof(std::max_align_t),
               "generic smallest bucket");
 #endif
 static_assert(kGenericMaxBucketed == 983040, "generic max bucketed");
 static_assert(kMaxSystemPagesPerSlotSpan < (1 << 8),
               "System pages per slot span must be less than 128.");
 
+#if ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
+namespace internal {
+BASE_EXPORT PartitionTagWrapper g_checked_ptr_single_tag = {{},
+                                                            kFixedTagValue,
+                                                            {}};
+}
+#endif
+
 Lock& GetHooksLock() {
   static NoDestructor<Lock> lock;
   return *lock;
diff --git a/base/allocator/partition_allocator/partition_alloc.h b/base/allocator/partition_allocator/partition_alloc.h
index 3a7f887..104be59 100644
--- a/base/allocator/partition_allocator/partition_alloc.h
+++ b/base/allocator/partition_allocator/partition_alloc.h
@@ -419,6 +419,9 @@
 #if ENABLE_TAG_FOR_CHECKED_PTR2 || ENABLE_TAG_FOR_MTE_CHECKED_PTR
   internal::PartitionTag current_partition_tag = 0;
 #endif
+#if ENABLE_TAG_FOR_MTE_CHECKED_PTR
+  char* next_tag_bitmap_page = nullptr;
+#endif
 
   // Some pre-computed constants.
   size_t order_index_shifts[kBitsPerSizeT + 1] = {};
@@ -566,15 +569,17 @@
     PA_DCHECK(raw_size == size);
     new_slot_size = raw_size;
   }
-  // Layout inside the slot: |tag|cookie|object|[empty]|cookie|
-  //                                    <--a--->
-  //                                    <------b------->
-  //                         <---------------c---------------->
+  // Layout inside the slot: |[tag]|cookie|object|[empty]|cookie|
+  //                                      <--a--->
+  //                                      <------b------->
+  //                         <-----------------c---------------->
   //   a: size
   //   b: size_with_no_extras
   //   c: new_slot_size
   // Note, empty space occurs if the slot size is larger than needed to
   // accommodate the request.
+  // The tag may or may not exist in the slot, depending on CheckedPtr
+  // implementation.
   size_t size_with_no_extras =
       internal::PartitionSizeAdjustSubtract(allow_extras, new_slot_size);
   // The value given to the application is just after the tag and cookie.
@@ -600,7 +605,9 @@
   }
 
   if (allow_extras && !bucket->is_direct_mapped()) {
-    internal::PartitionTagSetValue(ret, page->bucket->slot_size,
+    size_t slot_size_with_no_extras = internal::PartitionSizeAdjustSubtract(
+        allow_extras, page->bucket->slot_size);
+    internal::PartitionTagSetValue(ret, slot_size_with_no_extras,
                                    GetNewPartitionTag());
   }
 
@@ -627,8 +634,10 @@
   PA_DCHECK(IsValidPage(page));
   auto* root = PartitionRoot<thread_safe>::FromPage(page);
   if (root->allow_extras && !page->bucket->is_direct_mapped()) {
+    size_t size_with_no_extras = internal::PartitionSizeAdjustSubtract(
+        root->allow_extras, page->bucket->slot_size);
     // TODO(tasak): clear partition tag. Temporarily set the tag to be 0.
-    internal::PartitionTagClearValue(ptr, page->bucket->slot_size);
+    internal::PartitionTagClearValue(ptr, size_with_no_extras);
   }
   ptr = internal::PartitionPointerAdjustSubtract(root->allow_extras, ptr);
   internal::DeferredUnmap deferred_unmap;
@@ -823,9 +832,7 @@
   }
   size_t requested_size = size;
   size = internal::PartitionSizeAdjustAdd(allow_extras, size);
-#if ENABLE_TAG_FOR_CHECKED_PTR2
-  PA_CHECK(size >= requested_size);
-#endif
+  PA_CHECK(size >= requested_size);  // check for overflows
   auto* bucket = SizeToBucket(size);
   PA_DCHECK(bucket);
   {
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 0285ef5..79bdff6 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2490,7 +2490,8 @@
   }
 }
 
-#if ENABLE_TAG_FOR_CHECKED_PTR2 || ENABLE_TAG_FOR_MTE_CHECKED_PTR
+#if ENABLE_TAG_FOR_CHECKED_PTR2 || ENABLE_TAG_FOR_MTE_CHECKED_PTR || \
+    ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
 
 TEST_F(PartitionAllocTest, TagBasic) {
   size_t alloc_size = 64 - kExtraAllocSize;
@@ -2513,28 +2514,33 @@
   EXPECT_EQ(char_ptr1 + page->bucket->slot_size, char_ptr2);
   EXPECT_EQ(char_ptr2 + page->bucket->slot_size, char_ptr3);
 
+#if !ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
   constexpr PartitionTag kTag1 = static_cast<PartitionTag>(0xBADA);
   constexpr PartitionTag kTag2 = static_cast<PartitionTag>(0xDB8A);
   constexpr PartitionTag kTag3 = static_cast<PartitionTag>(0xA3C4);
+#else
+  // The in-memory tag will always be kFixedTagValue no matter what we set.
+  constexpr PartitionTag kTag1 = static_cast<PartitionTag>(kFixedTagValue);
+  constexpr PartitionTag kTag2 = static_cast<PartitionTag>(kFixedTagValue);
+  constexpr PartitionTag kTag3 = static_cast<PartitionTag>(kFixedTagValue);
+#endif
   PartitionTagSetValue(ptr1, page->bucket->slot_size, kTag1);
   PartitionTagSetValue(ptr2, page->bucket->slot_size, kTag2);
   PartitionTagSetValue(ptr3, page->bucket->slot_size, kTag3);
 
-  memset(ptr1, 0, kTestAllocSize);
-  memset(ptr2, 0, kTestAllocSize);
-  memset(ptr3, 0, kTestAllocSize);
+  memset(ptr1, 0, alloc_size);
+  memset(ptr2, 0, alloc_size);
+  memset(ptr3, 0, alloc_size);
 
   EXPECT_EQ(kTag1, PartitionTagGetValue(ptr1));
   EXPECT_EQ(kTag2, PartitionTagGetValue(ptr2));
   EXPECT_EQ(kTag3, PartitionTagGetValue(ptr3));
 
-  EXPECT_TRUE(!memchr(ptr1, static_cast<uint8_t>(kTag1), kTestAllocSize));
-  EXPECT_TRUE(!memchr(ptr2, static_cast<uint8_t>(kTag2), kTestAllocSize));
+  EXPECT_TRUE(!memchr(ptr1, static_cast<uint8_t>(kTag1), alloc_size));
+  EXPECT_TRUE(!memchr(ptr2, static_cast<uint8_t>(kTag2), alloc_size));
   if (sizeof(PartitionTag) > 1) {
-    EXPECT_TRUE(
-        !memchr(ptr1, static_cast<uint8_t>(kTag1 >> 8), kTestAllocSize));
-    EXPECT_TRUE(
-        !memchr(ptr2, static_cast<uint8_t>(kTag2 >> 8), kTestAllocSize));
+    EXPECT_TRUE(!memchr(ptr1, static_cast<uint8_t>(kTag1 >> 8), alloc_size));
+    EXPECT_TRUE(!memchr(ptr2, static_cast<uint8_t>(kTag2 >> 8), alloc_size));
   }
 
   allocator.root()->Free(ptr1);
@@ -2545,11 +2551,8 @@
   EXPECT_EQ(ptr2, new_ptr2);
   EXPECT_EQ(kTag3, PartitionTagGetValue(ptr3));
 
-  request_size =
-      page->bucket->slot_size - kExtraAllocSize + kInSlotTagBufferSize;
-#if ENABLE_TAG_FOR_MTE_CHECKED_PTR
-  request_size += kGenericSmallestBucket;
-#endif
+  // Add 1B to ensure the object is rellocated to a larger slot.
+  request_size = page->bucket->slot_size - kExtraAllocSize + 1;
   new_ptr2 = allocator.root()->Realloc(ptr2, request_size, type_name);
   EXPECT_TRUE(new_ptr2);
   EXPECT_NE(ptr2, new_ptr2);
@@ -2562,42 +2565,6 @@
 
 #endif
 
-#if ENABLE_TAG_FOR_CHECKED_PTR2
-
-TEST_F(PartitionAllocTest, TagForDirectMap) {
-  size_t request_size = kGenericMinDirectMappedDownsize + kExtraAllocSize;
-  void* ptr1 = allocator.root()->Alloc(request_size, type_name);
-  void* ptr2 = allocator.root()->Alloc(request_size, type_name);
-  EXPECT_TRUE(ptr1);
-  EXPECT_TRUE(ptr2);
-
-  PartitionRoot<ThreadSafe>::Page* page =
-      PartitionRoot<ThreadSafe>::Page::FromPointer(
-          PartitionPointerAdjustSubtract(true, ptr1));
-  EXPECT_TRUE(page);
-
-  constexpr PartitionTag kTag1 = 0xBADA;
-  constexpr PartitionTag kTag2 = 0xDB8A;
-  PartitionTagSetValue(ptr1, kTag1);
-  PartitionTagSetValue(ptr2, kTag2);
-
-  memset(ptr1, 0, request_size);
-  memset(ptr2, 0, request_size);
-  EXPECT_EQ(kTag1, PartitionTagGetValue(ptr1));
-  EXPECT_EQ(kTag2, PartitionTagGetValue(ptr2));
-
-  EXPECT_TRUE(!memchr(ptr1, static_cast<uint8_t>(kTag1 >> 8), kTestAllocSize));
-  EXPECT_TRUE(!memchr(ptr1, static_cast<uint8_t>(kTag1), kTestAllocSize));
-  EXPECT_TRUE(!memchr(ptr2, static_cast<uint8_t>(kTag2 >> 8), kTestAllocSize));
-  EXPECT_TRUE(!memchr(ptr2, static_cast<uint8_t>(kTag2), kTestAllocSize));
-
-  allocator.root()->Free(ptr1);
-  EXPECT_EQ(kTag2, PartitionTagGetValue(ptr2));
-  allocator.root()->Free(ptr2);
-}
-
-#endif
-
 }  // namespace internal
 }  // namespace base
 
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index b061a96..e8c58a318 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -239,6 +239,27 @@
 
     root->next_partition_page += total_size;
     root->IncreaseCommittedPages(total_size);
+
+#if ENABLE_TAG_FOR_MTE_CHECKED_PTR
+    PA_DCHECK(root->next_tag_bitmap_page);
+    char* next_tag_bitmap_page = reinterpret_cast<char*>(
+        bits::Align(reinterpret_cast<uintptr_t>(
+                        PartitionTagPointer(root->next_partition_page)),
+                    kSystemPageSize));
+    if (root->next_tag_bitmap_page < next_tag_bitmap_page) {
+#if DCHECK_IS_ON()
+      char* super_page = reinterpret_cast<char*>(
+          reinterpret_cast<uintptr_t>(ret) & kSuperPageBaseMask);
+      char* tag_bitmap = super_page + kPartitionPageSize;
+      PA_DCHECK(next_tag_bitmap_page <= tag_bitmap + kActualTagBitmapSize);
+      PA_DCHECK(next_tag_bitmap_page > tag_bitmap);
+#endif
+      SetSystemPagesAccess(root->next_tag_bitmap_page,
+                           next_tag_bitmap_page - root->next_tag_bitmap_page,
+                           PageReadWrite);
+      root->next_tag_bitmap_page = next_tag_bitmap_page;
+    }
+#endif
     return ret;
   }
 
@@ -289,12 +310,22 @@
   SetSystemPagesAccess(super_page + (kSystemPageSize * 2),
                        kPartitionPageSize - (kSystemPageSize * 2),
                        PageInaccessible);
-  if (kActualTagBitmapSize < kReservedTagBitmapSize) {
-    // Make guard pages between tag bitmap and the first slotspan if possible.
-    SetSystemPagesAccess(tag_bitmap + kActualTagBitmapSize,
-                         kReservedTagBitmapSize - kActualTagBitmapSize,
-                         PageInaccessible);
-  }
+#if ENABLE_TAG_FOR_MTE_CHECKED_PTR
+  // Make the first |total_size| region of the tag bitmap accessible.
+  // The rest of the region is set to inaccessible.
+  char* next_tag_bitmap_page = reinterpret_cast<char*>(
+      bits::Align(reinterpret_cast<uintptr_t>(
+                      PartitionTagPointer(root->next_partition_page)),
+                  kSystemPageSize));
+  PA_DCHECK(next_tag_bitmap_page <= tag_bitmap + kActualTagBitmapSize);
+  PA_DCHECK(next_tag_bitmap_page > tag_bitmap);
+  // |ret| points at the end of the tag bitmap.
+  PA_DCHECK(next_tag_bitmap_page <= ret);
+  SetSystemPagesAccess(next_tag_bitmap_page, ret - next_tag_bitmap_page,
+                       PageInaccessible);
+  root->next_tag_bitmap_page = next_tag_bitmap_page;
+#endif
+
   //  SetSystemPagesAccess(super_page + (kSuperPageSize -
   //  kPartitionPageSize),
   //                             kPartitionPageSize, PageInaccessible);
diff --git a/base/allocator/partition_allocator/partition_tag.h b/base/allocator/partition_allocator/partition_tag.h
index 4dee8f9..91b6c15 100644
--- a/base/allocator/partition_allocator/partition_tag.h
+++ b/base/allocator/partition_allocator/partition_tag.h
@@ -11,6 +11,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/allocator/partition_allocator/partition_cookie.h"
 #include "base/allocator/partition_allocator/partition_tag_bitmap.h"
+#include "base/base_export.h"
 #include "base/notreached.h"
 #include "build/build_config.h"
 
@@ -22,9 +23,7 @@
 
 // Use 16 bits for the partition tag.
 // TODO(tasak): add a description about the partition tag.
-using PartitionTag = uint16_t;
-
-static constexpr PartitionTag kTagTemporaryInitialValue = 0x0BAD;
+using PartitionTag = uint8_t;
 
 // Allocate extra 16 bytes for the partition tag. 14 bytes are unused
 // (reserved).
@@ -87,8 +86,6 @@
     sizeof(PartitionTag) == tag_bitmap::kPartitionTagSize,
     "sizeof(PartitionTag) must be equal to bitmap::kPartitionTagSize.");
 
-static constexpr PartitionTag kTagTemporaryInitialValue = 0xAD;
-
 static constexpr size_t kInSlotTagBufferSize = 0;
 
 ALWAYS_INLINE size_t PartitionTagSizeAdjustAdd(size_t size) {
@@ -150,11 +147,56 @@
   memset(PartitionTagPointer(ptr), 0, tag_region_size);
 }
 
-#else  // !ENABLE_TAG_FOR_CHECKED_PTR2 && !ENABLE_TAG_FOR_MTE_CHECKED_PTR
+#elif ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
 
-using PartitionTag = uint16_t;
+using PartitionTag = uint8_t;
 
-static constexpr PartitionTag kTagTemporaryInitialValue = 0;
+static constexpr PartitionTag kFixedTagValue = 0xAD;
+
+struct PartitionTagWrapper {
+  // Add padding before and after the tag, to avoid cacheline false sharing.
+  // Assume cacheline is 64B.
+  uint8_t unused1[64];
+  PartitionTag partition_tag;
+  uint8_t unused2[64];
+};
+extern BASE_EXPORT PartitionTagWrapper g_checked_ptr_single_tag;
+
+static constexpr size_t kInSlotTagBufferSize = 0;
+
+ALWAYS_INLINE size_t PartitionTagSizeAdjustAdd(size_t size) {
+  return size;
+}
+
+ALWAYS_INLINE size_t PartitionTagSizeAdjustSubtract(size_t size) {
+  return size;
+}
+
+ALWAYS_INLINE PartitionTag* PartitionTagPointer(void*) {
+  return &g_checked_ptr_single_tag.partition_tag;
+}
+
+ALWAYS_INLINE void* PartitionTagPointerAdjustSubtract(void* ptr) {
+  return ptr;
+}
+
+ALWAYS_INLINE void* PartitionTagPointerAdjustAdd(void* ptr) {
+  return ptr;
+}
+
+ALWAYS_INLINE void PartitionTagSetValue(void*, size_t, PartitionTag) {}
+
+ALWAYS_INLINE PartitionTag PartitionTagGetValue(void* ptr) {
+  return *PartitionTagPointer(ptr);
+}
+
+ALWAYS_INLINE void PartitionTagClearValue(void* ptr, size_t) {}
+
+#else  // !ENABLE_TAG_FOR_CHECKED_PTR2 && !ENABLE_TAG_FOR_MTE_CHECKED_PTR &&
+       // !ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
+
+using PartitionTag = uint8_t;
+
 static constexpr size_t kInSlotTagBufferSize = 0;
 
 ALWAYS_INLINE size_t PartitionTagSizeAdjustAdd(size_t size) {
@@ -186,7 +228,8 @@
 
 ALWAYS_INLINE void PartitionTagClearValue(void* ptr, size_t) {}
 
-#endif  // !ENABLE_TAG_FOR_CHECKED_PTR2
+#endif  // !ENABLE_TAG_FOR_CHECKED_PTR2 && !ENABLE_TAG_FOR_MTE_CHECKED_PTR &&
+        // !ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
 
 }  // namespace internal
 }  // namespace base
diff --git a/base/memory/checked_ptr.cc b/base/memory/checked_ptr.cc
index 056de5b3..e8f9f02 100644
--- a/base/memory/checked_ptr.cc
+++ b/base/memory/checked_ptr.cc
@@ -35,7 +35,10 @@
   // the same result regardless, anyway.
   // TODO(bartekn): Figure out the thread-safety mismatch.
   return IsManagedByPartitionAllocNormalBuckets(ptr)
-#if ENABLE_TAG_FOR_CHECKED_PTR2
+  // Checking offset is not needed for ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR,
+  // but call it anyway for apples-to-apples comparison with
+  // ENABLE_TAG_FOR_CHECKED_PTR2.
+#if ENABLE_TAG_FOR_CHECKED_PTR2 || ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR
          && PartitionAllocGetSlotOffset<ThreadSafe>(ptr) == 0
 #endif
       ;
diff --git a/base/memory/checked_ptr.h b/base/memory/checked_ptr.h
index 37dc060..46f0c4ba 100644
--- a/base/memory/checked_ptr.h
+++ b/base/memory/checked_ptr.h
@@ -20,7 +20,8 @@
 
 #define ENABLE_CHECKED_PTR2_OR_MTE_IMPL 0
 #if ENABLE_CHECKED_PTR2_OR_MTE_IMPL
-static_assert(ENABLE_TAG_FOR_CHECKED_PTR2 || ENABLE_TAG_FOR_MTE_CHECKED_PTR,
+static_assert(ENABLE_TAG_FOR_CHECKED_PTR2 || ENABLE_TAG_FOR_MTE_CHECKED_PTR ||
+                  ENABLE_TAG_FOR_SINGLE_TAG_CHECKED_PTR,
               "CheckedPtr2OrMTEImpl can only by used if tags are enabled");
 #endif
 
@@ -185,10 +186,10 @@
   static ALWAYS_INLINE void* SafelyUnwrapPtrForDereference(
       uintptr_t wrapped_ptr) {
 #if CHECKED_PTR2_AVOID_BRANCH_WHEN_CHECKING_ENABLED
-    // This variant cannot be used with MTECheckedPtr algorithm, because it
+    // This variant can only be used with CheckedPtr2 algorithm, because it
     // relies on the generation to exist at a constant offset before the
     // allocation.
-    static_assert(!ENABLE_TAG_FOR_MTE_CHECKED_PTR, "");
+    static_assert(ENABLE_TAG_FOR_CHECKED_PTR2, "");
 
     // Top bit tells if the protection is enabled. Use it to decide whether to
     // read the word before the allocation, which exists only if the protection
diff --git a/base/memory/checked_ptr_unittest.cc b/base/memory/checked_ptr_unittest.cc
index 6ce8aaae..71174c5 100644
--- a/base/memory/checked_ptr_unittest.cc
+++ b/base/memory/checked_ptr_unittest.cc
@@ -772,7 +772,38 @@
   EXPECT_DEATH_IF_SUPPORTED(if (*ptr == 42) return, "");
 }
 
-#endif
+#ifdef ENABLE_TAG_FOR_MTE_CHECKED_PTR
+TEST(CheckedPtr2OrMTEImpl, CrashOnUseAfterFree_WithOffset) {
+  // This test works only if GigaCage is enabled. Bail out otherwise.
+  if (!IsPartitionAllocGigaCageEnabled())
+    return;
+
+  // TODO(bartekn): Avoid using PartitionAlloc API directly. Switch to
+  // new/delete once PartitionAlloc Everywhere is fully enabled.
+  PartitionAllocGlobalInit(HandleOOM);
+  PartitionAllocator<ThreadSafe> allocator;
+  allocator.init();
+  const uint8_t kSize = 100;
+  void* raw_ptr = allocator.root()->Alloc(kSize * sizeof(uint8_t), "uint8_t");
+  // Use the actual CheckedPtr implementation, not a test substitute, to
+  // exercise real PartitionAlloc paths.
+  CheckedPtr<uint8_t> ptrs[kSize];
+  for (uint8_t i = 0; i < kSize; ++i) {
+    ptrs[i] = static_cast<uint8_t*>(raw_ptr) + i;
+  }
+  for (uint8_t i = 0; i < kSize; ++i) {
+    *ptrs[i] = 42 + i;
+    EXPECT_TRUE(*ptrs[i] == 42 + i);
+  }
+  allocator.root()->Free(raw_ptr);
+  for (uint8_t i = 0; i < kSize; i += 15) {
+    EXPECT_DEATH_IF_SUPPORTED(if (*ptrs[i] == 42 + i) return, "");
+  }
+}
+#endif  // ENABLE_TAG_FOR_MTE_CHECKED_PTR
+
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC) && ENABLE_CHECKED_PTR2_OR_MTE_IMPL &&
+        // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 
 }  // namespace internal
 }  // namespace base
diff --git a/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java b/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java
index 6a52b9e..86afec9 100644
--- a/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java
+++ b/base/test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java
@@ -4,9 +4,10 @@
 
 package org.chromium.base.test;
 
+import androidx.test.core.app.ApplicationProvider;
+
 import org.junit.runners.model.InitializationError;
 import org.robolectric.DefaultTestLifecycle;
-import org.robolectric.RuntimeEnvironment;
 import org.robolectric.TestLifecycle;
 
 import org.chromium.base.ApplicationStatus;
@@ -28,8 +29,9 @@
     public static class BaseTestLifecycle extends DefaultTestLifecycle {
         @Override
         public void beforeTest(Method method) {
-            ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application);
-            ApplicationStatus.initialize(RuntimeEnvironment.application);
+            ContextUtils.initApplicationContextForTests(
+                    ApplicationProvider.getApplicationContext());
+            ApplicationStatus.initialize(ApplicationProvider.getApplicationContext());
             CommandLine.init(null);
             super.beforeTest(method);
         }
diff --git a/base/test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java b/base/test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java
index 22efb72a..de71920 100644
--- a/base/test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java
+++ b/base/test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java
@@ -12,6 +12,8 @@
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 
+import androidx.test.core.app.ApplicationProvider;
+
 import org.junit.Assert;
 import org.junit.rules.ExternalResource;
 import org.junit.runner.Description;
@@ -21,7 +23,6 @@
 import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.BlockJUnit4ClassRunner;
 import org.junit.runners.model.InitializationError;
-import org.robolectric.RuntimeEnvironment;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -49,7 +50,7 @@
         Instrumentation instrumentation = new Instrumentation() {
             @Override
             public Context getTargetContext() {
-                return RuntimeEnvironment.application;
+                return ApplicationProvider.getApplicationContext();
             }
         };
         InstrumentationRegistry.registerInstance(instrumentation, new Bundle());
diff --git a/base/tracing/OWNERS b/base/tracing/OWNERS
new file mode 100644
index 0000000..ac907a7
--- /dev/null
+++ b/base/tracing/OWNERS
@@ -0,0 +1,3 @@
+file://base/trace_event/OWNERS
+# COMPONENT: Speed>Tracing
+# TEAM: tracing@chromium.org
\ No newline at end of file
diff --git a/base/tracing/README.md b/base/tracing/README.md
new file mode 100644
index 0000000..fac9f22f
--- /dev/null
+++ b/base/tracing/README.md
@@ -0,0 +1,26 @@
+# Overhauled performance tracing in Chrome
+
+We are upgrading Chrome's support for performance tracing by replacing Chrome's implementation of
+TRACE_EVENT macros from //base/trace_event with [Perfetto](https://perfetto.dev). Perfetto
+introduces [trace events with typed
+arguments](https://perfetto.dev/docs/instrumentation/track-events) to support privacy-filtered trace
+recording and a more compact, efficient, and stable trace encoding.
+
+The Perfetto library itself lives in
+[AOSP](https://android.googlesource.com/platform/external/perfetto/) and is rolled in
+[/third_party/chrome/](https://cs.chromium.org/chromium/src/third_party/perfetto/). Progress is
+tracked on https://crbug.com/1006541.
+
+The code in this directory connects Chrome to Perfetto's [tracing
+SDK](https://perfetto.dev/docs/instrumentation/tracing-sdk), which implements trace event macros on
+top of Perfetto's [tracing service](https://perfetto.dev/docs/concepts/service-model). This service
+can be run in-process (e.g. in unit tests), as a Chrome mojo service (see //services/tracing), or as
+a system service on Android.
+
+For more details, see [Perfetto's documentation](https://docs.perfetto.dev), [Typed trace events in
+Chrome](https://docs.google.com/document/d/1f7tt4cb-JcA5bQFR1oXk60ncJPpkL02_Hi_Bc6MfTQk/edit#), and
+[Typed trace events in
+//base](https://docs.google.com/document/d/1UQ4Ez7B-TeowijOUuMXuoWj1amZcQ7E2abt3s4jaAEY/edit#).
+
+Note: The integration with Perfetto's SDK is work-in-progress behind the gn flag
+"use_perfetto_client_library".
\ No newline at end of file
diff --git a/base/tracing/protos/README.md b/base/tracing/protos/README.md
new file mode 100644
index 0000000..8185948
--- /dev/null
+++ b/base/tracing/protos/README.md
@@ -0,0 +1,12 @@
+# Perfetto typed events for Chrome
+
+**NOTE**: This is a work-in-progress.
+
+In order to simplify adding new typed events for Chrome tracing, a protobuf extension support is
+currently being implemented in Perfetto. The plan is that this folder is going to contain Chrome's
+extensions to TrackEvent, and the directory is going to be autorolled into Perfetto repository.
+
+More information: https://perfetto.dev/docs/design-docs/extensions
+
+As this is developed, the current process to add new types of trace events is documented on
+go/chrometto.
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 68767ac8..2350c429 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -376,6 +376,8 @@
     <ignore regexp="android_webview/test/shell/res/raw/resource_file.html"/>
     <ignore regexp="android_webview/test/shell/res/raw/resource_icon.png"/>
     <ignore regexp="android_webview/tools/automated_ui_tests/java/res/layout/"/>
+    <!-- 1: resource in //ui because it's used by multiple deps. -->
+    <ignore regexp="The resource `R.drawable.*_expand_.*` appears to be unused"/>
     <!-- 3: WebView uses some //components/browser_ui resources, but not all (crbug.com/1099842). -->
     <ignore regexp="components/browser_ui/styles/"/>
     <ignore regexp="components/browser_ui/widget/"/>
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index eb8ab9c..45b8b89 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200719.3.1
+0.20200720.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index eb8ab9c..45b8b89 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200719.3.1
+0.20200720.1.1
diff --git a/buildtools/DEPS b/buildtools/DEPS
index 4703a66..6d944a7 100644
--- a/buildtools/DEPS
+++ b/buildtools/DEPS
@@ -14,7 +14,7 @@
   #
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:d585128cdaf3e6ff7bfd58641965e60c12618eb1',
+  'gn_version': 'git_revision:3028c6a426a4aaf6da91c4ebafe716ae370225fe',
 
   # When changing these, also update the svn revisions in deps_revisions.gni
   'clang_format_revision': '96636aa0e9f047f17447f2d45a094d0b59ed7917',
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index 4b1d723..ba7eeaa 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -8,6 +8,7 @@
 #include "base/component_export.h"
 #include "cc/input/overscroll_behavior.h"
 #include "cc/input/touch_action.h"
+#include "cc/trees/browser_controls_params.h"
 #include "ipc/ipc_message_macros.h"
 
 #undef IPC_MESSAGE_EXPORT
@@ -24,4 +25,13 @@
 
 IPC_ENUM_TRAITS_MAX_VALUE(cc::TouchAction, cc::TouchAction::kMax)
 
+IPC_STRUCT_TRAITS_BEGIN(cc::BrowserControlsParams)
+  IPC_STRUCT_TRAITS_MEMBER(top_controls_height)
+  IPC_STRUCT_TRAITS_MEMBER(top_controls_min_height)
+  IPC_STRUCT_TRAITS_MEMBER(bottom_controls_height)
+  IPC_STRUCT_TRAITS_MEMBER(bottom_controls_min_height)
+  IPC_STRUCT_TRAITS_MEMBER(animate_browser_controls_height_changes)
+  IPC_STRUCT_TRAITS_MEMBER(browser_controls_shrink_blink_size)
+IPC_STRUCT_TRAITS_END()
+
 #endif  // CC_IPC_CC_PARAM_TRAITS_MACROS_H_
diff --git a/cc/mojom/BUILD.gn b/cc/mojom/BUILD.gn
index 12d7925..7c0b5ab 100644
--- a/cc/mojom/BUILD.gn
+++ b/cc/mojom/BUILD.gn
@@ -7,40 +7,46 @@
 mojom("mojom") {
   generate_java = true
   sources = [
+    "browser_controls_params.mojom",
     "overscroll_behavior.mojom",
     "touch_action.mojom",
   ]
 
   public_deps = [ "//mojo/public/mojom/base" ]
 
-  touch_action_typemap = {
-    types = [
-      {
-        mojom = "cc.mojom.TouchAction"
-        cpp = "::cc::TouchAction"
-      },
-    ]
-    traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
-    traits_public_deps = [ "//cc/ipc" ]
-  }
-
-  overscroll_behavior_typemap = {
-    types = [
-      {
-        mojom = "cc.mojom.OverscrollBehavior"
-        cpp = "::cc::OverscrollBehavior"
-      },
-    ]
-    traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
-    traits_public_deps = [ "//cc/ipc" ]
-  }
-
-  cpp_typemaps = [
-    overscroll_behavior_typemap,
-    touch_action_typemap,
+  shared_typemap = [
+    {
+      types = [
+        {
+          mojom = "cc.mojom.TouchAction"
+          cpp = "::cc::TouchAction"
+        },
+      ]
+      traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+      traits_public_deps = [ "//cc/ipc" ]
+    },
+    {
+      types = [
+        {
+          mojom = "cc.mojom.OverscrollBehavior"
+          cpp = "::cc::OverscrollBehavior"
+        },
+      ]
+      traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+      traits_public_deps = [ "//cc/ipc" ]
+    },
+    {
+      types = [
+        {
+          mojom = "cc.mojom.BrowserControlsParams"
+          cpp = "::cc::BrowserControlsParams"
+        },
+      ]
+      traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+      traits_public_deps = [ "//cc/ipc" ]
+    },
   ]
-  blink_cpp_typemaps = [
-    overscroll_behavior_typemap,
-    touch_action_typemap,
-  ]
+
+  cpp_typemaps = shared_typemap
+  blink_cpp_typemaps = shared_typemap
 }
diff --git a/cc/mojom/browser_controls_params.mojom b/cc/mojom/browser_controls_params.mojom
new file mode 100644
index 0000000..0fbf83c
--- /dev/null
+++ b/cc/mojom/browser_controls_params.mojom
@@ -0,0 +1,8 @@
+// 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.
+
+module cc.mojom;
+
+[Native]
+struct BrowserControlsParams;
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 840d1d2..4399ca9f 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -710,6 +710,7 @@
   "java/res/drawable/ic_full_screen_white_24dp.xml",
   "java/res/drawable/ic_globe_24dp.xml",
   "java/res/drawable/ic_google_round.xml",
+  "java/res/drawable/ic_incognito_24dp.xml",
   "java/res/drawable/ic_logo_googleg_20dp.xml",
   "java/res/drawable/ic_loop_round.xml",
   "java/res/drawable/ic_music_note_24dp.xml",
@@ -732,8 +733,11 @@
   "java/res/drawable/ic_site_timer.xml",
   "java/res/drawable/ic_swap_vert_round.xml",
   "java/res/drawable/ic_sync_badge_off_20dp.xml",
-  "java/res/drawable/ic_sync_error_40dp.xml",
-  "java/res/drawable/ic_sync_green_40dp.xml",
+  "java/res/drawable/ic_sync_error_48dp.xml",
+  "java/res/drawable/ic_sync_error_legacy_40dp.xml",
+  "java/res/drawable/ic_sync_green_legacy_40dp.xml",
+  "java/res/drawable/ic_sync_off_48dp.xml",
+  "java/res/drawable/ic_sync_on_48dp.xml",
   "java/res/drawable/ic_toolbar_share_offset_24dp.xml",
   "java/res/drawable/ic_tv_options_input_settings_rotated_grey.xml",
   "java/res/drawable/ic_videocam_24dp.xml",
@@ -809,6 +813,7 @@
   "java/res/layout/account_management_account_row.xml",
   "java/res/layout/account_picker_bottom_sheet_view.xml",
   "java/res/layout/account_picker_dialog_body.xml",
+  "java/res/layout/account_picker_incognito_row.xml",
   "java/res/layout/account_picker_new_account_row.xml",
   "java/res/layout/account_picker_new_account_row_legacy.xml",
   "java/res/layout/account_picker_row.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index a9a4b1e..a42b88b1 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1429,6 +1429,7 @@
   "java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerProperties.java",
   "java/src/org/chromium/chrome/browser/signin/account_picker/AddAccountRowViewBinder.java",
   "java/src/org/chromium/chrome/browser/signin/account_picker/ExistingAccountRowViewBinder.java",
+  "java/src/org/chromium/chrome/browser/signin/account_picker/IncognitoAccountRowViewBinder.java",
   "java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsClient.java",
   "java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsHelpClient.java",
   "java/src/org/chromium/chrome/browser/site_settings/ChromeWebappSettingsClient.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
index db3335d0..356cc30c 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
@@ -43,7 +43,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -470,7 +469,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "Flaky - https://crbug.com/1105146")
     public void interactingWithLocationBarHidesAutofillAssistant() {
         ArrayList<ActionProto> list = new ArrayList<>();
         list.add((ActionProto) ActionProto.newBuilder()
@@ -507,8 +505,39 @@
         onView(withId(org.chromium.chrome.R.id.url_bar))
                 .perform(click(), typeText(getURL(TEST_PAGE_B)), pressImeActionButton());
         waitUntilViewMatchesCondition(withText(containsString("Sorry")), isCompletelyDisplayed());
-        waitUntil(()
-                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
-                                  getURL(TEST_PAGE_B)));
+    }
+
+    @Test
+    @MediumTest
+    public void switchingBackToTabWithStoppedAutofillAssistantShowsErrorMessage() {
+        ArrayList<ActionProto> list = new ArrayList<>();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setTell(TellProto.newBuilder().setMessage("Shutdown"))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder().setStop(StopProto.newBuilder()).build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath(TEST_PAGE_A)
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                list);
+
+        int initialTabId =
+                TabModelUtils.getCurrentTabId(mTestRule.getActivity().getCurrentTabModel());
+
+        setupScripts(script);
+        startAutofillAssistantOnTab(TEST_PAGE_A);
+
+        waitUntilViewMatchesCondition(withText("Shutdown"), isCompletelyDisplayed());
+
+        ChromeTabUtils.fullyLoadUrlInNewTab(InstrumentationRegistry.getInstrumentation(),
+                mTestRule.getActivity(), getURL(TEST_PAGE_B), false);
+        waitUntilViewAssertionTrue(withText("Shutdown"), doesNotExist(), DEFAULT_MAX_TIME_TO_POLL);
+
+        ChromeTabUtils.closeCurrentTab(
+                InstrumentationRegistry.getInstrumentation(), mTestRule.getActivity());
+        waitUntilViewMatchesCondition(withText("Shutdown"), isCompletelyDisplayed());
     }
 }
diff --git a/chrome/android/java/res/drawable/ic_incognito_24dp.xml b/chrome/android/java/res/drawable/ic_incognito_24dp.xml
new file mode 100644
index 0000000..e13e148
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_incognito_24dp.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="21"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:name="path"
+        android:pathData="M 21 11 L 3 11 L 3 12 L 21 12 L 21 11 Z M 17.62 10 L 15.33 3.9 C 15.14 3.4 14.59 3.13 14.08 3.3 L 12 4 L 9.91 3.3 C 9.4 3.13 8.85 3.4 8.66 3.9 L 6.38 10 L 17.62 10 Z M 16.5 13 C 14.84 13 13.46 14.16 13.1 15.71 C 12.26 15.35 11.48 15.45 10.9 15.7 C 10.53 14.15 9.15 13 7.5 13 C 5.57 13 4 14.57 4 16.5 C 4 18.43 5.57 20 7.5 20 C 9.34 20 10.83 18.58 10.97 16.78 C 11.27 16.57 12.06 16.18 13.03 16.8 C 13.19 18.59 14.67 20 16.5 20 C 18.43 20 20 18.43 20 16.5 C 20 14.57 18.43 13 16.5 13 Z M 7.5 19 C 6.12 19 5 17.88 5 16.5 C 5 15.12 6.12 14 7.5 14 C 8.88 14 10 15.12 10 16.5 C 10 17.88 8.88 19 7.5 19 Z M 16.5 19 C 15.12 19 14 17.88 14 16.5 C 14 15.12 15.12 14 16.5 14 C 17.88 14 19 15.12 19 16.5 C 19 17.88 17.88 19 16.5 19 Z"
+        android:fillColor="@color/default_icon_color"
+        android:strokeWidth="1"/>
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_sync_error_48dp.xml b/chrome/android/java/res/drawable/ic_sync_error_48dp.xml
new file mode 100644
index 0000000..c812abbb
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_sync_error_48dp.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="40"
+    android:viewportHeight="40"
+    tools:targetApi="21">
+
+    <path
+        android:strokeWidth="1"
+        android:pathData="M20,30.5C25.799,30.5 30.5,25.799 30.5,20C30.5,14.201 25.799,9.5 20,9.5C14.201,9.5 9.5,14.201 9.5,20C9.5,25.799 14.201,30.5 20,30.5Z"
+        android:fillColor="@color/google_red_600"
+        android:strokeColor="@color/modern_white"/>
+    <path
+        android:pathData="M18.7501,16.4687V15.1625C18.2501,15.2938 17.7813,15.5 17.3563,15.7625L18.2688,16.675C18.4251,16.6 18.5813,16.525 18.7501,16.4687ZM14.2876,15.8812L15.7626,17.3562C15.2813,18.1188 15.0001,19.025 15.0001,20C15.0001,21.3813 15.5688,22.625 16.4751,23.525L15.0001,25H18.7501V21.25L17.3501,22.65C16.6751,21.9687 16.2501,21.0375 16.2501,20C16.2501,19.375 16.4063,18.7875 16.6751,18.2687L21.7251,23.3187C21.5688,23.4 21.4126,23.475 21.2438,23.5312V24.8375C21.7438,24.7062 22.2126,24.5 22.6376,24.2375L24.1126,25.7125L24.9063,24.9188L15.0876,15.0875L14.2876,15.8812ZM25.0001,15H21.2501V18.75L22.6501,17.35C23.3251,18.0313 23.7501,18.9625 23.7501,20C23.7501,20.625 23.5938,21.2125 23.3251,21.7313L24.2376,22.6437C24.7188,21.8812 25.0001,20.975 25.0001,20C25.0001,18.6187 24.4313,17.375 23.5251,16.475L25.0001,15Z"
+        android:fillColor="@color/modern_white"/>
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_sync_error_40dp.xml b/chrome/android/java/res/drawable/ic_sync_error_legacy_40dp.xml
similarity index 100%
rename from chrome/android/java/res/drawable/ic_sync_error_40dp.xml
rename to chrome/android/java/res/drawable/ic_sync_error_legacy_40dp.xml
diff --git a/chrome/android/java/res/drawable/ic_sync_green_40dp.xml b/chrome/android/java/res/drawable/ic_sync_green_legacy_40dp.xml
similarity index 100%
rename from chrome/android/java/res/drawable/ic_sync_green_40dp.xml
rename to chrome/android/java/res/drawable/ic_sync_green_legacy_40dp.xml
diff --git a/chrome/android/java/res/drawable/ic_sync_off_48dp.xml b/chrome/android/java/res/drawable/ic_sync_off_48dp.xml
new file mode 100644
index 0000000..e1461ca
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_sync_off_48dp.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="40"
+    android:viewportHeight="40"
+    tools:targetApi="21">
+
+    <path
+        android:strokeWidth="1"
+        android:pathData="M20,30.5C25.799,30.5 30.5,25.799 30.5,20C30.5,14.201 25.799,9.5 20,9.5C14.201,9.5 9.5,14.201 9.5,20C9.5,25.799 14.201,30.5 20,30.5Z"
+        android:fillColor="@color/modern_grey_500"
+        android:strokeColor="@color/modern_white"/>
+    <path
+        android:pathData="M18.7501,16.4687V15.1625C18.2501,15.2938 17.7813,15.5 17.3563,15.7625L18.2688,16.675C18.4251,16.6 18.5813,16.525 18.7501,16.4687ZM14.2876,15.8812L15.7626,17.3562C15.2813,18.1188 15.0001,19.025 15.0001,20C15.0001,21.3813 15.5688,22.625 16.4751,23.525L15.0001,25H18.7501V21.25L17.3501,22.65C16.6751,21.9687 16.2501,21.0375 16.2501,20C16.2501,19.375 16.4063,18.7875 16.6751,18.2687L21.7251,23.3187C21.5688,23.4 21.4126,23.475 21.2438,23.5312V24.8375C21.7438,24.7062 22.2126,24.5 22.6376,24.2375L24.1126,25.7125L24.9063,24.9188L15.0876,15.0875L14.2876,15.8812ZM25.0001,15H21.2501V18.75L22.6501,17.35C23.3251,18.0313 23.7501,18.9625 23.7501,20C23.7501,20.625 23.5938,21.2125 23.3251,21.7313L24.2376,22.6437C24.7188,21.8812 25.0001,20.975 25.0001,20C25.0001,18.6187 24.4313,17.375 23.5251,16.475L25.0001,15Z"
+        android:fillColor="@color/modern_white"/>
+</vector>
diff --git a/chrome/android/java/res/drawable/ic_sync_on_48dp.xml b/chrome/android/java/res/drawable/ic_sync_on_48dp.xml
new file mode 100644
index 0000000..232a62e
--- /dev/null
+++ b/chrome/android/java/res/drawable/ic_sync_on_48dp.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="40"
+    android:viewportHeight="40"
+    tools:targetApi="21">
+
+    <path
+        android:strokeWidth="1"
+        android:pathData="M20,30.5C25.799,30.5 30.5,25.799 30.5,20C30.5,14.201 25.799,9.5 20,9.5C14.201,9.5 9.5,14.201 9.5,20C9.5,25.799 14.201,30.5 20,30.5Z"
+        android:fillColor="@color/google_green_600"
+        android:strokeColor="@color/modern_white"/>
+    <path
+        android:pathData="M17.3482,22.6516C15.8854,21.1888 15.8854,18.8112 17.3482,17.3483C17.7946,16.902 18.3293,16.5882 18.895,16.4203L18.895,15.1298C18.0067,15.3287 17.1582,15.7706 16.4643,16.4644C14.5109,18.4178 14.5109,21.5821 16.4643,23.5355L15.1385,24.8613L18.674,24.8613L18.674,21.3258L17.3482,22.6516ZM23.5354,16.4644L24.8612,15.1386L21.3257,15.1386L21.3257,18.6742L22.6515,17.3483C24.1143,18.8112 24.1143,21.1888 22.6515,22.6516C22.2051,23.098 21.6704,23.4118 21.1047,23.5797L21.1047,24.8702C21.993,24.6713 22.8415,24.2294 23.5354,23.5355C25.4888,21.5821 25.4888,18.4178 23.5354,16.4644Z"
+        android:fillColor="@color/modern_white"
+        android:fillType="evenOdd"/>
+</vector>
diff --git a/chrome/android/java/res/layout/account_picker_incognito_row.xml b/chrome/android/java/res/layout/account_picker_incognito_row.xml
new file mode 100644
index 0000000..387ed2b
--- /dev/null
+++ b/chrome/android/java/res/layout/account_picker_incognito_row.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:padding="8dp">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="24dp"
+        android:gravity="center_vertical"
+        app:srcCompat="@drawable/ic_incognito_24dp"
+        tools:ignore="ContentDescription" />
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <TextView
+            android:text="@string/signin_incognito_mode_primary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
+        <TextView
+            android:text="@string/signin_incognito_mode_secondary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
index 7a6bd48..41176f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
@@ -45,8 +45,10 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -55,14 +57,15 @@
 import dagger.Reusable;
 
 /**
- * Used to verify postMessage origin for a designated package name.
+ * Use to check that an app has a Digital Asset Link relationship with the given origin.
  *
- * Uses Digital Asset Links to confirm that the given origin is associated with the package name as
- * a postMessage origin. It caches any origin that has been verified during the current application
- * lifecycle and reuses that without making any new network requests.
+ * Multiple instances of this object share a static cache, and as such the static
+ * {@link #wasPreviouslyVerified} can be used to check whether any verification has been carried
+ * out.
  *
- * The lifecycle of this object is governed by the owner. The owner has to call
- * {@link OriginVerifier#cleanUp()} for proper cleanup of dependencies.
+ * One instance of this object should be created per package, but {@link #start} may be called
+ * multiple times to verify different origins. This object has a native counterpart that will be
+ * kept alive as it is serving requests, but destroyed once all requests are finished.
  */
 @JNINamespace("customtabs")
 public class OriginVerifier {
@@ -75,8 +78,7 @@
     private final String mSignatureFingerprint;
     private final @Relation int mRelation;
     private long mNativeOriginVerifier;
-    @Nullable private OriginVerificationListener mListener;
-    private Origin mOrigin;
+    private final Map<Origin, Set<OriginVerificationListener>> mListeners = new HashMap<>();
     private long mVerificationStartTime;
     @Nullable
     private WebContents mWebContents;
@@ -106,17 +108,19 @@
 
     /** Small helper class to post a result of origin verification. */
     private class VerifiedCallback implements Runnable {
+        private final Origin mOrigin;
         private final boolean mResult;
         private final Boolean mOnline;
 
-        public VerifiedCallback(boolean result, Boolean online) {
+        public VerifiedCallback(Origin origin, boolean result, Boolean online) {
+            mOrigin = origin;
             mResult = result;
             mOnline = online;
         }
 
         @Override
         public void run() {
-            originVerified(mResult, mOnline);
+            originVerified(mOrigin, mResult, mOnline);
         }
     }
 
@@ -238,51 +242,64 @@
      */
     public void start(@NonNull OriginVerificationListener listener, @NonNull Origin origin) {
         ThreadUtils.assertOnUiThread();
-        mOrigin = origin;
-        mListener = listener;
+        if (mListeners.containsKey(origin)) {
+            // We already have an ongoing verification for that origin, just add the listener.
+            mListeners.get(origin).add(listener);
+            return;
+        } else {
+            mListeners.put(origin, new HashSet<>());
+            mListeners.get(origin).add(listener);
+        }
 
         // Website to app Digital Asset Link verification can be skipped for a specific URL by
         // passing a command line flag to ease development.
         String disableDalUrl = CommandLine.getInstance().getSwitchValue(
                 ChromeSwitches.DISABLE_DIGITAL_ASSET_LINK_VERIFICATION);
-        if (!TextUtils.isEmpty(disableDalUrl)
-                && mOrigin.equals(Origin.create(disableDalUrl))) {
+        if (!TextUtils.isEmpty(disableDalUrl) && origin.equals(Origin.create(disableDalUrl))) {
             Log.i(TAG, "Verification skipped for %s due to command line flag.", origin);
-            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, new VerifiedCallback(true, null));
+            PostTask.runOrPostTask(
+                    UiThreadTaskTraits.DEFAULT, new VerifiedCallback(origin, true, null));
             return;
         }
 
-        String scheme = mOrigin.uri().getScheme();
+        String scheme = origin.uri().getScheme();
         if (TextUtils.isEmpty(scheme)
                 || !UrlConstants.HTTPS_SCHEME.equals(scheme.toLowerCase(Locale.US))) {
             Log.i(TAG, "Verification failed for %s as not https.", origin);
             BrowserServicesMetrics.recordVerificationResult(
                     BrowserServicesMetrics.VerificationResult.HTTPS_FAILURE);
-            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, new VerifiedCallback(false, null));
+            PostTask.runOrPostTask(
+                    UiThreadTaskTraits.DEFAULT, new VerifiedCallback(origin, false, null));
             return;
         }
 
-        if (shouldOverrideVerification(mPackageName, mOrigin, mRelation)) {
+        if (shouldOverrideVerification(mPackageName, origin, mRelation)) {
             Log.i(TAG, "Verification succeeded for %s, it was overridden.", origin);
-            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, new VerifiedCallback(true, null));
+            PostTask.runOrPostTask(
+                    UiThreadTaskTraits.DEFAULT, new VerifiedCallback(origin, true, null));
             return;
         }
 
-        if (isAllowlisted(mPackageName, mOrigin, mRelation)) {
+        if (isAllowlisted(mPackageName, origin, mRelation)) {
             Log.i(TAG, "Verification succeeded for %s, %s, it was allowlisted.", mPackageName,
-                    mOrigin);
-            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, new VerifiedCallback(true, null));
+                    origin);
+            PostTask.runOrPostTask(
+                    UiThreadTaskTraits.DEFAULT, new VerifiedCallback(origin, true, null));
             return;
         }
 
-        if (mNativeOriginVerifier != 0) cleanUp();
         // Early return for testing without native.
         if (!BrowserStartupController.getInstance().isFullBrowserStarted()) return;
 
         if (mWebContents != null && mWebContents.isDestroyed()) mWebContents = null;
-        mNativeOriginVerifier = OriginVerifierJni.get().init(
-                OriginVerifier.this, mWebContents, Profile.getLastUsedRegularProfile());
-        assert mNativeOriginVerifier != 0;
+
+        // If the native side doesn't exist, create it.
+        if (mNativeOriginVerifier == 0) {
+            mNativeOriginVerifier = OriginVerifierJni.get().init(
+                    OriginVerifier.this, mWebContents, Profile.getLastUsedRegularProfile());
+            assert mNativeOriginVerifier != 0;
+        }
+
         String relationship = null;
         switch (mRelation) {
             case CustomTabsService.RELATION_USE_AS_ORIGIN:
@@ -299,21 +316,15 @@
         mVerificationStartTime = SystemClock.uptimeMillis();
         boolean requestSent =
                 OriginVerifierJni.get().verifyOrigin(mNativeOriginVerifier, OriginVerifier.this,
-                        mPackageName, mSignatureFingerprint, mOrigin.toString(), relationship);
+                        mPackageName, mSignatureFingerprint, origin.toString(), relationship);
         if (!requestSent) {
             BrowserServicesMetrics.recordVerificationResult(
                     BrowserServicesMetrics.VerificationResult.REQUEST_FAILURE);
-            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, new VerifiedCallback(false, false));
+            PostTask.runOrPostTask(
+                    UiThreadTaskTraits.DEFAULT, new VerifiedCallback(origin, false, false));
         }
     }
 
-    /**
-     * Removes the verification listener, but finishes the ongoing verification process, if any.
-     */
-    public void removeListener() {
-        mListener = null;
-    }
-
     private static boolean shouldOverrideVerification(String packageName, Origin origin,
             int relation) {
         if (sVerificationOverrides.get() == null) return false;
@@ -334,6 +345,8 @@
      * Cleanup native dependencies on this object.
      */
     public void cleanUp() {
+        // Only destroy native once we have no other pending verifications.
+        if (!mListeners.isEmpty()) return;
         if (mNativeOriginVerifier == 0) return;
         OriginVerifierJni.get().destroy(mNativeOriginVerifier, OriginVerifier.this);
         mNativeOriginVerifier = 0;
@@ -396,21 +409,22 @@
 
     /** Called asynchronously by OriginVerifierJni.get().verifyOrigin. */
     @CalledByNative
-    private void onOriginVerificationResult(int result) {
+    private void onOriginVerificationResult(String originAsString, int result) {
+        Origin origin = Origin.createOrThrow(originAsString);
         switch (result) {
             case RelationshipCheckResult.SUCCESS:
                 BrowserServicesMetrics.recordVerificationResult(
                         BrowserServicesMetrics.VerificationResult.ONLINE_SUCCESS);
-                originVerified(true, true);
+                originVerified(origin, true, true);
                 break;
             case RelationshipCheckResult.FAILURE:
                 BrowserServicesMetrics.recordVerificationResult(
                         BrowserServicesMetrics.VerificationResult.ONLINE_FAILURE);
-                originVerified(false, true);
+                originVerified(origin, false, true);
                 break;
             case RelationshipCheckResult.NO_CONNECTION:
                 Log.i(TAG, "Device is offline, checking saved verification result.");
-                checkForSavedResult();
+                checkForSavedResult(origin);
                 break;
             default:
                 assert false;
@@ -418,20 +432,23 @@
     }
 
     /** Deal with the result of an Origin check. Will be called on UI Thread. */
-    private void originVerified(boolean originVerified, Boolean online) {
-        Log.i(TAG, "Verification %s.", (originVerified ? "succeeded" : "failed"));
+    private void originVerified(Origin origin, boolean originVerified, Boolean online) {
         if (originVerified) {
-            Log.d(TAG, "Adding: %s for %s", mPackageName, mOrigin);
-            VerificationResultStore.addRelationship(new Relationship(mPackageName,
-                    mSignatureFingerprint, mOrigin, mRelation));
+            Log.d(TAG, "Adding: %s for %s", mPackageName, origin);
+            VerificationResultStore.addRelationship(
+                    new Relationship(mPackageName, mSignatureFingerprint, origin, mRelation));
         }
 
         // We save the result even if there is a failure as a way of overwriting a previously
         // successfully verified result that fails on a subsequent check.
-        saveVerificationResult(originVerified);
+        saveVerificationResult(origin, originVerified);
 
-        if (mListener != null) {
-            mListener.onOriginVerified(mPackageName, mOrigin, originVerified, online);
+        if (mListeners.containsKey(origin)) {
+            Set<OriginVerificationListener> listeners = mListeners.get(origin);
+            for (OriginVerificationListener listener : listeners) {
+                listener.onOriginVerified(mPackageName, origin, originVerified, online);
+            }
+            mListeners.remove(origin);
         }
 
         if (online != null) {
@@ -445,9 +462,9 @@
     /**
      * Saves the result of a verification to Preferences so we can reuse it when offline.
      */
-    private void saveVerificationResult(boolean originVerified) {
+    private void saveVerificationResult(Origin origin, boolean originVerified) {
         Relationship relationship =
-                new Relationship(mPackageName, mSignatureFingerprint, mOrigin, mRelation);
+                new Relationship(mPackageName, mSignatureFingerprint, origin, mRelation);
         if (originVerified) {
             VerificationResultStore.addRelationship(relationship);
         } else {
@@ -458,16 +475,16 @@
     /**
      * Checks for a previously saved verification result.
      */
-    private void checkForSavedResult() {
+    private void checkForSavedResult(Origin origin) {
         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
             boolean verified = VerificationResultStore.isRelationshipSaved(
-                    new Relationship(mPackageName, mSignatureFingerprint, mOrigin, mRelation));
+                    new Relationship(mPackageName, mSignatureFingerprint, origin, mRelation));
 
             BrowserServicesMetrics.recordVerificationResult(verified
                             ? BrowserServicesMetrics.VerificationResult.OFFLINE_SUCCESS
                             : BrowserServicesMetrics.VerificationResult.OFFLINE_FAILURE);
 
-            originVerified(verified, false);
+            originVerified(origin, verified, false);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java
index 0a337ef..cb20c75 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java
@@ -44,6 +44,7 @@
      */
     @Nullable
     private Set<Origin> mPendingOrigins;
+    private boolean mDestroyed;
 
     /**
      * All the origins that have been successfully verified.
@@ -69,8 +70,7 @@
 
     @Override
     public void destroy() {
-        // Verification may finish after activity is destroyed.
-        mOriginVerifier.removeListener();
+        mDestroyed = true;
     }
 
     @Override
@@ -81,6 +81,8 @@
         Promise<Boolean> promise = new Promise<>();
         if (getPendingOrigins().contains(origin)) {
             mOriginVerifier.start((packageName, unused, verified, online) -> {
+                if (mDestroyed) return;
+
                 getPendingOrigins().remove(origin);
                 if (verified) mVerifiedOrigins.add(origin);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
index 6b2f066..9cc3b52 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SyncErrorInfoBar.java
@@ -106,7 +106,7 @@
 
     private SyncErrorInfoBar(@SyncErrorInfoBarType int type, String title, String detailsMessage,
             String primaryButtonText) {
-        super(R.drawable.ic_sync_error_40dp, R.color.default_red, null, title, null,
+        super(R.drawable.ic_sync_error_legacy_40dp, R.color.default_red, null, title, null,
                 primaryButtonText, null);
         mType = type;
         mDetailsMessage = detailsMessage;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
index 70db4b5..c95fd35 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
@@ -65,11 +65,14 @@
         public final String latestVersion;
         public final String downloadUrl;
         public final int serverDate;
+        public final String updateStatus;
 
-        protected VersionConfig(String latestVersion, String downloadUrl, int serverDate) {
+        protected VersionConfig(
+                String latestVersion, String downloadUrl, int serverDate, String updateStatus) {
             this.latestVersion = latestVersion;
             this.downloadUrl = downloadUrl;
             this.serverDate = serverDate;
+            this.updateStatus = updateStatus;
         }
     }
 
@@ -204,6 +207,11 @@
                     ? UpdateStatus.OFFLINE
                     : UpdateStatus.FAILED;
         }
+        // If the version matches exactly, the Omaha server will return status="noupdate" without
+        // providing the latest version number.
+        if (versionConfig.updateStatus != null && versionConfig.updateStatus.equals("noupdate")) {
+            return UpdateStatus.UPDATED;
+        }
         Log.i(TAG,
                 "OmahaBase::checkForUpdates(): Received latest version String from Omaha "
                         + "server: \"" + versionConfig.latestVersion + "\"");
@@ -642,6 +650,8 @@
     static VersionConfig getVersionConfig(SharedPreferences sharedPref) {
         return new VersionConfig(sharedPref.getString(OmahaBase.PREF_LATEST_VERSION, ""),
                 sharedPref.getString(OmahaBase.PREF_MARKET_URL, ""),
-                sharedPref.getInt(OmahaBase.PREF_SERVER_DATE, -2));
+                sharedPref.getInt(OmahaBase.PREF_SERVER_DATE, -2),
+                // updateStatus is only used for the on-demand check.
+                null);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
index 081a3582..48701a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
@@ -91,7 +91,7 @@
         XMLParser parser = new XMLParser(xml);
         Node rootNode = parser.getRootNode();
         parseRootNode(rootNode);
-        return new VersionConfig(getNewVersion(), getURL(), getDaystartDays());
+        return new VersionConfig(getNewVersion(), getURL(), getDaystartDays(), getUpdateStatus());
     }
 
     public int getDaystartSeconds() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index c7a580f..e86c873 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -20,6 +20,7 @@
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.ActivityState;
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.IntentUtils;
@@ -576,9 +577,10 @@
         }
 
         // When invoked directly from a browser, we want to trigger switch to tab animation.
-        // If invoded from other activitiies, ex. searchActivity, we do not need to trigger the
+        // If invoked from other activities, ex. searchActivity, we do not need to trigger the
         // animation since Android will show the animation for switching apps.
-        if (mWindowAndroid.equals(tab.getWindowAndroid())) {
+        if (tab.getWindowAndroid().getActivityState() != ActivityState.STOPPED
+                && tab.getWindowAndroid().getActivityState() != ActivityState.DESTROYED) {
             // TODO(1097292):  Do not use Activity to get TabModelSelector.
             assert tab.getWindowAndroid().getActivity().get() instanceof ChromeActivity;
 
@@ -589,13 +591,13 @@
             chromeActivity.getTabModelSelector().getCurrentModel().setIndex(
                     tabIndex, TabSelectionType.FROM_OMNIBOX);
         } else {
+            // Browser is in background, bring to to foreground and switch to the tab.
             Intent newIntent = ChromeIntentUtil.createBringTabToFrontIntent(tab.getId());
             if (newIntent != null) {
                 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 IntentUtils.safeStartActivity(ContextUtils.getApplicationContext(), newIntent);
             }
         }
-
         recordMetrics(position, WindowOpenDisposition.SWITCH_TO_TAB, suggestion);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
index d9bc9ea..54edf38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
@@ -521,6 +521,9 @@
     }
 
     @Override
+    public void goIncognitoMode() {}
+
+    @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == ADD_ACCOUNT_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
             if (data == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
index ac18730..a8e206b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
@@ -71,6 +71,14 @@
         mAccountPickerDelegate.addAccount();
     }
 
+    /**
+     * Notifies when the user clicked the "Go Incognito mode" button.
+     */
+    @Override
+    public void goIncognitoMode() {
+        mAccountPickerDelegate.goIncognitoMode();
+    }
+
     PropertyModel getModel() {
         return mModel;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerCoordinator.java
index 0979f2a..38c0725 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerCoordinator.java
@@ -8,6 +8,7 @@
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.ItemType;
 import org.chromium.ui.modelutil.MVCListAdapter;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
@@ -34,6 +35,11 @@
          * Notifies when the user clicked the "add account" button.
          */
         void addAccount();
+
+        /**
+         * Notifies when the user clicked the "Go incognito mode" button.
+         */
+        void goIncognitoMode();
     }
 
     private final AccountPickerMediator mMediator;
@@ -59,8 +65,13 @@
         adapter.registerType(ItemType.EXISTING_ACCOUNT_ROW, ExistingAccountRowViewBinder::buildView,
                 ExistingAccountRowViewBinder::bindView);
 
-        view.setAdapter(adapter);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)) {
+            adapter.registerType(ItemType.INCOGNITO_ACCOUNT_ROW,
+                    IncognitoAccountRowViewBinder::buildView,
+                    IncognitoAccountRowViewBinder::bindView);
+        }
 
+        view.setAdapter(adapter);
         mMediator = new AccountPickerMediator(
                 view.getContext(), listModel, listener, selectedAccountName);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerDelegate.java
index 970a460..1a7a699 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerDelegate.java
@@ -84,4 +84,9 @@
                     }
                 });
     }
+
+    /**
+     * Notifies when the user clicked the "Go incognito mode" button.
+     */
+    public void goIncognitoMode() {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediator.java
index 4362403..f7ef6ac9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediator.java
@@ -12,10 +12,12 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.signin.DisplayableProfileData;
 import org.chromium.chrome.browser.signin.ProfileDataCache;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.AddAccountRowProperties;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.ExistingAccountRowProperties;
+import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.IncognitoAccountRowProperties;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.ItemType;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
@@ -96,6 +98,14 @@
         PropertyModel model =
                 AddAccountRowProperties.createModel(mAccountPickerListener::addAccount);
         mListModel.add(new MVCListAdapter.ListItem(ItemType.ADD_ACCOUNT_ROW, model));
+
+        // Add a "Go incognito mode" row
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)) {
+            PropertyModel incognitoModel = IncognitoAccountRowProperties.createModel(
+                    mAccountPickerListener::goIncognitoMode);
+            mListModel.add(
+                    new MVCListAdapter.ListItem(ItemType.INCOGNITO_ACCOUNT_ROW, incognitoModel));
+        }
     }
 
     private MVCListAdapter.ListItem createExistingAccountRowItem(
@@ -109,11 +119,13 @@
     }
 
     private void updateSelectedAccount() {
-        for (int i = 0; i < mListModel.size() - 1; ++i) {
-            PropertyModel model = mListModel.get(i).model;
-            boolean isSelectedAccount = TextUtils.equals(mSelectedAccountName,
-                    model.get(ExistingAccountRowProperties.PROFILE_DATA).getAccountName());
-            model.set(ExistingAccountRowProperties.IS_SELECTED_ACCOUNT, isSelectedAccount);
+        for (MVCListAdapter.ListItem item : mListModel) {
+            if (item.type == AccountPickerProperties.ItemType.EXISTING_ACCOUNT_ROW) {
+                PropertyModel model = item.model;
+                boolean isSelectedAccount = TextUtils.equals(mSelectedAccountName,
+                        model.get(ExistingAccountRowProperties.PROFILE_DATA).getAccountName());
+                model.set(ExistingAccountRowProperties.IS_SELECTED_ACCOUNT, isSelectedAccount);
+            }
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerProperties.java
index 472ffdb..f37423b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerProperties.java
@@ -39,6 +39,24 @@
     }
 
     /**
+     * Properties for "incognito account" row in account picker.
+     */
+    static class IncognitoAccountRowProperties {
+        static final PropertyModel.ReadableObjectPropertyKey<Runnable> ON_CLICK_LISTENER =
+                new PropertyModel.ReadableObjectPropertyKey<>("on_click_listener");
+
+        static final PropertyKey[] ALL_KEYS = new PropertyKey[] {ON_CLICK_LISTENER};
+
+        private IncognitoAccountRowProperties() {}
+
+        static PropertyModel createModel(Runnable runnableIncognitoAccount) {
+            return new PropertyModel.Builder(ALL_KEYS)
+                    .with(ON_CLICK_LISTENER, runnableIncognitoAccount)
+                    .build();
+        }
+    }
+
+    /**
      * Properties for account row in account picker.
      */
     static class ExistingAccountRowProperties {
@@ -68,7 +86,8 @@
     /**
      * Item types of account picker.
      */
-    @IntDef({ItemType.EXISTING_ACCOUNT_ROW, ItemType.ADD_ACCOUNT_ROW})
+    @IntDef({ItemType.EXISTING_ACCOUNT_ROW, ItemType.ADD_ACCOUNT_ROW,
+            ItemType.INCOGNITO_ACCOUNT_ROW})
     @Retention(RetentionPolicy.SOURCE)
     @interface ItemType {
         /**
@@ -82,5 +101,11 @@
          * use {@link AddAccountRowViewBinder} for view setup.
          */
         int ADD_ACCOUNT_ROW = 2;
+
+        /**
+         * Item type for models created with {@link IncognitoAccountRowProperties#createModel} and
+         * use {@link IncognitoAccountRowViewBinder} for view setup.
+         */
+        int INCOGNITO_ACCOUNT_ROW = 3;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/IncognitoAccountRowViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/IncognitoAccountRowViewBinder.java
new file mode 100644
index 0000000..c97c8e6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/IncognitoAccountRowViewBinder.java
@@ -0,0 +1,40 @@
+// 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.chrome.browser.signin.account_picker;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.IncognitoAccountRowProperties;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * This class regroups the buildView and bindView util methods of the
+ * incognito account row.
+ */
+class IncognitoAccountRowViewBinder {
+    private IncognitoAccountRowViewBinder() {}
+
+    static View buildView(ViewGroup parent) {
+        @LayoutRes
+        int layoutRes = R.layout.account_picker_incognito_row;
+        return LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false);
+    }
+
+    static void bindView(PropertyModel model, View view, PropertyKey propertyKey) {
+        if (propertyKey == IncognitoAccountRowProperties.ON_CLICK_LISTENER) {
+            view.setOnClickListener(
+                    v -> model.get(IncognitoAccountRowProperties.ON_CLICK_LISTENER).run());
+        } else {
+            throw new IllegalArgumentException(
+                    "Cannot update the view for propertyKey: " + propertyKey);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/AndroidSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/AndroidSyncSettings.java
index 2702c5d..bba285e86 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/AndroidSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/AndroidSyncSettings.java
@@ -21,8 +21,12 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
-import org.chromium.components.signin.ChromeSigninController;
+import org.chromium.components.signin.base.CoreAccountInfo;
+import org.chromium.components.signin.identitymanager.ConsentLevel;
+import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.sync.SyncContentResolverDelegate;
 import org.chromium.components.sync.SystemSyncContentResolverDelegate;
 
@@ -89,21 +93,22 @@
      */
     @VisibleForTesting
     public AndroidSyncSettings(SyncContentResolverDelegate syncContentResolverDelegate) {
-        this(syncContentResolverDelegate, null);
+        this(syncContentResolverDelegate, null, getSyncAccount());
     }
 
     /**
      * @param syncContentResolverDelegate an implementation of {@link SyncContentResolverDelegate}.
      * @param callback Callback that will be called after updating account is finished. Boolean
      *                 passed to the callback indicates whether syncability was changed.
+     * @param account The sync account if sync is enabled, null otherwise.
      */
     @VisibleForTesting
     public AndroidSyncSettings(SyncContentResolverDelegate syncContentResolverDelegate,
-            @Nullable Callback<Boolean> callback) {
+            @Nullable Callback<Boolean> callback, @Nullable Account account) {
         mContractAuthority = ContextUtils.getApplicationContext().getPackageName();
         mSyncContentResolverDelegate = syncContentResolverDelegate;
 
-        mAccount = ChromeSigninController.get().getSignedInUser();
+        mAccount = account;
         updateSyncability(callback);
         updateCachedSettings();
 
@@ -324,4 +329,14 @@
             observer.androidSyncSettingsChanged();
         }
     }
+
+    /**
+     * Returns the sync account in the last used regular profile.
+     */
+    private static @Nullable Account getSyncAccount() {
+        IdentityManager identityManager = IdentityServicesProvider.get().getIdentityManager(
+                Profile.getLastUsedRegularProfile());
+        return CoreAccountInfo.getAndroidAccountFrom(
+                identityManager.getPrimaryAccountInfo(ConsentLevel.SYNC));
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
index 632c82b..4240e3e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
@@ -192,7 +192,7 @@
         mSyncCategory = (PreferenceCategory) findPreference(PREF_SYNC_CATEGORY);
         mSyncErrorCard = findPreference(PREF_SYNC_ERROR_CARD);
         mSyncErrorCard.setIcon(UiUtils.getTintedDrawable(
-                getActivity(), R.drawable.ic_sync_error_40dp, R.color.default_red));
+                getActivity(), R.drawable.ic_sync_error_legacy_40dp, R.color.default_red));
         mSyncErrorCard.setOnPreferenceClickListener(
                 SyncSettingsUtils.toOnClickListener(this, this::onSyncErrorCardClicked));
         mSyncDisabledByAdministrator = findPreference(PREF_SYNC_DISABLED_BY_ADMINISTRATOR);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java
index fa8b0f1..b3d7966 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java
@@ -14,6 +14,7 @@
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
 import androidx.browser.customtabs.CustomTabsIntent;
 import androidx.fragment.app.Fragment;
 import androidx.preference.Preference;
@@ -29,6 +30,7 @@
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider.CustomTabsUiType;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.AndroidSyncSettings;
@@ -220,33 +222,38 @@
             return null;
         }
 
+        boolean useNewIcon =
+                ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY);
         ProfileSyncService profileSyncService = ProfileSyncService.get();
         if (profileSyncService == null || !AndroidSyncSettings.get().isSyncEnabled()) {
-            return UiUtils.getTintedDrawable(
-                    context, R.drawable.ic_sync_green_40dp, R.color.default_icon_color);
+            return useNewIcon
+                    ? AppCompatResources.getDrawable(context, R.drawable.ic_sync_off_48dp)
+                    : UiUtils.getTintedDrawable(context, R.drawable.ic_sync_green_legacy_40dp,
+                            R.color.default_icon_color);
         }
 
         if (profileSyncService.isSyncDisabledByEnterprisePolicy()) {
-            return UiUtils.getTintedDrawable(
-                    context, R.drawable.ic_sync_error_40dp, R.color.default_icon_color);
-        }
-
-        if (!profileSyncService.isFirstSetupComplete()) {
-            return UiUtils.getTintedDrawable(
-                    context, R.drawable.ic_sync_error_40dp, R.color.default_red);
+            return useNewIcon
+                    ? AppCompatResources.getDrawable(context, R.drawable.ic_sync_off_48dp)
+                    : UiUtils.getTintedDrawable(context, R.drawable.ic_sync_error_legacy_40dp,
+                            R.color.default_icon_color);
         }
 
         if (profileSyncService.isEngineInitialized()
                 && (profileSyncService.hasUnrecoverableError()
                         || profileSyncService.getAuthError() != GoogleServiceAuthError.State.NONE
                         || profileSyncService.isPassphraseRequiredForPreferredDataTypes()
-                        || profileSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes())) {
-            return UiUtils.getTintedDrawable(
-                    context, R.drawable.ic_sync_error_40dp, R.color.default_red);
+                        || profileSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()
+                        || !profileSyncService.isFirstSetupComplete())) {
+            return useNewIcon
+                    ? AppCompatResources.getDrawable(context, R.drawable.ic_sync_error_48dp)
+                    : UiUtils.getTintedDrawable(
+                            context, R.drawable.ic_sync_error_legacy_40dp, R.color.default_red);
         }
 
-        return UiUtils.getTintedDrawable(
-                context, R.drawable.ic_sync_green_40dp, R.color.default_green);
+        return useNewIcon ? AppCompatResources.getDrawable(context, R.drawable.ic_sync_on_48dp)
+                          : UiUtils.getTintedDrawable(context, R.drawable.ic_sync_green_legacy_40dp,
+                                  R.color.default_green);
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
index d4571b6..0e0b0ce 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
@@ -18,6 +18,7 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
@@ -201,6 +202,7 @@
     @LargeTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
+    @DisabledTest(message = "crbug.com/1107395")
     public void testSelectWithManyTabs() throws Exception {
         ChromeTabUtils.newTabsFromMenu(
                 InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity(), 4);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java
index c87a762..9cd418b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java
@@ -8,11 +8,11 @@
 import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
 import android.content.Intent;
-import android.os.Build;
 import android.support.test.InstrumentationRegistry;
-import android.util.Pair;
+import android.text.TextUtils;
 import android.view.ViewGroup;
 import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.test.filters.MediumTest;
 
@@ -24,8 +24,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.ActivityState;
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -42,6 +43,7 @@
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.CriteriaNotSatisfiedException;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.net.test.EmbeddedTestServer;
@@ -90,46 +92,112 @@
     }
 
     /**
-     * Type the |text| into |activity|'s url_bar, and wait for switch to tab suggestion shows up.
+     * Type the |text| into |activity|'s URL bar, and wait for switch to tab suggestion shows up.
      *
-     * @param activity The Activity which url_bar is in.
+     * @param activity The Activity which URL bar is in.
      * @param locationBarLayout The layout which omnibox suggestions will show in.
-     * @param text The text will be typed into url_bar.
+     * @param tab The tab will be switched to.
      */
-    private void typeAndWaitForTabMatchSuggestions(Activity activity,
-            LocationBarLayout locationBarLayout, String input) throws InterruptedException {
-        typeInOmnibox(activity, input);
+    private void typeAndClickMatchingTabMatchSuggestion(Activity activity,
+            LocationBarLayout locationBarLayout, Tab tab) throws InterruptedException {
+        typeInOmnibox(activity, tab.getTitle());
 
         OmniboxTestUtils.waitForOmniboxSuggestions(locationBarLayout);
         // waitForOmniboxSuggestions only wait until one suggestion shows up, we need to wait util
         // autocomplete return more suggestions.
         CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat(findFirstTabMatchOmniboxSuggestion(locationBarLayout).first,
-                    Matchers.not(INVALID_INDEX));
+            OmniboxSuggestion matchSuggestion =
+                    findTabMatchOmniboxSuggestion(locationBarLayout, tab);
+            Criteria.checkThat(matchSuggestion, Matchers.notNullValue());
+
+            OmniboxSuggestionsDropdown suggestionsDropdown =
+                    AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
+                            locationBarLayout.getAutocompleteCoordinator());
+
+            // Make sure data populated to UI
+            int index = findIndexOfTabMatchSuggestionView(suggestionsDropdown, matchSuggestion);
+            Criteria.checkThat(index, Matchers.not(INVALID_INDEX));
+
+            try {
+                clickSuggestionActionAt(suggestionsDropdown, index);
+            } catch (InterruptedException e) {
+                throw new CriteriaNotSatisfiedException(e);
+            }
         });
     }
 
     /**
-     * Find the first switch to tab suggestion in the omnibox suggestion list, and return the
-     * suggestion and its index.
+     * Find the switch to tab suggestion which suggests the |tab|, and return the
+     * suggestion. This method needs to run on the UI thread.
      *
      * @param locationBarLayout The layout which omnibox suggestions will show in.
-     * @return The the first switch to tab suggestion's index, and the suggesstion.
+     * @param tab The tab which the OmniboxSuggestion should suggest.
+     * @return The suggesstion which suggests the |tab|.
      */
-    private Pair<Integer, OmniboxSuggestion> findFirstTabMatchOmniboxSuggestion(
-            LocationBarLayout locationBarLayout) {
-        OmniboxSuggestionsDropdown suggestionsDropdown =
-                AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
-                        locationBarLayout.getAutocompleteCoordinator());
-        // Find the index of the first matching suggestion.
-        for (int i = 0; i < suggestionsDropdown.getItemCount(); ++i) {
-            OmniboxSuggestion suggestion = AutocompleteCoordinatorTestUtils.getOmniboxSuggestionAt(
-                    locationBarLayout.getAutocompleteCoordinator(), i);
-            if (suggestion != null && suggestion.hasTabMatch()) {
-                return new Pair<Integer, OmniboxSuggestion>(i, suggestion);
+    private OmniboxSuggestion findTabMatchOmniboxSuggestion(
+            LocationBarLayout locationBarLayout, Tab tab) {
+        ThreadUtils.assertOnUiThread();
+
+        AutocompleteCoordinator coordinator = locationBarLayout.getAutocompleteCoordinator();
+        // Find the first matching suggestion.
+        for (int i = 0; i < coordinator.getSuggestionCount(); ++i) {
+            OmniboxSuggestion suggestion = coordinator.getSuggestionAt(i);
+            if (suggestion != null && suggestion.hasTabMatch()
+                    && TextUtils.equals(suggestion.getDescription(), tab.getTitle())
+                    && TextUtils.equals(suggestion.getUrl().getSpec(), tab.getUrl().getSpec())) {
+                return suggestion;
             }
         }
-        return new Pair<Integer, OmniboxSuggestion>(INVALID_INDEX, null);
+        return null;
+    }
+
+    /**
+     * Find the index of the tab match suggestion in OmniboxSuggestionsDropdown. This method needs
+     * to run on the UI thread.
+     *
+     * @param suggestionsDropdown The OmniboxSuggestionsDropdown contains all the suggestions.
+     * @param suggestion The OmniboxSuggestion we are looking for in the view.
+     * @return The matching suggestion's index.
+     */
+    private int findIndexOfTabMatchSuggestionView(
+            OmniboxSuggestionsDropdown suggestionsDropdown, OmniboxSuggestion suggestion) {
+        ThreadUtils.assertOnUiThread();
+
+        ViewGroup viewGroup = suggestionsDropdown.getViewGroup();
+        if (viewGroup == null) {
+            return INVALID_INDEX;
+        }
+
+        for (int i = 0; i < viewGroup.getChildCount(); i++) {
+            BaseSuggestionView baseSuggestionView = null;
+            try {
+                baseSuggestionView = (BaseSuggestionView) viewGroup.getChildAt(i);
+            } catch (ClassCastException e) {
+                continue;
+            }
+
+            if (baseSuggestionView == null) {
+                continue;
+            }
+
+            TextView line1 = baseSuggestionView.findViewById(R.id.line_1);
+            TextView line2 = baseSuggestionView.findViewById(R.id.line_2);
+            if (line1 == null || line2 == null
+                    || !TextUtils.equals(suggestion.getDescription(), line1.getText())
+                    || !TextUtils.equals(suggestion.getDisplayText(), line2.getText())) {
+                continue;
+            }
+
+            List<ImageView> buttonsList = baseSuggestionView.getActionButtons();
+            if (buttonsList != null && buttonsList.size() == 1
+                    && TextUtils.equals(baseSuggestionView.getResources().getString(
+                                                R.string.accessibility_omnibox_switch_to_tab),
+                            buttonsList.get(0).getContentDescription())) {
+                return i;
+            }
+        }
+
+        return INVALID_INDEX;
     }
 
     /**
@@ -140,10 +208,12 @@
      */
     private void clickSuggestionActionAt(OmniboxSuggestionsDropdown suggestionsDropdown, int index)
             throws InterruptedException {
-        // Wait a bit since the button may not able to click.
         ViewGroup viewGroup = suggestionsDropdown.getViewGroup();
         BaseSuggestionView baseSuggestionView = (BaseSuggestionView) viewGroup.getChildAt(index);
+        Assert.assertNotNull("Null suggestion for index: " + index, baseSuggestionView);
+
         List<ImageView> buttonsList = baseSuggestionView.getActionButtons();
+        Assert.assertNotNull(buttonsList);
         Assert.assertEquals(buttonsList.size(), 1);
         TestTouchUtils.performClickOnMainSync(
                 InstrumentationRegistry.getInstrumentation(), buttonsList.get(0));
@@ -171,9 +241,6 @@
 
     @Test
     @MediumTest
-    @DisableIf.Build(message = "https://crbug.com/1101433",
-            sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP_MR1,
-            sdk_is_less_than = Build.VERSION_CODES.N)
     @EnableFeatures("OmniboxTabSwitchSuggestions")
     public void
     testSwitchToTabSuggestion() throws InterruptedException {
@@ -183,34 +250,23 @@
         final String testHttpsUrl1 = mTestServer.getURL("/chrome/test/data/android/about.html");
         final String testHttpsUrl2 = mTestServer.getURL("/chrome/test/data/android/ok.txt");
         final String testHttpsUrl3 = mTestServer.getURL("/chrome/test/data/android/test.html");
-        mActivityTestRule.loadUrlInNewTab(testHttpsUrl1);
+        final Tab aboutTab = mActivityTestRule.loadUrlInNewTab(testHttpsUrl1);
         mActivityTestRule.loadUrlInNewTab(testHttpsUrl2);
         mActivityTestRule.loadUrlInNewTab(testHttpsUrl3);
 
         LocationBarLayout locationBarLayout =
                 (LocationBarLayout) mActivityTestRule.getActivity().findViewById(R.id.location_bar);
-        typeAndWaitForTabMatchSuggestions(
-                mActivityTestRule.getActivity(), locationBarLayout, "about");
-
-        Pair<Integer, OmniboxSuggestion> matchSuggestion =
-                findFirstTabMatchOmniboxSuggestion(locationBarLayout);
-
-        Assert.assertNotEquals(INVALID_INDEX, (int) matchSuggestion.first);
-        Assert.assertNotNull("No Switch to Tab suggestion returned.", matchSuggestion.second);
-        Assert.assertEquals(matchSuggestion.second.getUrl().getSpec(), testHttpsUrl1);
-
-        OmniboxSuggestionsDropdown suggestionsDropdown =
-                AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
-                        locationBarLayout.getAutocompleteCoordinator());
-        clickSuggestionActionAt(suggestionsDropdown, (int) matchSuggestion.first);
+        typeAndClickMatchingTabMatchSuggestion(
+                mActivityTestRule.getActivity(), locationBarLayout, aboutTab);
 
         CriteriaHelper.pollUiThread(() -> {
             Tab tab = mActivityTestRule.getActivity().getActivityTab();
-            if (tab == null) return false;
+            Criteria.checkThat(tab, Matchers.notNullValue());
+            Criteria.checkThat(tab, Matchers.is(aboutTab));
             // Make sure tab is in either upload page or result page. cannot only verify one of
             // them since on fast device tab jump to result page really quick but on slow device
             // may stay on upload page for a really long time.
-            return tab.getUrlString().equals(testHttpsUrl1);
+            Criteria.checkThat(tab.getUrlString(), Matchers.is(testHttpsUrl1));
         });
     }
 
@@ -225,7 +281,7 @@
         final String testHttpsUrl2 = mTestServer.getURL("/chrome/test/data/android/ok.txt");
         final String testHttpsUrl3 = mTestServer.getURL("/chrome/test/data/android/test.html");
         // Open the url trying to match in incognito mode.
-        mActivityTestRule.loadUrlInNewTab(testHttpsUrl1, true);
+        final Tab aboutTab = mActivityTestRule.loadUrlInNewTab(testHttpsUrl1, true);
         mActivityTestRule.loadUrlInNewTab(testHttpsUrl2);
         mActivityTestRule.loadUrlInNewTab(testHttpsUrl3);
 
@@ -235,52 +291,51 @@
         mActivityTestRule.typeInOmnibox("about", false);
         OmniboxTestUtils.waitForOmniboxSuggestions(locationBarLayout);
 
-        Pair<Integer, OmniboxSuggestion> matchSuggestion =
-                findFirstTabMatchOmniboxSuggestion(locationBarLayout);
-
-        Assert.assertNull(
-                "Should no Switch to Incognito Tab from normal tab.", matchSuggestion.second);
+        CriteriaHelper.pollUiThread(() -> {
+            OmniboxSuggestion matchSuggestion =
+                    findTabMatchOmniboxSuggestion(locationBarLayout, aboutTab);
+            Criteria.checkThat(matchSuggestion, Matchers.nullValue());
+        });
     }
 
     @Test
     @MediumTest
     @EnableFeatures("OmniboxTabSwitchSuggestions")
-    public void testSwitchToTabInSearchActiviy() throws InterruptedException {
+    public void testSwitchToTabInSearchActivity() throws InterruptedException {
         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
                 InstrumentationRegistry.getInstrumentation().getContext(),
                 ServerCertificate.CERT_OK);
         final String testHttpsUrl1 = mTestServer.getURL("/chrome/test/data/android/about.html");
         final String testHttpsUrl2 = mTestServer.getURL("/chrome/test/data/android/ok.txt");
         final String testHttpsUrl3 = mTestServer.getURL("/chrome/test/data/android/test.html");
-        mActivityTestRule.loadUrlInNewTab(testHttpsUrl1);
+        final Tab aboutTab = mActivityTestRule.loadUrlInNewTab(testHttpsUrl1);
         mActivityTestRule.loadUrlInNewTab(testHttpsUrl2);
         mActivityTestRule.loadUrlInNewTab(testHttpsUrl3);
+        Assert.assertNotEquals(mActivityTestRule.getActivity().getActivityTab(), aboutTab);
 
         final SearchActivity searchActivity = startSearchActivity();
+        CriteriaHelper.pollUiThread(() -> {
+            Tab tab = mActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(tab, Matchers.notNullValue());
+
+            // Make sure chrome fully in background.
+            Criteria.checkThat(tab.getWindowAndroid().getActivityState(),
+                    Matchers.isOneOf(ActivityState.STOPPED, ActivityState.DESTROYED));
+        });
 
         final LocationBarLayout locationBarLayout =
                 (LocationBarLayout) searchActivity.findViewById(R.id.search_location_bar);
-        typeAndWaitForTabMatchSuggestions(searchActivity, locationBarLayout, "about");
-
-        Pair<Integer, OmniboxSuggestion> matchSuggestion =
-                findFirstTabMatchOmniboxSuggestion(locationBarLayout);
-
-        Assert.assertNotEquals(INVALID_INDEX, (int) matchSuggestion.first);
-        Assert.assertNotNull("No Switch to Tab suggestion returned.", matchSuggestion.second);
-        Assert.assertEquals(matchSuggestion.second.getUrl().getSpec(), testHttpsUrl1);
-
-        OmniboxSuggestionsDropdown suggestionsDropdown =
-                AutocompleteCoordinatorTestUtils.getSuggestionsDropdown(
-                        locationBarLayout.getAutocompleteCoordinator());
-        clickSuggestionActionAt(suggestionsDropdown, (int) matchSuggestion.first);
+        typeAndClickMatchingTabMatchSuggestion(searchActivity, locationBarLayout, aboutTab);
 
         CriteriaHelper.pollUiThread(() -> {
             Tab tab = mActivityTestRule.getActivity().getActivityTab();
-            if (tab == null) return false;
-            // Make sure tab is in either upload page or result page. cannot only verify one of
-            // them since on fast device tab jump to result page really quick but on slow device
-            // may stay on upload page for a really long time.
-            return tab.getUrlString().equals(testHttpsUrl1);
+            Criteria.checkThat(tab, Matchers.notNullValue());
+            Criteria.checkThat(tab, Matchers.is(aboutTab));
+            Criteria.checkThat(tab.getUrlString(), Matchers.is(testHttpsUrl1));
+            // Make sure tab is loaded and in foreground.
+            Criteria.checkThat(
+                    tab.getWindowAndroid().getActivityState(), Matchers.is(ActivityState.RESUMED));
+            Assert.assertEquals(tab, aboutTab);
         });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
index 6a01200..5eb2b2a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
@@ -99,8 +99,8 @@
     @Before
     public void setUp() {
         initMocks(this);
-        mAccountManagerTestRule.addAccount(PROFILE_DATA1.getAccountName(), PROFILE_DATA1);
-        mAccountManagerTestRule.addAccount(PROFILE_DATA2.getAccountName(), PROFILE_DATA2);
+        mAccountManagerTestRule.addAccount(PROFILE_DATA1);
+        mAccountManagerTestRule.addAccount(PROFILE_DATA2);
         mActivityTestRule.startMainActivityOnBlankPage();
     }
 
@@ -281,6 +281,15 @@
         checkCollapsedAccountList(PROFILE_DATA1);
     }
 
+    @Test
+    @MediumTest
+    public void testIncognitoOptionShownOnExpandedSheet() throws Exception {
+        buildAndShowExpandedBottomSheet();
+        onView(withText(R.string.signin_incognito_mode_secondary)).check(matches(isDisplayed()));
+        onView(withText(R.string.signin_incognito_mode_primary)).perform(click());
+        verify(mAccountPickerDelegateMock).goIncognitoMode();
+    }
+
     private void checkZeroAccountBottomSheet() {
         onView(allOf(withText(PROFILE_DATA1.getAccountName()), withEffectiveVisibility(VISIBLE)))
                 .check(doesNotExist());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerDialogFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerDialogFragmentTest.java
index e15fc66..5eeedcac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerDialogFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerDialogFragmentTest.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.components.signin.ProfileDataSource;
 import org.chromium.components.signin.test.util.FakeProfileDataSource;
@@ -48,16 +49,22 @@
  * Use FragmentScenario to test this fragment once we start using fragment from androidx.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags
-        .Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-        @Features.DisableFeatures({ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY})
-        public class AccountPickerDialogFragmentTest extends DummyUiActivityTestCase {
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@DisableFeatures({ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY})
+public class AccountPickerDialogFragmentTest extends DummyUiActivityTestCase {
     @Rule
     public final Features.JUnitProcessor mProcessor = new Features.JUnitProcessor();
 
+    // This test rule is only applied for Legacy AccountPickerDialog fragment.
+    // TODO(crbug.com/1106737): Refactor the tests into two separate files.
+    @Rule
+    public final ChromeRenderTestRule mRenderTestRuleLegacy =
+            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+
+    // This test rule is only applied when MOBILE_IDENTITY_CONSISTENCY feature flag is enabled.
     @Rule
     public final ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
+            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(2).build();
 
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule =
@@ -125,7 +132,7 @@
     @Feature("RenderTest")
     public void testAccountPickerDialogViewLegacy() throws IOException {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        mRenderTestRule.render(
+        mRenderTestRuleLegacy.render(
                 mDialog.getDialog().getWindow().getDecorView(), "account_picker_dialog_legacy");
     }
 
@@ -136,7 +143,7 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
         TestThreadUtils.runOnUiThreadBlocking(() -> mDialog.updateSelectedAccount(mAccountName2));
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        mRenderTestRule.render(mDialog.getDialog().getWindow().getDecorView(),
+        mRenderTestRuleLegacy.render(mDialog.getDialog().getWindow().getDecorView(),
                 "account_picker_dialog_update_selected_account");
     }
 
@@ -153,6 +160,6 @@
     private void addAccount(String accountName, String fullName) {
         ProfileDataSource.ProfileData profileData =
                 new ProfileDataSource.ProfileData(accountName, null, fullName, null);
-        mAccountManagerTestRule.addAccount(accountName, profileData);
+        mAccountManagerTestRule.addAccount(profileData);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/DummyAccountPickerTargetFragment.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/DummyAccountPickerTargetFragment.java
index 905b17e..62b8cc4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/DummyAccountPickerTargetFragment.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/DummyAccountPickerTargetFragment.java
@@ -17,4 +17,7 @@
 
     @Override
     public void addAccount() {}
+
+    @Override
+    public void goIncognitoMode() {}
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTest.java
index cbbf3d6b..353a873 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTest.java
@@ -30,7 +30,6 @@
 import org.chromium.chrome.test.util.browser.Features.JUnitProcessor;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.AccountUtils;
-import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
 import org.chromium.components.sync.test.util.MockSyncContentResolverDelegate;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -124,14 +123,11 @@
         mNumberOfCallsToWait = 0;
         mCallbackHelper = new CallbackHelper();
         setupTestAccounts();
-        // Set signed in account to mAccount before initializing AndroidSyncSettings to let
-        // AndroidSyncSettings establish correct assumptions.
-        ChromeSigninController.get().setSignedInAccountName(mAccount.name);
 
         mSyncContentResolverDelegate = new CountingMockSyncContentResolverDelegate();
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mAndroidSyncSettings = new AndroidSyncSettings(mSyncContentResolverDelegate,
-                    (Boolean result) -> mCallbackHelper.notifyCalled());
+                    (Boolean result) -> mCallbackHelper.notifyCalled(), mAccount);
         });
         mNumberOfCallsToWait++;
         mCallbackHelper.waitForCallback(0, mNumberOfCallsToWait);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTestUtils.java
index 6622c46bf..d23d1a47 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/AndroidSyncSettingsTestUtils.java
@@ -28,6 +28,8 @@
     @VisibleForTesting
     public static void setUpAndroidSyncSettingsForTesting(SyncContentResolverDelegate delegate) {
         delegate.setMasterSyncAutomatically(true);
-        AndroidSyncSettings.overrideForTests(new AndroidSyncSettings(delegate));
+        // AndroidSyncSettings ctor usually gets sync account from IdentityManager, which may have
+        // state left by previous tests. To avoid this, pass null account explicitly.
+        AndroidSyncSettings.overrideForTests(new AndroidSyncSettings(delegate, null, null));
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediatorTest.java
index 435cdd0..c292575 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerMediatorTest.java
@@ -12,14 +12,17 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.robolectric.RuntimeEnvironment;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.signin.DisplayableProfileData;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.AddAccountRowProperties;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerProperties.ExistingAccountRowProperties;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.components.signin.ProfileDataSource;
 import org.chromium.components.signin.test.util.FakeProfileDataSource;
@@ -36,6 +39,9 @@
     private static final String ACCOUNT_NAME2 = "test.account2@gmail.com";
 
     @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
+    @Rule
     public final AccountManagerTestRule mAccountManagerTestRule =
             new AccountManagerTestRule(new FakeProfileDataSource());
 
@@ -59,11 +65,27 @@
     }
 
     @Test
+    @Features.EnableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
     public void testModelPopulatedWhenStarted() {
         addAccount(ACCOUNT_NAME1, FULL_NAME1);
         addAccount(ACCOUNT_NAME2, "");
         mMediator = new AccountPickerMediator(
                 RuntimeEnvironment.application, mModelList, mListenerMock, ACCOUNT_NAME1);
+        // ACCOUNT_NAME1, ACCOUNT_NAME2, ADD_ACCOUNT, INCOGNITO MODE.
+        Assert.assertEquals(4, mModelList.size());
+        checkItemForExistingAccountRow(0, ACCOUNT_NAME1, FULL_NAME1, /* isSelectedAccount= */ true);
+        checkItemForExistingAccountRow(1, ACCOUNT_NAME2, "", /* isSelectedAccount= */ false);
+        checkItemForAddAccountRow(2);
+        checkItemForIncognitoAccountRow(3);
+    }
+
+    @Test
+    @Features.DisableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+    public void testModelPopulatedWhenStartedLegacy() {
+        addAccount(ACCOUNT_NAME1, FULL_NAME1);
+        addAccount(ACCOUNT_NAME2, "");
+        mMediator = new AccountPickerMediator(
+                RuntimeEnvironment.application, mModelList, mListenerMock, ACCOUNT_NAME1);
         Assert.assertEquals(3, mModelList.size());
         checkItemForExistingAccountRow(0, ACCOUNT_NAME1, FULL_NAME1, /* isSelectedAccount= */ true);
         checkItemForExistingAccountRow(1, ACCOUNT_NAME2, "", /* isSelectedAccount= */ false);
@@ -71,12 +93,30 @@
     }
 
     @Test
+    @Features.EnableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
     public void testModelUpdatedAfterSetSelectedAccountName() {
         addAccount(ACCOUNT_NAME1, FULL_NAME1);
         addAccount(ACCOUNT_NAME2, "");
         mMediator = new AccountPickerMediator(
                 RuntimeEnvironment.application, mModelList, mListenerMock, ACCOUNT_NAME1);
         mMediator.setSelectedAccountName(ACCOUNT_NAME2);
+        // ACCOUNT_NAME1, ACCOUNT_NAME2, ADD_ACCOUNT, INCOGNITO MODE.
+        Assert.assertEquals(4, mModelList.size());
+        checkItemForExistingAccountRow(
+                0, ACCOUNT_NAME1, FULL_NAME1, /* isSelectedAccount= */ false);
+        checkItemForExistingAccountRow(1, ACCOUNT_NAME2, "", /* isSelectedAccount= */ true);
+        checkItemForAddAccountRow(2);
+        checkItemForIncognitoAccountRow(3);
+    }
+
+    @Test
+    @Features.DisableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+    public void testModelUpdatedAfterSetSelectedAccountNameLegacy() {
+        addAccount(ACCOUNT_NAME1, FULL_NAME1);
+        addAccount(ACCOUNT_NAME2, "");
+        mMediator = new AccountPickerMediator(
+                RuntimeEnvironment.application, mModelList, mListenerMock, ACCOUNT_NAME1);
+        mMediator.setSelectedAccountName(ACCOUNT_NAME2);
         Assert.assertEquals(3, mModelList.size());
         checkItemForExistingAccountRow(
                 0, ACCOUNT_NAME1, FULL_NAME1, /* isSelectedAccount= */ false);
@@ -106,9 +146,17 @@
         verify(mListenerMock).addAccount();
     }
 
+    private void checkItemForIncognitoAccountRow(int position) {
+        MVCListAdapter.ListItem item = mModelList.get(position);
+        Assert.assertEquals(AccountPickerProperties.ItemType.INCOGNITO_ACCOUNT_ROW, item.type);
+        item.model.get(AccountPickerProperties.IncognitoAccountRowProperties.ON_CLICK_LISTENER)
+                .run();
+        verify(mListenerMock).goIncognitoMode();
+    }
+
     private void addAccount(String accountName, String fullName) {
         ProfileDataSource.ProfileData profileData =
                 new ProfileDataSource.ProfileData(accountName, null, fullName, null);
-        mAccountManagerTestRule.addAccount(accountName, profileData);
+        mAccountManagerTestRule.addAccount(profileData);
     }
 }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 98ec8355..ab01a67 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-86.0.4206.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-86.0.4207.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b2d9315..3b2018c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -16,7 +16,6 @@
 import("//chrome/browser/downgrade/buildflags.gni")
 import("//chrome/common/features.gni")
 import("//components/captive_portal/core/features.gni")
-import("//components/feature_engagement/features.gni")
 import("//components/feed/features.gni")
 import("//components/nacl/features.gni")
 import("//components/offline_pages/buildflags/features.gni")
@@ -3353,6 +3352,8 @@
       "performance_manager/policies/background_tab_loading_policy.h",
       "performance_manager/policies/background_tab_loading_policy_helpers.cc",
       "performance_manager/policies/background_tab_loading_policy_helpers.h",
+      "performance_manager/policies/page_discarding_helper.cc",
+      "performance_manager/policies/page_discarding_helper.h",
       "performance_manager/policies/urgent_page_discarding_policy.cc",
       "performance_manager/policies/urgent_page_discarding_policy.h",
       "permissions/attestation_permission_request.cc",
@@ -4826,27 +4827,6 @@
     ]
   }
 
-  if (enable_legacy_desktop_in_product_help) {
-    sources += [
-      "feature_engagement/bookmark/bookmark_tracker.cc",
-      "feature_engagement/bookmark/bookmark_tracker.h",
-      "feature_engagement/bookmark/bookmark_tracker_factory.cc",
-      "feature_engagement/bookmark/bookmark_tracker_factory.h",
-      "feature_engagement/feature_tracker.cc",
-      "feature_engagement/feature_tracker.h",
-      "feature_engagement/incognito_window/incognito_window_tracker.cc",
-      "feature_engagement/incognito_window/incognito_window_tracker.h",
-      "feature_engagement/incognito_window/incognito_window_tracker_factory.cc",
-      "feature_engagement/incognito_window/incognito_window_tracker_factory.h",
-      "feature_engagement/new_tab/new_tab_tracker.cc",
-      "feature_engagement/new_tab/new_tab_tracker.h",
-      "feature_engagement/new_tab/new_tab_tracker_factory.cc",
-      "feature_engagement/new_tab/new_tab_tracker_factory.h",
-      "feature_engagement/session_duration_updater.cc",
-      "feature_engagement/session_duration_updater.h",
-    ]
-  }
-
   if (enable_dice_support) {
     sources += [
       "signin/dice_intercepted_session_startup_helper.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4aab6a27..7c3b27c7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -80,7 +80,6 @@
 #include "components/download/public/common/download_features.h"
 #include "components/error_page/common/error_page_switches.h"
 #include "components/favicon/core/features.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/feature_list.h"
 #include "components/feed/feed_feature_list.h"
@@ -5584,6 +5583,9 @@
     {"media-app", flag_descriptions::kMediaAppName,
      flag_descriptions::kMediaAppDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kMediaApp)},
+    {"os-settings-polymer3", flag_descriptions::kOsSettingsPolymer3Name,
+     flag_descriptions::kOsSettingsPolymer3Description, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kOsSettingsPolymer3)},
 #endif  // defined(OS_CHROMEOS)
 
     {"passive-mixed-content-warning",
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 376ef8b..a964562 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -426,6 +426,7 @@
       return;
     }
   }
+  has_had_ui_ = true;
 
   if (!ui_controller_android_->IsAttached() ||
       (controller_ != nullptr &&
@@ -536,6 +537,17 @@
   return web_contents_;
 }
 
+void ClientAndroid::RecordDropOut(Metrics::DropOutReason reason) {
+  if (started_)
+    Metrics::RecordDropOut(reason);
+
+  started_ = false;
+}
+
+bool ClientAndroid::HasHadUI() const {
+  return has_had_ui_;
+}
+
 void ClientAndroid::Shutdown(Metrics::DropOutReason reason) {
   if (!controller_)
     return;
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index 04bba410..81aa35c2 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -111,6 +111,8 @@
   bool IsAccessibilityEnabled() const override;
   content::WebContents* GetWebContents() const override;
   void Shutdown(Metrics::DropOutReason reason) override;
+  void RecordDropOut(Metrics::DropOutReason reason) override;
+  bool HasHadUI() const override;
 
   // Overrides AccessTokenFetcher
   void FetchAccessToken(
@@ -154,6 +156,9 @@
   // True if Start() was called. This turns on the tracking of dropouts.
   bool started_ = false;
 
+  // True if the UI was ever attached.
+  bool has_had_ui_ = false;
+
   std::unique_ptr<UiControllerAndroid> ui_controller_android_;
 
   base::OnceCallback<void(bool, const std::string&)>
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index af59b97e..9099396 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -326,7 +326,6 @@
   ui_delegate_ = ui_delegate;
   ui_delegate_->AddObserver(this);
 
-  captured_debug_context_.clear();
   destroy_timer_.reset();
 
   JNIEnv* env = AttachCurrentThread();
@@ -470,7 +469,8 @@
 
       // Make sure the user sees the error message.
       ShowContentAndExpandBottomSheet();
-      Detach();
+      SetDestroyOnNavigation();
+      ResetGenericUiControllers();
       return;
 
     case AutofillAssistantState::TRACKING:
@@ -595,7 +595,8 @@
   JNIEnv* env = AttachCurrentThread();
   Java_AutofillAssistantUiController_showFeedback(
       env, java_object_,
-      base::android::ConvertUTF8ToJavaString(env, GetDebugContext()));
+      base::android::ConvertUTF8ToJavaString(env,
+                                             ui_delegate_->GetDebugContext()));
 }
 
 void UiControllerAndroid::OnViewEvent(const EventHandler::EventKey& key) {
@@ -649,14 +650,10 @@
   std::move(action).Run();
 }
 
-std::string UiControllerAndroid::GetDebugContext() {
-  if (captured_debug_context_.empty() && ui_delegate_) {
-    return ui_delegate_->GetDebugContext();
-  }
-  return captured_debug_context_;
-}
-
 void UiControllerAndroid::DestroySelf() {
+  if (ui_delegate_)
+    ui_delegate_->ShutdownIfNecessary();
+
   self_destruct_observer_.reset();
   client_->DestroyUI();
 }
@@ -898,7 +895,7 @@
   }
   overlay_state_ = state;
 
-  if (ui_delegate_->ShouldShowOverlay()) {
+  if (ui_delegate_ && ui_delegate_->ShouldShowOverlay()) {
     ApplyOverlayState(state);
   }
 }
@@ -985,25 +982,13 @@
   ui_controller_->DestroySelf();
 }
 
-void UiControllerAndroid::Detach() {
-  if (!ui_delegate_)
-    return;
-
+void UiControllerAndroid::SetDestroyOnNavigation() {
   auto* web_contents = client_->GetWebContents();
   if (web_contents != nullptr) {
     self_destruct_observer_ = std::make_unique<SelfDestructObserver>(
         web_contents, this, ui_delegate_->GetErrorCausingNavigationId());
   }
-
-  ResetGenericUiControllers();
-
-  // Capture the debug context, for including into a feedback possibly sent
-  // later.
-  captured_debug_context_ = ui_delegate_->GetDebugContext();
-  ui_delegate_->RemoveObserver(this);
-  ui_delegate_ = nullptr;
 }
-
 // Collect user data related methods.
 
 base::android::ScopedJavaLocalRef<jobject>
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index f88782f..e92e544c 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -61,18 +61,15 @@
 
   // Attaches the UI to the given client, its web contents and delegate.
   //
-  // |web_contents| and |client| must remain valid for the lifetime of this
-  // instance or until Attach() is called again, with different pointers.
-  //
-  // |ui_delegate| must remain valid for the lifetime of this instance or until
-  // either Attach() or Detach() are called.
+  // |web_contents|, |client| and |ui_delegate| must remain valid for the
+  // lifetime of this instance or until Attach() is called again, with different
+  // pointers.
   void Attach(content::WebContents* web_contents,
               Client* client,
               UiDelegate* ui_delegate);
 
-  // Detaches the UI from its delegate. This guarantees the delegate is not
-  // called anymore after the call.
-  void Detach();
+  // Destroys the UI the next time the user navigates.
+  void SetDestroyOnNavigation();
 
   // Returns true if the UI is attached to a delegate.
   bool IsAttached() { return ui_delegate_ != nullptr; }
@@ -282,10 +279,6 @@
   // for a few seconds before it destroys itself.
   std::unique_ptr<base::OneShotTimer> destroy_timer_;
 
-  // Debug context captured previously. If non-empty, GetDebugContext() returns
-  // this context.
-  std::string captured_debug_context_;
-
   // Java-side AutofillAssistantUiController object.
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 
diff --git a/chrome/browser/android/customtabs/origin_verifier.cc b/chrome/browser/android/customtabs/origin_verifier.cc
index a2b3c7e..cfd16895 100644
--- a/chrome/browser/android/customtabs/origin_verifier.cc
+++ b/chrome/browser/android/customtabs/origin_verifier.cc
@@ -36,11 +36,10 @@
   jobject_.Reset(obj);
   Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
   DCHECK(profile);
-  asset_link_handler_ =
-      std::make_unique<digital_asset_links::DigitalAssetLinksHandler>(
-          content::BrowserContext::GetDefaultStoragePartition(profile)
-              ->GetURLLoaderFactoryForBrowserProcess(),
-          content::WebContents::FromJavaWebContents(jweb_contents));
+  url_loader_factory_ =
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetURLLoaderFactoryForBrowserProcess();
+  web_contents_ = content::WebContents::FromJavaWebContents(jweb_contents);
 }
 
 OriginVerifier::~OriginVerifier() = default;
@@ -59,24 +58,30 @@
   std::string origin = ConvertJavaStringToUTF8(env, j_origin);
   std::string relationship = ConvertJavaStringToUTF8(env, j_relationship);
 
-  // Multiple calls here will end up resetting the callback on the handler side
-  // and cancelling previous requests.
-  // If during the request this verifier gets killed, the handler and the
-  // UrlFetcher making the request will also get killed, so we won't get any
-  // dangling callback reference issues.
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return asset_link_handler_->CheckDigitalAssetLinkRelationshipForAndroidApp(
+
+  auto asset_link_handler =
+      std::make_unique<digital_asset_links::DigitalAssetLinksHandler>(
+          url_loader_factory_, web_contents_);
+
+  auto* asset_link_handler_ptr = asset_link_handler.get();
+
+  return asset_link_handler_ptr->CheckDigitalAssetLinkRelationshipForAndroidApp(
       origin, relationship, fingerprint, package_name,
       base::BindOnce(&customtabs::OriginVerifier::OnRelationshipCheckComplete,
-                     base::Unretained(this)));
+                     base::Unretained(this), std::move(asset_link_handler),
+                     origin));
 }
 
 void OriginVerifier::OnRelationshipCheckComplete(
+    std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler> handler,
+    const std::string& origin,
     RelationshipCheckResult result) {
   JNIEnv* env = base::android::AttachCurrentThread();
 
-  Java_OriginVerifier_onOriginVerificationResult(env,
-                                                 jobject_,
+  auto j_origin = base::android::ConvertUTF8ToJavaString(env, origin);
+
+  Java_OriginVerifier_onOriginVerificationResult(env, jobject_, j_origin,
                                                  static_cast<jint>(result));
 }
 
diff --git a/chrome/browser/android/customtabs/origin_verifier.h b/chrome/browser/android/customtabs/origin_verifier.h
index 4b83ed5..16499b7a 100644
--- a/chrome/browser/android/customtabs/origin_verifier.h
+++ b/chrome/browser/android/customtabs/origin_verifier.h
@@ -5,10 +5,18 @@
 #ifndef CHROME_BROWSER_ANDROID_CUSTOMTABS_ORIGIN_VERIFIER_H_
 #define CHROME_BROWSER_ANDROID_CUSTOMTABS_ORIGIN_VERIFIER_H_
 
+#include <memory>
+
 #include "base/android/scoped_java_ref.h"
+#include "base/memory/scoped_refptr.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
 
 namespace digital_asset_links {
 enum class RelationshipCheckResult;
@@ -41,10 +49,12 @@
   static int GetClearBrowsingDataCallCountForTesting();
  private:
   void OnRelationshipCheckComplete(
+      std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler> handler,
+      const std::string& origin,
       digital_asset_links::RelationshipCheckResult result);
 
-  std::unique_ptr<digital_asset_links::DigitalAssetLinksHandler>
-      asset_link_handler_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  content::WebContents* web_contents_;
 
   base::android::ScopedJavaGlobalRef<jobject> jobject_;
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 52d0282..506a6f7 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -95,7 +95,6 @@
 #include "chrome/browser/prerender/isolated/isolated_prerender_url_loader_interceptor.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
-#include "chrome/browser/prerender/prerender_util.h"
 #include "chrome/browser/previews/previews_content_util.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -171,7 +170,6 @@
 #include "chrome/common/pepper_permission_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/prerender_url_loader_throttle.h"
-#include "chrome/common/prerender_util.h"
 #include "chrome/common/profiler/stack_sampling_configuration.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/renderer_configuration.mojom.h"
@@ -230,6 +228,7 @@
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prerender/common/prerender_final_status.h"
 #include "components/prerender/common/prerender_types.mojom.h"
+#include "components/prerender/common/prerender_util.h"
 #include "components/previews/content/previews_decider.h"
 #include "components/previews/content/previews_decider_impl.h"
 #include "components/previews/content/previews_ui_service.h"
diff --git a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.cc b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.cc
index 6f764af..d9dafd6 100644
--- a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.cc
+++ b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.cc
@@ -11,9 +11,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
 #include "chromeos/attestation/attestation_flow.h"
-#include "chromeos/cryptohome/async_method_caller.h"
-#include "chromeos/cryptohome/cryptohome_parameters.h"
-#include "chromeos/dbus/cryptohome/cryptohome_client.h"
+#include "chromeos/attestation/attestation_flow_integrated.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
@@ -37,8 +35,8 @@
     on_success.Run(data);
     return;
   }
-  LOG(ERROR) << "Cryptohome DBus method or server called failed with status: "
-             << status << ": " << from_here.ToString();
+  LOG(ERROR) << "Attestation DBus method failed with status: " << status << ": "
+             << from_here.ToString();
   if (!on_failure.is_null())
     on_failure.Run(status);
 }
@@ -51,15 +49,12 @@
 EnrollmentCertificateUploaderImpl::EnrollmentCertificateUploaderImpl(
     policy::CloudPolicyClient* policy_client)
     : EnrollmentCertificateUploaderImpl(policy_client,
-                                        nullptr, /* cryptohome_client */
                                         nullptr /* attestation_flow */) {}
 
 EnrollmentCertificateUploaderImpl::EnrollmentCertificateUploaderImpl(
     policy::CloudPolicyClient* policy_client,
-    CryptohomeClient* cryptohome_client,
     AttestationFlow* attestation_flow)
     : policy_client_(policy_client),
-      cryptohome_client_(cryptohome_client),
       attestation_flow_(attestation_flow),
       retry_limit_(kRetryLimit),
       retry_delay_(kRetryDelay) {
@@ -90,15 +85,8 @@
     return;
   }
 
-  if (!cryptohome_client_)
-    cryptohome_client_ = CryptohomeClient::Get();
-
   if (!attestation_flow_) {
-    std::unique_ptr<ServerProxy> attestation_ca_client(
-        new AttestationCAClient());
-    default_attestation_flow_.reset(new AttestationFlow(
-        cryptohome::AsyncMethodCaller::GetInstance(), cryptohome_client_,
-        std::move(attestation_ca_client)));
+    default_attestation_flow_ = std::make_unique<AttestationFlowIntegrated>();
     attestation_flow_ = default_attestation_flow_.get();
   }
 
diff --git a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h
index b562a4e..6677103f 100644
--- a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h
+++ b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h
@@ -22,8 +22,6 @@
 
 namespace chromeos {
 
-class CryptohomeClient;
-
 namespace attestation {
 
 class AttestationFlow;
@@ -34,10 +32,9 @@
   explicit EnrollmentCertificateUploaderImpl(
       policy::CloudPolicyClient* policy_client);
 
-  // A constructor which allows custom CryptohomeClient and AttestationFlow
-  // implementations. Useful for testing.
+  // A constructor which allows custom AttestationFlow implementation. Useful
+  // for testing.
   EnrollmentCertificateUploaderImpl(policy::CloudPolicyClient* policy_client,
-                                    CryptohomeClient* cryptohome_client,
                                     AttestationFlow* attestation_flow);
 
   ~EnrollmentCertificateUploaderImpl() override;
@@ -78,7 +75,6 @@
   void Reschedule();
 
   policy::CloudPolicyClient* policy_client_;
-  CryptohomeClient* cryptohome_client_;
   AttestationFlow* attestation_flow_;
   std::unique_ptr<AttestationFlow> default_attestation_flow_;
   // Callbacks to run when a certificate is uploaded (or we fail to).
diff --git a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc
index 9b79459..bd0f479 100644
--- a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc
+++ b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h"
+
 #include <stdint.h>
 
 #include <string>
@@ -14,11 +16,9 @@
 #include "base/test/bind_test_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/attestation/attestation_key_payload.pb.h"
-#include "chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h"
 #include "chrome/browser/chromeos/attestation/fake_certificate.h"
 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
 #include "chromeos/attestation/mock_attestation_flow.h"
-#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "content/public/test/browser_task_environment.h"
@@ -86,8 +86,8 @@
   }
 
   void Run(bool expected_status) {
-    EnrollmentCertificateUploaderImpl uploader(
-        &policy_client_, &cryptohome_client_, &attestation_flow_);
+    EnrollmentCertificateUploaderImpl uploader(&policy_client_,
+                                               &attestation_flow_);
     uploader.set_retry_limit(3);
     uploader.set_retry_delay(base::TimeDelta());
 
@@ -101,7 +101,6 @@
 
   content::BrowserTaskEnvironment task_environment_;
   ScopedCrosSettingsTestHelper settings_helper_;
-  FakeCryptohomeClient cryptohome_client_;
   StrictMock<MockAttestationFlow> attestation_flow_;
   StrictMock<policy::MockCloudPolicyClient> policy_client_;
 };
diff --git a/chrome/browser/chromeos/attestation/machine_certificate_uploader_impl.cc b/chrome/browser/chromeos/attestation/machine_certificate_uploader_impl.cc
index 14034e7..73a5ac24 100644
--- a/chrome/browser/chromeos/attestation/machine_certificate_uploader_impl.cc
+++ b/chrome/browser/chromeos/attestation/machine_certificate_uploader_impl.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/chromeos/attestation/attestation_key_payload.pb.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chromeos/attestation/attestation_flow.h"
+#include "chromeos/attestation/attestation_flow_integrated.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
@@ -163,11 +164,7 @@
     cryptohome_client_ = CryptohomeClient::Get();
 
   if (!attestation_flow_) {
-    std::unique_ptr<ServerProxy> attestation_ca_client(
-        new AttestationCAClient());
-    default_attestation_flow_.reset(new AttestationFlow(
-        cryptohome::AsyncMethodCaller::GetInstance(), cryptohome_client_,
-        std::move(attestation_ca_client)));
+    default_attestation_flow_ = std::make_unique<AttestationFlowIntegrated>();
     attestation_flow_ = default_attestation_flow_.get();
   }
 
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.cc b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
index b594be3..c193f33 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/permissions/permission_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/attestation/attestation_flow.h"
+#include "chromeos/attestation/attestation_flow_integrated.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
@@ -152,9 +153,7 @@
       delegate_(NULL),
       timeout_delay_(base::TimeDelta::FromSeconds(kTimeoutInSeconds)) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  std::unique_ptr<ServerProxy> attestation_ca_client(new AttestationCAClient());
-  default_attestation_flow_.reset(new AttestationFlow(
-      async_caller_, cryptohome_client_, std::move(attestation_ca_client)));
+  default_attestation_flow_ = std::make_unique<AttestationFlowIntegrated>();
   attestation_flow_ = default_attestation_flow_.get();
   default_delegate_.reset(new DefaultDelegate());
   delegate_ = default_delegate_.get();
diff --git a/chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.cc b/chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.cc
index 00c9671ea..c28e854 100644
--- a/chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.cc
+++ b/chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/attestation/attestation_flow_integrated.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/constants/attestation_constants.h"
@@ -115,10 +116,7 @@
 }  // namespace
 
 TpmChallengeKeySubtleImpl::TpmChallengeKeySubtleImpl()
-    : default_attestation_flow_(std::make_unique<AttestationFlow>(
-          cryptohome::AsyncMethodCaller::GetInstance(),
-          CryptohomeClient::Get(),
-          std::make_unique<AttestationCAClient>())),
+    : default_attestation_flow_(std::make_unique<AttestationFlowIntegrated>()),
       attestation_flow_(default_attestation_flow_.get()) {}
 
 TpmChallengeKeySubtleImpl::TpmChallengeKeySubtleImpl(
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 4b932ec..19c8886 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -425,6 +425,7 @@
   SET_STRING("FEEDBACK_COLLAPSE_LABEL",
              IDS_FILE_BROWSER_FEEDBACK_COLLAPSE_LABEL);
   SET_STRING("FILES_FEEDBACK_WINDOW", IDS_FILE_BROWSER_FILES_FEEDBACK_WINDOW);
+  SET_STRING("COMPLETE_LABEL", IDS_FILE_BROWSER_COMPLETE_LABEL);
   SET_STRING("CONFIGURE_VOLUME_BUTTON_LABEL",
              IDS_FILE_BROWSER_CONFIGURE_VOLUME_BUTTON_LABEL);
   SET_STRING("CONFIRM_MOBILE_DATA_USE",
@@ -450,10 +451,6 @@
   SET_STRING("COPY_FILE_NAME_LONG", IDS_FILE_BROWSER_COPY_FILE_NAME_LONG);
   SET_STRING("COPY_ITEMS_REMAINING_LONG",
              IDS_FILE_BROWSER_COPY_ITEMS_REMAINING_LONG);
-  SET_STRING("COPY_TIME_REMAINING_ESTIMATE",
-             IDS_FILE_BROWSER_COPY_TIME_REMAINING_ESTIMATE);
-  SET_STRING("COPY_TIME_REMAINING_CLOSE",
-             IDS_FILE_BROWSER_COPY_TIME_REMAINING_CLOSE);
   SET_STRING("COPY_PROGRESS_SUMMARY", IDS_FILE_BROWSER_COPY_PROGRESS_SUMMARY);
   SET_STRING("COPY_SOURCE_NOT_FOUND_ERROR",
              IDS_FILE_BROWSER_COPY_SOURCE_NOT_FOUND_ERROR);
@@ -493,6 +490,7 @@
              IDS_FILE_BROWSER_DISABLED_MOBILE_SYNC_NOTIFICATION_ENABLE_BUTTON);
   SET_STRING("DISABLED_MOBILE_SYNC_NOTIFICATION_MESSAGE",
              IDS_FILE_BROWSER_DISABLED_MOBILE_SYNC_NOTIFICATION_MESSAGE);
+  SET_STRING("DISMISS_LABEL", IDS_FILE_BROWSER_DISMISS_LABEL);
   SET_STRING("DOWNLOADS_DIRECTORY_LABEL",
              IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL);
   SET_STRING("DOWNLOADS_DIRECTORY_WARNING",
@@ -735,6 +733,7 @@
   SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL);
   SET_STRING("PASTE_INTO_FOLDER_BUTTON_LABEL",
              IDS_FILE_BROWSER_PASTE_INTO_FOLDER_BUTTON_LABEL);
+  SET_STRING("PENDING_LABEL", IDS_FILE_BROWSER_PENDING_LABEL);
   SET_STRING("PLUGIN_VM_DIRECTORY_LABEL",
              IDS_FILE_BROWSER_PLUGIN_VM_DIRECTORY_LABEL);
   SET_STRING("PREPARING_LABEL", IDS_FILE_BROWSER_PREPARING_LABEL);
@@ -893,6 +892,10 @@
   SET_STRING("TASK_OPEN_GSLIDES", IDS_FILE_BROWSER_TASK_OPEN_GSLIDES);
   SET_STRING("TASK_VIEW", IDS_FILE_BROWSER_TASK_VIEW);
   SET_STRING("THUMBNAIL_VIEW_TOOLTIP", IDS_FILE_BROWSER_THUMBNAIL_VIEW_TOOLTIP);
+  SET_STRING("TIME_REMAINING_ESTIMATE",
+             IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE);
+  SET_STRING("TIME_REMAINING_ESTIMATE_2",
+             IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE_2);
   SET_STRING("TIME_TODAY", IDS_FILE_BROWSER_TIME_TODAY);
   SET_STRING("TIME_YESTERDAY", IDS_FILE_BROWSER_TIME_YESTERDAY);
   SET_STRING("TRANSFER_PROGRESS_SUMMARY",
@@ -928,6 +931,7 @@
   SET_STRING("GRID_VIEW_FILES_TITLE", IDS_FILE_BROWSER_GRID_VIEW_FILES_TITLE);
   SET_STRING("LOCATION_BREADCRUMB_ELIDER_BUTTON_LABEL",
              IDS_FILE_BROWSER_LOCATION_BREADCRUMB_ELIDER_BUTTON_LABEL);
+
 #undef SET_STRING
 
   dict->SetString(
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 7032c75..c93e161 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
@@ -741,7 +741,8 @@
 
 // Zero touch with attestation authentication fail. Attestation fails because we
 // send empty cert request. Should switch to interactive authentication.
-IN_PROC_BROWSER_TEST_F(InitialEnrollmentTest, ZeroTouchForcedAttestationFail) {
+IN_PROC_BROWSER_TEST_F(InitialEnrollmentTest,
+                       DISABLED_ZeroTouchForcedAttestationFail) {
   auto initial_enrollment =
       enterprise_management::DeviceInitialEnrollmentStateResponse::
           INITIAL_ENROLLMENT_MODE_ZERO_TOUCH_ENFORCED;
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
index 889373c..4ba6f5b 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
@@ -60,11 +60,11 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/attestation/attestation_flow.h"
+#include "chromeos/attestation/attestation_flow_integrated.h"
 #include "chromeos/constants/chromeos_paths.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
-#include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/dbus/upstart/upstart_client.h"
@@ -518,10 +518,7 @@
 
 std::unique_ptr<chromeos::attestation::AttestationFlow>
 BrowserPolicyConnectorChromeOS::CreateAttestationFlow() {
-  return std::make_unique<chromeos::attestation::AttestationFlow>(
-      cryptohome::AsyncMethodCaller::GetInstance(),
-      chromeos::CryptohomeClient::Get(),
-      std::make_unique<chromeos::attestation::AttestationCAClient>());
+  return std::make_unique<chromeos::attestation::AttestationFlowIntegrated>();
 }
 
 chromeos::AffiliationIDSet
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 2aa16327..141a47d 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -2059,7 +2059,7 @@
     policy::PolicyTest::SetUpInProcessBrowserTestFixture();
     policy::PolicyMap policies;
     SetPolicy(&policies, policy::key::kUserAgentClientHintsEnabled,
-              std::make_unique<base::Value>(false));
+              base::Value(false));
     provider_.UpdateChromePolicy(policies);
   }
 };
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc
index 1b1ed0a..c0b4d24 100644
--- a/chrome/browser/download/download_request_limiter.cc
+++ b/chrome/browser/download/download_request_limiter.cc
@@ -235,7 +235,10 @@
   permissions::PermissionRequestManager* permission_request_manager =
       permissions::PermissionRequestManager::FromWebContents(web_contents_);
   if (permission_request_manager) {
+    // TODO(https://crbug.com/1061899): We should pass the frame which initiated
+    // the action instead of assuming that it was the current main frame.
     permission_request_manager->AddRequest(
+        web_contents_->GetMainFrame(),
         new DownloadPermissionRequest(factory_.GetWeakPtr(), request_origin));
   } else {
     // Call CancelOnce() so we don't set the content settings.
diff --git a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc
index 5cea0f0..6bba029 100644
--- a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc
+++ b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc
@@ -383,6 +383,13 @@
 void AutofillAssistantPrivateAPI::Shutdown(
     autofill_assistant::Metrics::DropOutReason reason) {}
 
+void AutofillAssistantPrivateAPI::RecordDropOut(
+    autofill_assistant::Metrics::DropOutReason reason) {}
+
+void bool AutofillAssistantPrivateAPI::HasHadUI() {
+  return true;
+}
+
 // Note that this method implements autofill_assistant::Client and simply
 // forwards the web_contents associated with the controller. There is no reason
 // to use this method in this context.
diff --git a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h
index 8d54afe..ae35aa0 100644
--- a/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h
+++ b/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h
@@ -216,6 +216,8 @@
   bool IsAccessibilityEnabled() const override;
   void Shutdown(autofill_assistant::Metrics::DropOutReason reason) override;
   content::WebContents* GetWebContents() const override;
+  void RecordDropOut(Metrics::DropOutReason reason) override;
+  bool HasHadUI() const override;
 
   // BrowserContextKeyedAPI:
   void Shutdown() override;
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
index 204f0bd2..3dfa12e 100644
--- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
+++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
@@ -272,10 +272,15 @@
 
   RecordAttestationEvent(U2FAttestationPromptResult::kQueried);
   // The created AttestationPermissionRequest deletes itself once complete.
-  permission_request_manager->AddRequest(NewAttestationPermissionRequest(
-      origin,
-      base::BindOnce(
-          &CryptotokenPrivateCanAppIdGetAttestationFunction::Complete, this)));
+  permission_request_manager->AddRequest(
+      web_contents->GetMainFrame(),  // Extension API targets a particular tab,
+                                     // so select the current main frame to
+                                     // handle the request.
+      NewAttestationPermissionRequest(
+          origin,
+          base::BindOnce(
+              &CryptotokenPrivateCanAppIdGetAttestationFunction::Complete,
+              this)));
   return RespondLater();
 }
 
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc
index 9ee28a4..a2d9480 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -1045,8 +1045,8 @@
             "This feature cannot be disabled in settings, but disabling all "
             "extensions will prevent it."
           chrome_policy {
-            ExtensionInstallBlacklist {
-              ExtensionInstallBlacklist: {
+            ExtensionInstallBlocklist {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
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 54808bf..dca2862 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -259,7 +259,7 @@
       api_credential.is_android_credential = true;
       // |formatted_orgin|, |detailed_origin| and |change_password_url| need
       // special handling for Android. Here we use affiliation information
-      // instead of the signon_realm.
+      // instead of the origin.
       const PasswordForm& android_form =
           compromised_credentials_manager_.GetSavedPasswordsFor(credential)[0];
       if (!android_form.app_display_name.empty()) {
@@ -280,7 +280,7 @@
       api_credential.is_android_credential = false;
       api_credential.formatted_origin =
           base::UTF16ToUTF8(url_formatter::FormatUrl(
-              GURL(credential.signon_realm),
+              credential.url.GetOrigin(),
               url_formatter::kFormatUrlOmitDefaults |
                   url_formatter::kFormatUrlOmitHTTPS |
                   url_formatter::kFormatUrlOmitTrivialSubdomains |
@@ -288,9 +288,9 @@
               net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
       api_credential.detailed_origin =
           base::UTF16ToUTF8(url_formatter::FormatUrlForSecurityDisplay(
-              GURL(credential.signon_realm)));
+              credential.url.GetOrigin()));
       api_credential.change_password_url =
-          std::make_unique<std::string>(credential.signon_realm);
+          std::make_unique<std::string>(credential.url.GetOrigin().spec());
     }
 
     api_credential.id =
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
index ef4fb10..495f31a 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
@@ -160,6 +160,7 @@
                                base::StringPiece username_element = "") {
   PasswordForm form;
   form.signon_realm = std::string(signon_realm);
+  form.url = GURL(signon_realm);
   form.username_value = base::ASCIIToUTF16(username);
   form.password_value = base::ASCIIToUTF16(password);
   form.username_element = base::ASCIIToUTF16(username_element);
@@ -296,22 +297,25 @@
 
   EXPECT_THAT(
       delegate().GetCompromisedCredentials(),
-      ElementsAre(ExpectCompromisedCredential(
-                      "example.com", kExampleCom, kExampleCom, kUsername2,
-                      base::TimeDelta::FromMinutes(2), "2 minutes ago",
-                      api::passwords_private::COMPROMISE_TYPE_PHISHED),
-                  ExpectCompromisedCredential(
-                      "example.org", kExampleOrg, kExampleOrg, kUsername1,
-                      base::TimeDelta::FromMinutes(4), "4 minutes ago",
-                      api::passwords_private::COMPROMISE_TYPE_PHISHED),
-                  ExpectCompromisedCredential(
-                      "example.com", kExampleCom, kExampleCom, kUsername1,
-                      base::TimeDelta::FromMinutes(1), "1 minute ago",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED),
-                  ExpectCompromisedCredential(
-                      "example.org", kExampleOrg, kExampleOrg, kUsername2,
-                      base::TimeDelta::FromMinutes(3), "3 minutes ago",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED)));
+      ElementsAre(
+          ExpectCompromisedCredential(
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername2, base::TimeDelta::FromMinutes(2), "2 minutes ago",
+              api::passwords_private::COMPROMISE_TYPE_PHISHED),
+          ExpectCompromisedCredential(
+              "example.org", "http://www.example.org",
+              "http://www.example.org/", kUsername1,
+              base::TimeDelta::FromMinutes(4), "4 minutes ago",
+              api::passwords_private::COMPROMISE_TYPE_PHISHED),
+          ExpectCompromisedCredential(
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername1, base::TimeDelta::FromMinutes(1), "1 minute ago",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED),
+          ExpectCompromisedCredential(
+              "example.org", "http://www.example.org",
+              "http://www.example.org/", kUsername2,
+              base::TimeDelta::FromMinutes(3), "3 minutes ago",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED)));
 }
 
 // Verifies that the formatted timestamp associated with a compromised
@@ -339,22 +343,25 @@
 
   EXPECT_THAT(
       delegate().GetCompromisedCredentials(),
-      ElementsAre(ExpectCompromisedCredential(
-                      "example.com", kExampleCom, kExampleCom, kUsername1,
-                      base::TimeDelta::FromSeconds(59), "Just now",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED),
-                  ExpectCompromisedCredential(
-                      "example.com", kExampleCom, kExampleCom, kUsername2,
-                      base::TimeDelta::FromSeconds(60), "1 minute ago",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED),
-                  ExpectCompromisedCredential(
-                      "example.org", kExampleOrg, kExampleOrg, kUsername1,
-                      base::TimeDelta::FromDays(100), "3 months ago",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED),
-                  ExpectCompromisedCredential(
-                      "example.org", kExampleOrg, kExampleOrg, kUsername2,
-                      base::TimeDelta::FromDays(800), "2 years ago",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED)));
+      ElementsAre(
+          ExpectCompromisedCredential(
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername1, base::TimeDelta::FromSeconds(59), "Just now",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED),
+          ExpectCompromisedCredential(
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername2, base::TimeDelta::FromSeconds(60), "1 minute ago",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED),
+          ExpectCompromisedCredential(
+              "example.org", "http://www.example.org",
+              "http://www.example.org/", kUsername1,
+              base::TimeDelta::FromDays(100), "3 months ago",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED),
+          ExpectCompromisedCredential(
+              "example.org", "http://www.example.org",
+              "http://www.example.org/", kUsername2,
+              base::TimeDelta::FromDays(800), "2 years ago",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED)));
 }
 
 // Verifies that both leaked and phished credentials are ordered correctly
@@ -392,20 +399,22 @@
       delegate().GetCompromisedCredentials(),
       ElementsAre(
           ExpectCompromisedCredential(
-              "example.com", kExampleCom, kExampleCom, kUsername1,
-              base::TimeDelta::FromMinutes(1), "1 minute ago",
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername1, base::TimeDelta::FromMinutes(1), "1 minute ago",
               api::passwords_private::COMPROMISE_TYPE_PHISHED_AND_LEAKED),
           ExpectCompromisedCredential(
-              "example.org", kExampleOrg, kExampleOrg, kUsername1,
+              "example.org", "http://www.example.org",
+              "http://www.example.org/", kUsername1,
               base::TimeDelta::FromMinutes(3), "3 minutes ago",
               api::passwords_private::COMPROMISE_TYPE_PHISHED),
           ExpectCompromisedCredential(
-              "example.org", kExampleOrg, kExampleOrg, kUsername2,
+              "example.org", "http://www.example.org",
+              "http://www.example.org/", kUsername2,
               base::TimeDelta::FromMinutes(4), "4 minutes ago",
               api::passwords_private::COMPROMISE_TYPE_PHISHED_AND_LEAKED),
           ExpectCompromisedCredential(
-              "example.com", kExampleCom, kExampleCom, kUsername2,
-              base::TimeDelta::FromMinutes(2), "2 minutes ago",
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername2, base::TimeDelta::FromMinutes(2), "2 minutes ago",
               api::passwords_private::COMPROMISE_TYPE_LEAKED)));
 }
 
@@ -430,18 +439,19 @@
   // password store.
   EXPECT_THAT(
       delegate().GetCompromisedCredentials(),
-      ElementsAre(ExpectCompromisedCredential(
-                      "Example App", "Example App", kExampleCom, kUsername2,
-                      base::TimeDelta::FromDays(3), "3 days ago",
-                      api::passwords_private::COMPROMISE_TYPE_PHISHED),
-                  ExpectCompromisedCredential(
-                      "App (com.example.app)", kExampleApp, base::nullopt,
-                      kUsername1, base::TimeDelta::FromDays(4), "4 days ago",
-                      api::passwords_private::COMPROMISE_TYPE_PHISHED),
-                  ExpectCompromisedCredential(
-                      "example.com", kExampleCom, kExampleCom, kUsername1,
-                      base::TimeDelta::FromMinutes(5), "5 minutes ago",
-                      api::passwords_private::COMPROMISE_TYPE_LEAKED)));
+      ElementsAre(
+          ExpectCompromisedCredential(
+              "Example App", "Example App", "https://example.com", kUsername2,
+              base::TimeDelta::FromDays(3), "3 days ago",
+              api::passwords_private::COMPROMISE_TYPE_PHISHED),
+          ExpectCompromisedCredential(
+              "App (com.example.app)", "com.example.app", base::nullopt,
+              kUsername1, base::TimeDelta::FromDays(4), "4 days ago",
+              api::passwords_private::COMPROMISE_TYPE_PHISHED),
+          ExpectCompromisedCredential(
+              "example.com", "https://example.com", "https://example.com/",
+              kUsername1, base::TimeDelta::FromMinutes(5), "5 minutes ago",
+              api::passwords_private::COMPROMISE_TYPE_LEAKED)));
 }
 
 // Test that a change to compromised credential notifies observers.
diff --git a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
index aaf771f..b3718db 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
@@ -36,6 +36,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "components/version_info/version_info.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/extension_pref_value_map.h"
 #include "extensions/browser/extension_pref_value_map_factory.h"
@@ -364,6 +365,18 @@
   void TearDown() override {
     ExtensionMessageBubbleController::set_should_ignore_learn_more_for_testing(
         false);
+    WaitForStorageCleanup();
+    // Clean up global state for the delegates. Since profiles are stored in
+    // global variables, they can be shared between tests and cause
+    // unpredicatable behavior.
+    DevModeBubbleDelegate(profile()).ClearProfileSetForTesting();
+    NtpOverriddenBubbleDelegate(profile()).ClearProfileSetForTesting();
+    ProxyOverriddenBubbleDelegate(profile()).ClearProfileSetForTesting();
+    for (auto type : {BUBBLE_TYPE_HOME_PAGE, BUBBLE_TYPE_SEARCH_ENGINE,
+                      BUBBLE_TYPE_STARTUP_PAGES}) {
+      SettingsApiBubbleDelegate(profile(), type).ClearProfileSetForTesting();
+    }
+    SuspiciousExtensionBubbleDelegate(profile()).ClearProfileSetForTesting();
     BrowserWithTestWindowTest::TearDown();
   }
 
@@ -396,6 +409,13 @@
     EXPECT_FALSE(controller->ShouldShow());
   }
 
+  void WaitForStorageCleanup() {
+    content::StoragePartition* partition =
+        content::BrowserContext::GetDefaultStoragePartition(profile());
+    if (partition)
+      partition->WaitForDeletionTasksForTesting();
+  }
+
  protected:
   ExtensionService* service_;
 
@@ -412,9 +432,8 @@
 // Test that the bubble correctly treats dismissal due to deactivation.
 // Currently, the NTP bubble is the only one that has flexible behavior (toggled
 // by a feature).
-// TODO(https://crbug.com/836332): Re-enable once not flaky.
 TEST_P(ExtensionMessageBubbleTestWithParam,
-       DISABLED_BubbleCorrectlyReshowsOnDeactivationDismissal) {
+       BubbleCorrectlyReshowsOnDeactivationDismissal) {
   const bool kAcknowledgeOnDeactivate = GetParam();
   base::test::ScopedFeatureList feature_list;
   if (kAcknowledgeOnDeactivate) {
@@ -741,12 +760,8 @@
 
 // The feature this is meant to test is only implemented on Windows and Mac.
 #if defined(OS_WIN) || defined(OS_MACOSX)
-#define MAYBE_SettingsApiControllerTest SettingsApiControllerTest
-#else
-#define MAYBE_SettingsApiControllerTest DISABLED_SettingsApiControllerTest
-#endif
 
-TEST_F(ExtensionMessageBubbleTest, MAYBE_SettingsApiControllerTest) {
+TEST_F(ExtensionMessageBubbleTest, SettingsApiControllerTest) {
 #if defined(OS_MACOSX)
   // On Mac, this API is limited to trunk.
   ScopedCurrentChannel scoped_channel(version_info::Channel::UNKNOWN);
@@ -888,11 +903,11 @@
   }
 }
 
+#endif  // defined(OS_WIN) || defined(OS_MACOSX)
+
 // Tests that a displayed extension bubble will be closed after its associated
 // enabled extension is uninstalled.
-// TODO(https://crbug.com/836332): Re-enable once not flaky.
-TEST_F(ExtensionMessageBubbleTest,
-       DISABLED_TestBubbleClosedAfterEnabledExtensionUninstall) {
+TEST_F(ExtensionMessageBubbleTest, BubbleClosedAfterEnabledExtensionUninstall) {
   Init();
   ASSERT_TRUE(LoadExtensionOverridingNtp("1", kId1, Manifest::UNPACKED));
 
@@ -924,9 +939,8 @@
 // Tests that a displayed extension bubble will be closed after its associated
 // disabled extension is uninstalled. Here a suspicious bubble controller is
 // tested, which can display bubbles for disabled extensions.
-// TODO(https://crbug.com/836332): Re-enable once not flaky.
 TEST_F(ExtensionMessageBubbleTest,
-       DISABLED_TestBubbleClosedAfterDisabledExtensionUninstall) {
+       BubbleClosedAfterDisabledExtensionUninstall) {
   Init();
   ASSERT_TRUE(LoadExtensionOverridingNtp("1", kId1, Manifest::COMMAND_LINE));
 
@@ -978,9 +992,7 @@
 // Tests that a bubble associated with multiple extensions remains shown after
 // one of its associated extensions is uninstalled. Also tests that the bubble
 // closes when all of its associated extensions are uninstalled.
-// Flaky: https://crbug.com/836332
-TEST_F(ExtensionMessageBubbleTest,
-       DISABLED_TestBubbleShownForMultipleExtensions) {
+TEST_F(ExtensionMessageBubbleTest, BubbleShownForMultipleExtensions) {
   FeatureSwitch::ScopedOverride force_dev_mode_highlighting(
       FeatureSwitch::force_dev_mode_highlighting(), true);
   Init();
@@ -1024,8 +1036,7 @@
 
 // The feature this is meant to test is only enacted on Windows, but it should
 // pass on all platforms.
-// TODO(https://crbug.com/836332): Re-enable once not flaky.
-TEST_F(ExtensionMessageBubbleTest, DISABLED_NtpOverriddenControllerTest) {
+TEST_F(ExtensionMessageBubbleTest, NtpOverriddenControllerTest) {
   Init();
   // Load two extensions overriding new tab page and one overriding something
   // unrelated (to check for interference). Extension 2 should still win
@@ -1138,9 +1149,7 @@
 // an NTP overriding extension is installed for a single profile. Note that the
 // NTP bubble is only implemented on Windows and ChromeOs, but this test should
 // still pass on Linux and Mac.
-// TODO(https://crbug.com/836332): Re-enable once not flaky.
-TEST_F(ExtensionMessageBubbleTest,
-       DISABLED_ShowNtpBubblePerProfilePerExtensionTest) {
+TEST_F(ExtensionMessageBubbleTest, ShowNtpBubblePerProfilePerExtensionTest) {
   Init();
   ASSERT_TRUE(LoadExtensionOverridingNtp("1", kId1, Manifest::UNPACKED));
   std::unique_ptr<TestExtensionMessageBubbleController> controller(
@@ -1340,22 +1349,12 @@
   controller.reset();
 }
 
-// Fails on linux-chromeos-rel: crbug.com/839371
-#if defined(OS_CHROMEOS)
-#define MAYBE_TestUninstallExtensionAfterBrowserDestroyed \
-  DISABLED_TestUninstallExtensionAfterBrowserDestroyed
-#else
-// TODO(https://crbug.com/836332): Re-enable once not flaky.
-#define MAYBE_TestUninstallExtensionAfterBrowserDestroyed \
-  DISABLED_TestUninstallExtensionAfterBrowserDestroyed
-#endif  // defined(OS_CHROMEOS)
-
 // Tests that when an extension -- associated with a bubble controller -- is
 // uninstalling after the browser is destroyed, the controller does not access
 // the associated browser object and therefore, no use-after-free occurs.
 // crbug.com/756316
 TEST_F(ExtensionMessageBubbleTest,
-       MAYBE_TestUninstallExtensionAfterBrowserDestroyed) {
+       TestUninstallExtensionAfterBrowserDestroyed) {
   FeatureSwitch::ScopedOverride force_dev_mode_highlighting(
       FeatureSwitch::force_dev_mode_highlighting(), true);
   Init();
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc
index b05c4ef..ac4c0758 100644
--- a/chrome/browser/extensions/install_signer.cc
+++ b/chrome/browser/extensions/install_signer.cc
@@ -327,9 +327,9 @@
             "This feature cannot be disabled, but it is only activated if "
             "extensions are installed."
           chrome_policy {
-            ExtensionInstallBlacklist {
+            ExtensionInstallBlocklist {
               policy_options {mode: MANDATORY}
-              ExtensionInstallBlacklist: {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc
index 4b83edc..02640da 100644
--- a/chrome/browser/extensions/webstore_installer.cc
+++ b/chrome/browser/extensions/webstore_installer.cc
@@ -656,8 +656,8 @@
             "This feature cannot be disabled. It is only activated if the user "
             "triggers an extension installation."
           chrome_policy {
-            ExtensionInstallBlacklist {
-              ExtensionInstallBlacklist: {
+            ExtensionInstallBlocklist {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc b/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc
deleted file mode 100644
index 6ef9e83..0000000
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2017 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/feature_engagement/bookmark/bookmark_tracker.h"
-
-#include "base/time/time.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/common/pref_names.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-
-namespace {
-
-constexpr int kDefaultBookmarkPromoShowTimeInHours = 5;
-constexpr char kBookmarkObservedSessionTimeKey[] =
-    "bookmark_in_product_help_observed_session_time_key";
-
-}  // namespace
-
-namespace feature_engagement {
-
-BookmarkTracker::BookmarkTracker(Profile* profile)
-    : FeatureTracker(
-          profile,
-          &kIPHBookmarkFeature,
-          kBookmarkObservedSessionTimeKey,
-          base::TimeDelta::FromHours(kDefaultBookmarkPromoShowTimeInHours)) {}
-
-BookmarkTracker::~BookmarkTracker() = default;
-
-void BookmarkTracker::OnPromoClosed() {
-  GetTracker()->Dismissed(kIPHBookmarkFeature);
-}
-
-void BookmarkTracker::OnBookmarkAdded() {
-  GetTracker()->NotifyEvent(events::kBookmarkAdded);
-}
-
-void BookmarkTracker::OnVisitedKnownURL() {
-  if (ShouldShowPromo())
-    ShowPromo();
-}
-
-void BookmarkTracker::OnSessionTimeMet() {
-  GetTracker()->NotifyEvent(events::kBookmarkSessionTimeMet);
-}
-
-void BookmarkTracker::ShowPromo() {
-  // TODO: Call the promo.
-
-  // Clears the flag for whether there is any in-product help being displayed.
-  GetTracker()->Dismissed(kIPHBookmarkFeature);
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h b/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h
deleted file mode 100644
index 3af9ad4..0000000
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_BOOKMARK_BOOKMARK_TRACKER_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_BOOKMARK_BOOKMARK_TRACKER_H_
-
-#include "base/macros.h"
-#include "chrome/browser/feature_engagement/feature_tracker.h"
-
-class Profile;
-
-namespace feature_engagement {
-
-// The BookmarkTracker provides a backend for displaying in-product help for the
-// bookmarks. BookmarkTracker is the interface through which the event
-// constants for the Bookmark feature can be be altered. Once all of the event
-// constants are met, BookmarkTracker calls for the BookmarkPromo to be shown,
-// along with recording when the BookmarkPromo is dismissed. The requirements to
-// show the BookmarkPromo are as follows:
-//
-// - At least five hours of observed session time have elapsed.
-// - The user has never added another bookmark through any means.
-// - The user has navigated to a URL that they have visited at least 3 times.
-// - This URL cannot be the home page or new tab page.
-class BookmarkTracker : public FeatureTracker {
- public:
-  explicit BookmarkTracker(Profile* profile);
-
-  // Alerts the bookmark tracker that a bookmark was added.
-  void OnBookmarkAdded();
-  // Checks if the promo should be displayed since a known URL has been visited.
-  void OnVisitedKnownURL();
-  // Clears the flag for whether there is any in-product help being displayed.
-  void OnPromoClosed();
-
- protected:
-  ~BookmarkTracker() override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(BookmarkTrackerEventTest, TestOnSessionTimeMet);
-  FRIEND_TEST_ALL_PREFIXES(BookmarkTrackerTest, TestShouldNotShowPromo);
-  FRIEND_TEST_ALL_PREFIXES(BookmarkTrackerTest, TestShouldShowPromo);
-
-  // FeatureTracker:
-  void OnSessionTimeMet() override;
-
-  // Shows the Bookmark in-product help promo bubble.
-  void ShowPromo();
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkTracker);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_BOOKMARK_BOOKMARK_TRACKER_H_
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.cc b/chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.cc
deleted file mode 100644
index da024e8e..0000000
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 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/feature_engagement/bookmark/bookmark_tracker_factory.h"
-
-#include "base/memory/singleton.h"
-#include "chrome/browser/feature_engagement/bookmark/bookmark_tracker.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "content/public/browser/browser_context.h"
-
-namespace feature_engagement {
-
-// static
-BookmarkTrackerFactory* BookmarkTrackerFactory::GetInstance() {
-  return base::Singleton<BookmarkTrackerFactory>::get();
-}
-
-BookmarkTracker* BookmarkTrackerFactory::GetForProfile(Profile* profile) {
-  return static_cast<BookmarkTracker*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-BookmarkTrackerFactory::BookmarkTrackerFactory()
-    : BrowserContextKeyedServiceFactory(
-          "BookmarkTracker",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(TrackerFactory::GetInstance());
-}
-
-BookmarkTrackerFactory::~BookmarkTrackerFactory() = default;
-
-KeyedService* BookmarkTrackerFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new BookmarkTracker(Profile::FromBrowserContext(context));
-}
-
-content::BrowserContext* BookmarkTrackerFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  return chrome::GetBrowserContextRedirectedInIncognito(context);
-}
-
-bool BookmarkTrackerFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.h b/chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.h
deleted file mode 100644
index 359c26e8..0000000
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_BOOKMARK_BOOKMARK_TRACKER_FACTORY_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_BOOKMARK_BOOKMARK_TRACKER_FACTORY_H_
-
-#include "base/macros.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace feature_engagement {
-class BookmarkTracker;
-
-// BookmarkTrackerFactory is the main client class for interaction with the
-// BookmarkTracker component.
-class BookmarkTrackerFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns singleton instance of BookmarkTrackerFactory.
-  static BookmarkTrackerFactory* GetInstance();
-
-  // Returns the FeatureEngagementTracker associated with the profile.
-  BookmarkTracker* GetForProfile(Profile* profile);
-
- private:
-  friend struct base::DefaultSingletonTraits<BookmarkTrackerFactory>;
-
-  BookmarkTrackerFactory();
-  ~BookmarkTrackerFactory() override;
-
-  // BrowserContextKeyedServiceFactory overrides:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkTrackerFactory);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_BOOKMARK_BOOKMARK_TRACKER_FACTORY_H_
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc b/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc
deleted file mode 100644
index 59321f01..0000000
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2017 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/feature_engagement/bookmark/bookmark_tracker.h"
-
-#include <memory>
-
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/feature_engagement/feature_tracker.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/feature_engagement/test/mock_tracker.h"
-#include "components/feature_engagement/test/test_tracker.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace feature_engagement {
-
-namespace {
-
-constexpr char kTestProfileName[] = "test-profile";
-
-class FakeBookmarkTracker : public BookmarkTracker {
- public:
-  FakeBookmarkTracker(Tracker* feature_tracker, Profile* profile)
-      : BookmarkTracker(profile),
-        feature_tracker_(feature_tracker),
-        pref_service_(
-            std::make_unique<sync_preferences::TestingPrefServiceSyncable>()) {
-    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
-  }
-
-  PrefService* GetPrefs() { return pref_service_.get(); }
-
-  // feature_engagement::BookmarkTracker:
-  Tracker* GetTracker() const override { return feature_tracker_; }
-
- private:
-  Tracker* const feature_tracker_;
-  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
-      pref_service_;
-};
-
-class BookmarkTrackerEventTest : public testing::Test {
- public:
-  BookmarkTrackerEventTest() = default;
-  ~BookmarkTrackerEventTest() override = default;
-
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-    mock_tracker_ = std::make_unique<testing::StrictMock<test::MockTracker>>();
-    bookmark_tracker_ = std::make_unique<FakeBookmarkTracker>(
-        mock_tracker_.get(),
-        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-  }
-
-  void TearDown() override {
-    bookmark_tracker_->RemoveSessionDurationObserver();
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  std::unique_ptr<test::MockTracker> mock_tracker_;
-  std::unique_ptr<FakeBookmarkTracker> bookmark_tracker_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkTrackerEventTest);
-};
-
-}  // namespace
-
-// Tests to verify FeatureEngagementTracker API boundary expectations:
-
-// If OnBookmarkAdded() is called, the FeatureEngagementTracker
-// receives the kBookmarkAddedEvent.
-TEST_F(BookmarkTrackerEventTest, TestOnBookmarkAdded) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kBookmarkAdded));
-  bookmark_tracker_->OnBookmarkAdded();
-}
-
-// If OnSessionTimeMet() is called, the FeatureEngagementTracker
-// receives the kSessionTime event.
-TEST_F(BookmarkTrackerEventTest, TestOnSessionTimeMet) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kBookmarkSessionTimeMet));
-  bookmark_tracker_->OnSessionTimeMet();
-}
-
-namespace {
-
-class BookmarkTrackerTest : public testing::Test {
- public:
-  BookmarkTrackerTest() {
-    base::FieldTrialParams bookmark_params;
-    bookmark_params["event_bookmark_added"] =
-        "name:bookmark_added;comparator:==0;window:3650;storage:3650";
-    bookmark_params["event_bookmark_session_time_met"] =
-        "name:bookmark_session_time_met;comparator:>=1;window:3650;storage:"
-        "3650";
-    bookmark_params["event_trigger"] =
-        "name:bookmark_trigger;comparator:any;window:3650;storage:3650";
-    bookmark_params["event_used"] =
-        "name:bookmark_clicked;comparator:any;window:3650;storage:3650";
-    bookmark_params["session_rate"] = "<=3";
-    bookmark_params["availability"] = "any";
-    bookmark_params["x_date_released_in_seconds"] =
-        base::NumberToString(static_cast<int64_t>(
-            first_run::GetFirstRunSentinelCreationTime().ToDoubleT()));
-
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(kIPHBookmarkFeature,
-                                                            bookmark_params);
-  }
-  ~BookmarkTrackerTest() override = default;
-
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-
-    feature_engagement_tracker_ = CreateTestTracker();
-
-    bookmark_tracker_ = std::make_unique<FakeBookmarkTracker>(
-        feature_engagement_tracker_.get(),
-        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-    // The feature engagement tracker does async initialization.
-    base::RunLoop().RunUntilIdle();
-    ASSERT_TRUE(feature_engagement_tracker_->IsInitialized());
-  }
-
-  void TearDown() override {
-    bookmark_tracker_->RemoveSessionDurationObserver();
-    testing_profile_manager_->DeleteTestingProfile(kTestProfileName);
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-
-    // This is required to ensure each test can define its own params.
-    base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
-  }
-
- protected:
-  std::unique_ptr<FakeBookmarkTracker> bookmark_tracker_;
-  std::unique_ptr<Tracker> feature_engagement_tracker_;
-
- private:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkTrackerTest);
-};
-
-}  // namespace
-
-// Tests to verify BookmarkFeatureEngagementTracker functional expectations:
-
-// Test that a promo is not shown if the user has a Bookmark. If
-// OnBookmarkAdded() is called, the ShouldShowPromo() should return false.
-TEST_F(BookmarkTrackerTest, TestShouldShowPromo) {
-  EXPECT_FALSE(bookmark_tracker_->ShouldShowPromo());
-
-  bookmark_tracker_->OnSessionTimeMet();
-
-  EXPECT_TRUE(bookmark_tracker_->ShouldShowPromo());
-
-  bookmark_tracker_->OnBookmarkAdded();
-
-  EXPECT_FALSE(bookmark_tracker_->ShouldShowPromo());
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/feature_tracker.cc b/chrome/browser/feature_engagement/feature_tracker.cc
deleted file mode 100644
index 1fbbd637..0000000
--- a/chrome/browser/feature_engagement/feature_tracker.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2017 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/feature_engagement/feature_tracker.h"
-
-#include "base/files/file_util.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-
-namespace {
-constexpr double kTwentyFourHoursInSeconds = 86400;
-}
-
-namespace feature_engagement {
-
-FeatureTracker::FeatureTracker(
-    Profile* profile,
-    const base::Feature* feature,
-    const char* observed_session_time_dict_key,
-    base::TimeDelta default_time_required_to_show_promo)
-    : profile_(profile),
-      session_duration_updater_(profile->GetPrefs(),
-                                observed_session_time_dict_key),
-      session_duration_observer_(this),
-      feature_(feature),
-      field_trial_time_delta_(default_time_required_to_show_promo) {
-  if (!HasEnoughSessionTimeElapsed(
-          session_duration_updater_.GetCumulativeElapsedSessionTime())) {
-    AddSessionDurationObserver();
-  }
-}
-
-FeatureTracker::~FeatureTracker() = default;
-
-void FeatureTracker::AddSessionDurationObserver() {
-  session_duration_observer_.Add(&session_duration_updater_);
-}
-
-void FeatureTracker::RemoveSessionDurationObserver() {
-  session_duration_observer_.Remove(&session_duration_updater_);
-}
-
-bool FeatureTracker::IsObserving() {
-  return session_duration_observer_.IsObserving(&session_duration_updater_);
-}
-
-bool FeatureTracker::ShouldShowPromo() {
-  if (IsObserving()) {
-    NotifyAndRemoveSessionDurationObserverIfSessionTimeMet(
-        session_duration_updater_.GetCumulativeElapsedSessionTime());
-  }
-
-  return IsNewUser() ? GetTracker()->ShouldTriggerHelpUI(*feature_) : false;
-}
-
-Tracker* FeatureTracker::GetTracker() const {
-  return TrackerFactory::GetForBrowserContext(profile_);
-}
-
-void FeatureTracker::OnSessionEnded(base::TimeDelta total_session_time) {
-  NotifyAndRemoveSessionDurationObserverIfSessionTimeMet(total_session_time);
-}
-
-base::TimeDelta FeatureTracker::GetSessionTimeRequiredToShow() {
-  if (!has_retrieved_field_trial_minutes_) {
-    has_retrieved_field_trial_minutes_ = true;
-    std::string field_trial_string_value =
-        base::GetFieldTrialParamValueByFeature(*feature_, "x_minutes");
-    int field_trial_int_value;
-    if (base::StringToInt(field_trial_string_value, &field_trial_int_value)) {
-      field_trial_time_delta_ =
-          base::TimeDelta::FromMinutes(field_trial_int_value);
-    }
-  }
-  return field_trial_time_delta_;
-}
-
-void FeatureTracker::NotifyAndRemoveSessionDurationObserverIfSessionTimeMet(
-    base::TimeDelta total_session_time) {
-  if (has_session_time_been_met_ ||
-      !HasEnoughSessionTimeElapsed(total_session_time)) {
-    return;
-  }
-
-  has_session_time_been_met_ = true;
-  OnSessionTimeMet();
-  RemoveSessionDurationObserver();
-}
-
-bool FeatureTracker::HasEnoughSessionTimeElapsed(
-    base::TimeDelta total_session_time) {
-  return total_session_time.InSeconds() >=
-         GetSessionTimeRequiredToShow().InSeconds();
-}
-
-bool FeatureTracker::IsNewUser() {
-  // Gets the date in seconds since epoch the experiment was released.
-  const std::string date_released_string_value =
-      base::GetFieldTrialParamValueByFeature(*feature_,
-                                             "x_date_released_in_seconds");
-  int64_t date_released_int64_value;
-  // If the date release string value is incorrect and it's not for testing,
-  // directly return false.
-  if (!base::StringToInt64(date_released_string_value,
-                           &date_released_int64_value)) {
-    if (use_default_for_chrome_variation_configuration_release_time_for_testing_) {
-      date_released_int64_value = base::Time().ToDoubleT();
-    } else {
-      return false;
-    }
-  }
-
-  base::TimeDelta new_user_threshold =
-      base::TimeDelta::FromSeconds(kTwentyFourHoursInSeconds);
-  // Gets the date in seconds the experiment was released.
-  const std::string new_user_threshold_string_value =
-      base::GetFieldTrialParamValueByFeature(
-          *feature_, "x_new_user_creation_time_threshold_in_seconds");
-  int64_t new_user_threshold_int64_value;
-  // If the threshold string value is incorrect, return false.
-  if (base::StringToInt64(new_user_threshold_string_value,
-                          &new_user_threshold_int64_value)) {
-    new_user_threshold =
-        base::TimeDelta::FromSeconds(new_user_threshold_int64_value);
-  } else if (!new_user_threshold_string_value.empty()) {
-    return false;
-  }
-
-  // We consider a new user only if the first run sentinel has been created no
-  // more than 24 hours before the date released.
-  return (base::Time::FromDoubleT(date_released_int64_value) -
-          first_run::GetFirstRunSentinelCreationTime()) <= new_user_threshold;
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/feature_tracker.h b/chrome/browser/feature_engagement/feature_tracker.h
deleted file mode 100644
index bf7b278..0000000
--- a/chrome/browser/feature_engagement/feature_tracker.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
-
-#include "base/feature_list.h"
-#include "base/scoped_observer.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/core/keyed_service.h"
-
-namespace feature_engagement {
-
-class Tracker;
-
-// The FeatureTracker provides a backend for displaying in-product help for the
-// various features by collecting all common functionality. All subclasses of
-// FeatureTracker's factories depend on
-// SessionDurationUpdaterFactory::GetInstance() as SessionDurationUpdater is
-// responsible for letting all FeatureTrackers know how much active session time
-// has passed.
-//
-// SessionDurationUpdater keeps track of the observed session time and, upon
-// each session ending, updates all of the FeatureTrackers with the new total
-// observed session time. Once the observed session time exceeds the time
-// requirement provided by GetSessionTimeRequiredToShow(), the
-// FeatureTracker unregisters itself as an observer of SessionDurationUpdater.
-// SessionDurationUpdater stops updating the observed session time if no
-// features are observing it, and will start tracking the observed session time
-// again if another feature is added as an observer later.
-//
-// Desktop In Product Help only shows promos to new users which means that
-// if the user is not considered new based on the chrome variations
-// configuration, the promo bubble will not show.
-class FeatureTracker : public SessionDurationUpdater::Observer,
-                       public KeyedService {
- public:
-  FeatureTracker(Profile* profile,
-                 const base::Feature* feature,
-                 const char* observed_session_time_dict_key,
-                 base::TimeDelta defaultTimeRequiredToShowPromo);
-
-  // Adds the SessionDurationUpdater observer.
-  void AddSessionDurationObserver();
-  // Removes the SessionDurationUpdater observer.
-  void RemoveSessionDurationObserver();
-
-  // Returns the whether |session_duration_observer_| is observing sources for
-  // testing purposes.
-  bool IsObserving();
-
-  // SessionDurationUpdater::Observer:
-  void OnSessionEnded(base::TimeDelta total_session_time) override;
-
-  // Returns if a user is new, whether or not the promo should be displayed.
-  bool ShouldShowPromo();
-
-  void UseDefaultForChromeVariationConfigurationReleaseTimeForTesting() {
-    use_default_for_chrome_variation_configuration_release_time_for_testing_ =
-        true;
-  }
-
- protected:
-  ~FeatureTracker() override;
-
-  // Alerts the feature tracker that the session time is up.
-  virtual void OnSessionTimeMet() = 0;
-  // Returns the Tracker associated with this FeatureTracker.
-  virtual Tracker* GetTracker() const;
-
-  // Returns the required session time in minutes for the FeatureTracker's
-  // subclass to show its promo.
-  base::TimeDelta GetSessionTimeRequiredToShow();
-
-  // Whether the user has been created at least 24 hours before the chrome
-  // variations configuration.
-  bool IsNewUser();
-
- private:
-  // Notifies In-Product Help and removes the session duration obverser if the
-  // session time requirement has been met for the feature.
-  void NotifyAndRemoveSessionDurationObserverIfSessionTimeMet(
-      base::TimeDelta total_session_time);
-
-  // Returns whether the active session time of a user has elapsed more than the
-  // required active session time for the feature.
-  bool HasEnoughSessionTimeElapsed(base::TimeDelta total_session_time);
-
-  // Owned by the ProfileManager.
-  Profile* const profile_;
-
-  // Tracks the elapsed session time while |feature_| is active.
-  SessionDurationUpdater session_duration_updater_;
-
-  // Observes the SessionDurationUpdater and notifies when a desktop session
-  // starts and ends.
-  ScopedObserver<SessionDurationUpdater, SessionDurationUpdater::Observer>
-      session_duration_observer_;
-
-  // IPH Feature that the tracker is tracking.
-  const base::Feature* const feature_;
-
-  // "x_minutes" param value from the field trial.
-  base::TimeDelta field_trial_time_delta_;
-
-  // Whether the "x_minutes" param value has already been retrieved to prevent
-  // reading from the field trial multiple times for the same param.
-  bool has_retrieved_field_trial_minutes_ = false;
-
-  // Whether the "x_session_time" requirement has already been met.
-  bool has_session_time_been_met_ = false;
-
-  bool
-      use_default_for_chrome_variation_configuration_release_time_for_testing_ =
-          false;
-
-  DISALLOW_COPY_AND_ASSIGN(FeatureTracker);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
diff --git a/chrome/browser/feature_engagement/feature_tracker_unittest.cc b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
deleted file mode 100644
index ff2a13e..0000000
--- a/chrome/browser/feature_engagement/feature_tracker_unittest.cc
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright 2017 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/feature_engagement/feature_tracker.h"
-
-#include <memory>
-
-#include "base/files/file_util.h"
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/time/time.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace feature_engagement {
-
-namespace {
-
-constexpr int kTestTimeDeltaInMinutes = 100;
-constexpr int kTestTimeSufficentInMinutes = 110;
-constexpr int kTestTimeInsufficientInMinutes = 90;
-constexpr char kTestProfileName[] = "test-profile";
-constexpr char kTestObservedSessionTimeKey[] = "test_observed_session_time_key";
-
-class TestFeatureTracker : public FeatureTracker {
- public:
-  explicit TestFeatureTracker(Profile* profile)
-      : FeatureTracker(profile,
-                       &kIPHNewTabFeature,
-                       kTestObservedSessionTimeKey,
-                       base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes)),
-        pref_service_(
-            std::make_unique<sync_preferences::TestingPrefServiceSyncable>()) {
-    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
-  }
-
-  base::TimeDelta GetSessionTimeRequiredToShowWrapper() {
-    return GetSessionTimeRequiredToShow();
-  }
-
-  bool IsNewUserWrapper() { return IsNewUser(); }
-
-  void OnSessionTimeMet() override {}
-
- private:
-  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
-      pref_service_;
-};
-
-class MockTestFeatureTracker : public TestFeatureTracker {
- public:
-  explicit MockTestFeatureTracker(Profile* profile)
-      : TestFeatureTracker(profile) {}
-  MOCK_METHOD0(OnSessionTimeMet, void());
-};
-
-class FeatureTrackerTest : public testing::Test {
- public:
-  FeatureTrackerTest() = default;
-  ~FeatureTrackerTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-    mock_feature_tracker_ =
-        std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-            testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-  }
-
-  void TearDown() override {
-    // Need to invoke the reset method as TearDown is on the UI thread.
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(FeatureTrackerTest);
-};
-
-// If OnSessionEnded parameter is greater than HasEnoughSessionTimeElapsed
-// then OnSessionTimeMet should be called.
-//
-// Note: in this case, RemoveSessionDurationObserver is called inside of
-// OnSessionTimeMet, so it doesn't need to be called after the fact.
-TEST_F(FeatureTrackerTest, TestExpectOnSessionTimeMet) {
-  EXPECT_CALL(*mock_feature_tracker_, OnSessionTimeMet());
-  mock_feature_tracker_.get()->OnSessionEnded(
-      base::TimeDelta::FromMinutes(kTestTimeSufficentInMinutes));
-}
-
-// If OnSessionEnded parameter is less than than HasEnoughSessionTimeElapsed
-// then OnSessionTimeMet should not be called.
-TEST_F(FeatureTrackerTest, TestDontExpectOnSessionTimeMet) {
-  mock_feature_tracker_.get()->OnSessionEnded(
-      base::TimeDelta::FromMinutes(kTestTimeInsufficientInMinutes));
-  mock_feature_tracker_.get()->RemoveSessionDurationObserver();
-}
-
-// The FeatureTracker should be observing sources until the
-// RemoveSessionDurationObserver is called.
-TEST_F(FeatureTrackerTest, TestAddAndRemoveObservers) {
-  // AddSessionDurationObserver is called on initialization.
-  ASSERT_TRUE(mock_feature_tracker_->IsObserving());
-
-  mock_feature_tracker_.get()->RemoveSessionDurationObserver();
-
-  EXPECT_FALSE(mock_feature_tracker_->IsObserving());
-}
-
-class FeatureTrackerParamsTest : public testing::Test {
- public:
-  FeatureTrackerParamsTest() = default;
-  ~FeatureTrackerParamsTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-  }
-
-  void TearDown() override {
-    // Need to invoke the rest method as TearDown is on the UI thread.
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
-  void SetFeatureParams(const base::Feature& feature,
-                        const FieldTrialParams& params) {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(kIPHNewTabFeature,
-                                                            params);
-  }
-
- protected:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(FeatureTrackerParamsTest);
-};
-
-// Test that session time defaults to the time in the constructor if there is no
-// field param value.
-TEST_F(FeatureTrackerParamsTest, TestSessionTimeWithNoFieldTrialValue) {
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_EQ(mock_feature_tracker->GetSessionTimeRequiredToShowWrapper(),
-            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes));
-
-  mock_feature_tracker.get()->RemoveSessionDurationObserver();
-}
-
-// Test that session time defaults to the valid time from the field param value.
-TEST_F(FeatureTrackerParamsTest, TestSessionTimeWithValidFieldTrialValue) {
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_minutes"] = "1";
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_EQ(mock_feature_tracker->GetSessionTimeRequiredToShowWrapper(),
-            base::TimeDelta::FromMinutes(1));
-
-  mock_feature_tracker.get()->RemoveSessionDurationObserver();
-}
-
-// Test that session time defaults to the time in the constructor if the field
-// param value is empty string.
-TEST_F(FeatureTrackerParamsTest, TestSessionTimeWithEmptyFieldTrialValue) {
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_minutes"] = "";
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_EQ(mock_feature_tracker->GetSessionTimeRequiredToShowWrapper(),
-            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes));
-
-  mock_feature_tracker.get()->RemoveSessionDurationObserver();
-}
-
-// Test that session time defaults to the time in the constructor if the field
-// param value is invalid.
-TEST_F(FeatureTrackerParamsTest, TestSessionTimeWithInvalidFieldTrialValue) {
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_minutes"] = "12g4";
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_EQ(mock_feature_tracker->GetSessionTimeRequiredToShowWrapper(),
-            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes));
-
-  mock_feature_tracker.get()->RemoveSessionDurationObserver();
-}
-
-// Test that the user is new if the creation time of the first run sentinel is
-// after the enabled time of the experiment.
-TEST_F(FeatureTrackerParamsTest, TestIsNewUser_DefaultTime) {
-  // Setting the experiment timestamp equal to the first run sentinel timestamp.
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_date_released_in_seconds"] =
-      base::NumberToString(static_cast<int64_t>(
-          first_run::GetFirstRunSentinelCreationTime().ToDoubleT()));
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_TRUE(mock_feature_tracker->IsNewUserWrapper());
-}
-
-// Test that the user is not considered a new user if the creation time is more
-// than 24 hours ago.
-TEST_F(FeatureTrackerParamsTest, TestIsNotNewUser_DefaultTime) {
-  // Setting the experiment timestamp equal to one second older than what is
-  // considered a new user.
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_date_released_in_seconds"] =
-      base::NumberToString(static_cast<int64_t>(
-          first_run::GetFirstRunSentinelCreationTime().ToDoubleT() +
-          base::TimeDelta::FromHours(24).InSeconds() + 1));
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_FALSE(mock_feature_tracker->IsNewUserWrapper());
-}
-
-// Test that the user is not considered a new user if the creation time is more
-// than the custom time threshold limit which in this case is 28 hours ago.
-TEST_F(FeatureTrackerParamsTest, TestIsNewUser_CustomTime) {
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_new_user_creation_time_threshold_in_seconds"] =
-      base::NumberToString(base::TimeDelta::FromHours(28).InSeconds());
-
-  // Setting the experiment timestamp equal to the limit of what is considered a
-  // new user.
-  new_tab_params["x_date_released_in_seconds"] =
-      base::NumberToString(static_cast<int64_t>(
-          first_run::GetFirstRunSentinelCreationTime().ToDoubleT()));
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_TRUE(mock_feature_tracker->IsNewUserWrapper());
-}
-
-// Test that the user is not considered a new user if the creation time is more
-// than the custom time threshold limit which in this case is 28 hours ago.
-TEST_F(FeatureTrackerParamsTest, TestIsNotNewUser_CustomTime) {
-  std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_new_user_creation_time_threshold_in_seconds"] =
-      base::NumberToString(base::TimeDelta::FromHours(28).InSeconds());
-
-  // Setting the experiment timestamp equal to one second older than what is
-  // considered a new user.
-  new_tab_params["x_date_released_in_seconds"] =
-      base::NumberToString(static_cast<int64_t>(
-          first_run::GetFirstRunSentinelCreationTime().ToDoubleT() +
-          base::TimeDelta::FromHours(28).InSeconds() + 1));
-  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
-
-  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker =
-      std::make_unique<testing::StrictMock<MockTestFeatureTracker>>(
-          testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-  EXPECT_FALSE(mock_feature_tracker->IsNewUserWrapper());
-}
-
-}  // namespace
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.cc b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.cc
deleted file mode 100644
index 52d81584..0000000
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2017 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/feature_engagement/incognito_window/incognito_window_tracker.h"
-
-#include <string>
-
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/in_product_help/in_product_help.h"
-#include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
-#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/variations/variations_associated_data.h"
-
-namespace {
-
-constexpr int kDefaultIncognitoWindowPromoShowTimeInHours = 2;
-constexpr char kIncognitoWindowObservedSessionTimeKey[] =
-    "incognito_window_in_product_help_observed_session_time_key";
-
-// Note: May return null.
-BrowserAppMenuButton* GetAppMenuButton() {
-  auto* browser = BrowserView::GetBrowserViewForBrowser(
-      BrowserList::GetInstance()->GetLastActive());
-  DCHECK(browser);
-  DCHECK(browser->IsActive());
-  DCHECK(browser->toolbar());
-  return browser->toolbar()->app_menu_button();
-}
-
-int GetPromoStringSpecifier() {
-  static constexpr int kTextIds[] = {
-      IDS_INCOGNITOWINDOW_PROMO_0, IDS_INCOGNITOWINDOW_PROMO_1,
-      IDS_INCOGNITOWINDOW_PROMO_2, IDS_INCOGNITOWINDOW_PROMO_3};
-  const std::string& str = variations::GetVariationParamValue(
-      "IncognitoWindowInProductHelp", "x_promo_string");
-  size_t text_specifier;
-  if (!base::StringToSizeT(str, &text_specifier) ||
-      text_specifier >= base::size(kTextIds)) {
-    text_specifier = 0;
-  }
-
-  return kTextIds[text_specifier];
-}
-
-}  // namespace
-
-namespace feature_engagement {
-
-IncognitoWindowTracker::IncognitoWindowTracker(Profile* profile)
-    : FeatureTracker(profile,
-                     &kIPHIncognitoWindowFeature,
-                     kIncognitoWindowObservedSessionTimeKey,
-                     base::TimeDelta::FromHours(
-                         kDefaultIncognitoWindowPromoShowTimeInHours)),
-      incognito_promo_observer_(this) {}
-
-IncognitoWindowTracker::~IncognitoWindowTracker() = default;
-
-void IncognitoWindowTracker::OnIncognitoWindowOpened() {
-  GetTracker()->NotifyEvent(events::kIncognitoWindowOpened);
-}
-
-void IncognitoWindowTracker::OnBrowsingDataCleared() {
-  auto* app_menu_button = GetAppMenuButton();
-  if (!app_menu_button)
-    return;
-
-  const auto severity = app_menu_button->severity();
-  if (severity == AppMenuIconController::Severity::NONE && ShouldShowPromo())
-    ShowPromo();
-}
-
-void IncognitoWindowTracker::OnPromoClosed() {
-  GetTracker()->Dismissed(kIPHIncognitoWindowFeature);
-}
-
-void IncognitoWindowTracker::OnSessionTimeMet() {
-  GetTracker()->NotifyEvent(events::kIncognitoWindowSessionTimeMet);
-}
-
-void IncognitoWindowTracker::ShowPromo() {
-  DCHECK(!incognito_promo_);
-  auto* app_menu_button = GetAppMenuButton();
-
-  // Owned by its native widget. Will be destroyed when its widget is destroyed.
-  incognito_promo_ = FeaturePromoBubbleView::CreateOwned(
-      app_menu_button, views::BubbleBorder::TOP_RIGHT,
-      FeaturePromoBubbleView::ActivationAction::ACTIVATE,
-      /*title_string_specifier=*/base::nullopt, GetPromoStringSpecifier());
-  views::Widget* widget = incognito_promo_->GetWidget();
-  incognito_promo_observer_.Add(widget);
-  app_menu_button->SetPromoFeature(InProductHelpFeature::kIncognitoWindow);
-}
-
-void IncognitoWindowTracker::OnWidgetDestroying(views::Widget* widget) {
-  OnPromoClosed();
-
-  if (incognito_promo_observer_.IsObserving(widget)) {
-    incognito_promo_observer_.Remove(widget);
-    BrowserAppMenuButton* app_menu_button = GetAppMenuButton();
-    if (app_menu_button)
-      app_menu_button->SetPromoFeature(base::nullopt);
-  }
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h
deleted file mode 100644
index 63f924e..0000000
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_INCOGNITO_WINDOW_INCOGNITO_WINDOW_TRACKER_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_INCOGNITO_WINDOW_INCOGNITO_WINDOW_TRACKER_H_
-
-#include "chrome/browser/feature_engagement/feature_tracker.h"
-
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "ui/views/widget/widget_observer.h"
-
-class FeaturePromoBubbleView;
-
-namespace feature_engagement {
-
-// The IncognitoWindowTracker provides a backend for displaying in-product help
-// for the incognito window. IncognitoWindowTracker is the interface through
-// which the event constants for the IncognitoWindow feature can be be altered.
-// Once all of the event constants are met, IncognitoWindowTracker calls for the
-// IncognitoWindowPromo to be shown, along with recording when the
-// IncognitoWindowPromo is dismissed. The requirements to show the
-// IncognitoWindowPromo are as follows:
-//
-// - At least two hours of observed session time have elapsed.
-// - The user has never opened incognito window through any means.
-// - The user has cleared browsing data.
-class IncognitoWindowTracker : public FeatureTracker,
-                               public views::WidgetObserver {
- public:
-  explicit IncognitoWindowTracker(Profile* profile);
-
-  // Alerts the incognito window tracker that an incognito window was opened.
-  void OnIncognitoWindowOpened();
-  // Alerts the incognito window tracker that browsing history was deleted.
-  void OnBrowsingDataCleared();
-  // Clears the flag for whether there is any in-product help being displayed.
-  void OnPromoClosed();
-  // Shows |incognito_promo_|.
-  void ShowPromo();
-
- protected:
-  ~IncognitoWindowTracker() override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(IncognitoWindowTrackerBrowserTest, ShowPromo);
-  FRIEND_TEST_ALL_PREFIXES(IncognitoWindowTrackerEventTest,
-                           TestOnSessionTimeMet);
-  FRIEND_TEST_ALL_PREFIXES(IncognitoWindowTrackerTest, TestShouldNotShowPromo);
-  FRIEND_TEST_ALL_PREFIXES(IncognitoWindowTrackerTest, TestShouldShowPromo);
-
-  // views::WidgetObserver:
-  void OnWidgetDestroying(views::Widget* widget) override;
-
-  FeaturePromoBubbleView* incognito_promo() { return incognito_promo_; }
-
-  // FeatureTracker:
-  void OnSessionTimeMet() override;
-
-  // Promotional UI that appears next to the AppMenuButton and encourages its
-  // use. Owned by its NativeWidget.
-  FeaturePromoBubbleView* incognito_promo_ = nullptr;
-
-  // Observes the |incognito_promo_|'s Widget. Used to tell whether the promo
-  // is open and is called back when it closes.
-  ScopedObserver<views::Widget, WidgetObserver> incognito_promo_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(IncognitoWindowTracker);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_INCOGNITO_WINDOW_INCOGNITO_WINDOW_TRACKER_H_
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_browsertest.cc b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_browsertest.cc
deleted file mode 100644
index c0fbc2c..0000000
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_browsertest.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2017 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/feature_engagement/incognito_window/incognito_window_tracker.h"
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
-#include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/interactive_test_utils.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/feature_engagement/test/mock_tracker.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/browser/web_ui.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "ui/views/widget/widget.h"
-#include "url/gurl.h"
-
-namespace feature_engagement {
-namespace {
-
-MATCHER_P(IsFeature, feature, "") {
-  return arg.name == feature.name;
-}
-
-std::unique_ptr<KeyedService> BuildTestTrackerFactory(
-    content::BrowserContext* context) {
-  return std::make_unique<testing::StrictMock<test::MockTracker>>();
-}
-
-// Set up a test profile for the incognito window In-Product Help (IPH)
-// tracker.
-class IncognitoWindowTrackerBrowserTest : public InProcessBrowserTest {
- public:
-  IncognitoWindowTrackerBrowserTest() = default;
-  ~IncognitoWindowTrackerBrowserTest() override = default;
-
-  void SetUpOnMainThread() override {
-    TrackerFactory::GetInstance()->SetTestingFactoryAndUse(
-        browser()->profile(), base::BindRepeating(&BuildTestTrackerFactory));
-
-    // Ensure all initialization is finished.
-    base::RunLoop().RunUntilIdle();
-
-    feature_engagement_tracker_ = static_cast<test::MockTracker*>(
-        TrackerFactory::GetForBrowserContext(browser()->profile()));
-
-    EXPECT_CALL(*feature_engagement_tracker_, IsInitialized())
-        .WillOnce(::testing::Return(true));
-
-    ASSERT_TRUE(TrackerFactory::GetForBrowserContext(browser()->profile())
-                    ->IsInitialized());
-  }
-
- protected:
-  // Owned by the Profile.
-  test::MockTracker* feature_engagement_tracker_ = nullptr;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IncognitoWindowTrackerBrowserTest);
-};
-
-}  // namespace
-
-// Test that after meeting all the requirements, the incognito window
-// In-Product Help (IPH) promo is visible.
-IN_PROC_BROWSER_TEST_F(IncognitoWindowTrackerBrowserTest, ShowPromo) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  // Bypass the 2 hour active session time requirement.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              NotifyEvent(events::kIncognitoWindowSessionTimeMet));
-  auto* incognito_window_tracker =
-      IncognitoWindowTrackerFactory::GetInstance()->GetForProfile(
-          browser()->profile());
-
-  incognito_window_tracker->OnSessionTimeMet();
-
-  incognito_window_tracker
-      ->UseDefaultForChromeVariationConfigurationReleaseTimeForTesting();
-
-  // Set up feature engagement ShouldTriggerHelpUI mock.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              ShouldTriggerHelpUI(IsFeature(kIPHIncognitoWindowFeature)))
-      .WillOnce(::testing::Return(true))
-      .WillRepeatedly(::testing::Return(false));
-
-  EXPECT_CALL(*feature_engagement_tracker_,
-              GetTriggerState(IsFeature(kIPHIncognitoWindowFeature)))
-      .WillRepeatedly(
-          ::testing::Return(Tracker::TriggerState::HAS_NOT_BEEN_DISPLAYED));
-
-  ui_test_utils::NavigateToURL(browser(), GURL("chrome://settings"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  content::WebUI* web_ui = web_contents->GetWebUI();
-
-  auto handler_owner =
-      std::make_unique<settings::ClearBrowsingDataHandler>(web_ui);
-  settings::ClearBrowsingDataHandler* handler = handler_owner.get();
-  web_ui->AddMessageHandler(std::move(handler_owner));
-  handler->AllowJavascriptForTesting();
-  handler->HandleClearBrowsingDataForTest();
-
-  auto* widget = incognito_window_tracker->incognito_promo()->GetWidget();
-
-  EXPECT_TRUE(widget->IsVisible());
-
-  // Tracker::Dismissed() must be invoked when the promo is closed. This will
-  // clear the flag for whether there is any in-product help being displayed.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              Dismissed(IsFeature(kIPHIncognitoWindowFeature)));
-
-  widget->Close();
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.cc b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.cc
deleted file mode 100644
index b0b8163..0000000
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 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/feature_engagement/incognito_window/incognito_window_tracker_factory.h"
-
-#include "base/memory/singleton.h"
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "content/public/browser/browser_context.h"
-
-namespace feature_engagement {
-
-// static
-IncognitoWindowTrackerFactory* IncognitoWindowTrackerFactory::GetInstance() {
-  return base::Singleton<IncognitoWindowTrackerFactory>::get();
-}
-
-IncognitoWindowTracker* IncognitoWindowTrackerFactory::GetForProfile(
-    Profile* profile) {
-  return static_cast<IncognitoWindowTracker*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-IncognitoWindowTrackerFactory::IncognitoWindowTrackerFactory()
-    : BrowserContextKeyedServiceFactory(
-          "IncognitoWindowTracker",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(TrackerFactory::GetInstance());
-}
-
-IncognitoWindowTrackerFactory::~IncognitoWindowTrackerFactory() = default;
-
-KeyedService* IncognitoWindowTrackerFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new IncognitoWindowTracker(Profile::FromBrowserContext(context));
-}
-
-content::BrowserContext* IncognitoWindowTrackerFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  return chrome::GetBrowserContextRedirectedInIncognito(context);
-}
-
-bool IncognitoWindowTrackerFactory::ServiceIsCreatedWithBrowserContext() const {
-  // Start IncognitoWindowTracker early so the incognito window in-product help
-  // starts tracking.
-  return true;
-}
-
-bool IncognitoWindowTrackerFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h
deleted file mode 100644
index 0031896..0000000
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_INCOGNITO_WINDOW_INCOGNITO_WINDOW_TRACKER_FACTORY_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_INCOGNITO_WINDOW_INCOGNITO_WINDOW_TRACKER_FACTORY_H_
-
-#include "base/macros.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace feature_engagement {
-class IncognitoWindowTracker;
-
-// IncognitoWindowTrackerFactory is the main client class for interaction with
-// the IncognitoWindowTracker component.
-class IncognitoWindowTrackerFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns singleton instance of IncognitoWindowTrackerFactory.
-  static IncognitoWindowTrackerFactory* GetInstance();
-
-  // Returns the FeatureEngagementTracker associated with the profile.
-  IncognitoWindowTracker* GetForProfile(Profile* profile);
-
- private:
-  friend struct base::DefaultSingletonTraits<IncognitoWindowTrackerFactory>;
-
-  IncognitoWindowTrackerFactory();
-  ~IncognitoWindowTrackerFactory() override;
-
-  // BrowserContextKeyedServiceFactory overrides:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(IncognitoWindowTrackerFactory);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_INCOGNITO_WINDOW_INCOGNITO_WINDOW_TRACKER_FACTORY_H_
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc
deleted file mode 100644
index 43e30b41..0000000
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2017 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/feature_engagement/incognito_window/incognito_window_tracker.h"
-
-#include <memory>
-
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/feature_engagement/feature_tracker.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/feature_engagement/test/mock_tracker.h"
-#include "components/feature_engagement/test/test_tracker.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace feature_engagement {
-
-namespace {
-
-constexpr char kTestProfileName[] = "test-profile";
-
-class FakeIncognitoWindowTracker : public IncognitoWindowTracker {
- public:
-  FakeIncognitoWindowTracker(Tracker* feature_tracker, Profile* profile)
-      : IncognitoWindowTracker(profile),
-        feature_tracker_(feature_tracker),
-        pref_service_(
-            std::make_unique<sync_preferences::TestingPrefServiceSyncable>()) {
-    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
-  }
-
-  PrefService* GetPrefs() { return pref_service_.get(); }
-
-  // feature_engagement::IncognitoWindowTracker:
-  Tracker* GetTracker() const override { return feature_tracker_; }
-
- private:
-  Tracker* const feature_tracker_;
-  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
-      pref_service_;
-};
-
-class IncognitoWindowTrackerEventTest : public testing::Test {
- public:
-  IncognitoWindowTrackerEventTest() = default;
-  ~IncognitoWindowTrackerEventTest() override = default;
-
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-    mock_tracker_ = std::make_unique<testing::StrictMock<test::MockTracker>>();
-    incognito_window_tracker_ = std::make_unique<FakeIncognitoWindowTracker>(
-        mock_tracker_.get(),
-        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-  }
-
-  void TearDown() override {
-    incognito_window_tracker_->RemoveSessionDurationObserver();
-    // Need to invoke the reset method as TearDown is on the UI thread.
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  std::unique_ptr<test::MockTracker> mock_tracker_;
-  std::unique_ptr<FakeIncognitoWindowTracker> incognito_window_tracker_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(IncognitoWindowTrackerEventTest);
-};
-
-}  // namespace
-
-// Tests to verify FeatureEngagementTracker API boundary expectations:
-
-// If OnIncognitoWindowOpened() is called, the FeatureEngagementTracker
-// receives the kIncognitoWindowOpened.
-TEST_F(IncognitoWindowTrackerEventTest, TestOnIncognitoWindowOpened) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kIncognitoWindowOpened));
-  incognito_window_tracker_->OnIncognitoWindowOpened();
-}
-
-// If OnSessionTimeMet() is called, the FeatureEngagementTracker
-// receives the kSessionTime event.
-TEST_F(IncognitoWindowTrackerEventTest, TestOnSessionTimeMet) {
-  EXPECT_CALL(*mock_tracker_,
-              NotifyEvent(events::kIncognitoWindowSessionTimeMet));
-  incognito_window_tracker_->OnSessionTimeMet();
-}
-
-namespace {
-
-class IncognitoWindowTrackerTest : public testing::Test {
- public:
-  IncognitoWindowTrackerTest() {
-    base::FieldTrialParams incognito_window_params;
-    incognito_window_params["event_incognito_window_opened"] =
-        "name:incognito_window_opened;comparator:==0;window:3650;storage:3650";
-    incognito_window_params["event_incognito_window_session_time_met"] =
-        "name:incognito_window_session_time_met;comparator:>=1;window:3650;"
-        "storage:3650";
-    incognito_window_params["event_trigger"] =
-        "name:incognito_window_trigger;comparator:==0;window:3650;storage:3650";
-    incognito_window_params["event_used"] =
-        "name:incognito_window_clicked;comparator:any;window:3650;storage:3650";
-    incognito_window_params["session_rate"] = "<=3";
-    incognito_window_params["availability"] = "any";
-    incognito_window_params["x_date_released_in_seconds"] =
-        base::NumberToString(static_cast<int64_t>(
-            first_run::GetFirstRunSentinelCreationTime().ToDoubleT()));
-
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        kIPHIncognitoWindowFeature, incognito_window_params);
-  }
-  ~IncognitoWindowTrackerTest() override = default;
-
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-
-    feature_engagement_tracker_ = CreateTestTracker();
-
-    incognito_window_tracker_ = std::make_unique<FakeIncognitoWindowTracker>(
-        feature_engagement_tracker_.get(),
-        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-    // The feature engagement tracker does async initialization.
-    base::RunLoop().RunUntilIdle();
-    ASSERT_TRUE(feature_engagement_tracker_->IsInitialized());
-  }
-
-  void TearDown() override {
-    incognito_window_tracker_->RemoveSessionDurationObserver();
-    testing_profile_manager_->DeleteTestingProfile(kTestProfileName);
-    // Need to invoke the reset method as TearDown is on the UI thread.
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<FakeIncognitoWindowTracker> incognito_window_tracker_;
-  std::unique_ptr<Tracker> feature_engagement_tracker_;
-
- private:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(IncognitoWindowTrackerTest);
-};
-
-}  // namespace
-
-// Tests to verify IncognitoWindowFeatureEngagementTracker functional
-// expectations:
-
-// Test that a promo is not shown if the user has not opened Incognito Window.
-// If OnIncognitoWindowOpened() is called, the ShouldShowPromo() should return
-// false.
-TEST_F(IncognitoWindowTrackerTest, TestShouldShowPromo) {
-  EXPECT_FALSE(incognito_window_tracker_->ShouldShowPromo());
-
-  incognito_window_tracker_->OnSessionTimeMet();
-
-  EXPECT_TRUE(incognito_window_tracker_->ShouldShowPromo());
-
-  incognito_window_tracker_->OnIncognitoWindowOpened();
-
-  EXPECT_FALSE(incognito_window_tracker_->ShouldShowPromo());
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
deleted file mode 100644
index 789aea9b..0000000
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 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/feature_engagement/new_tab/new_tab_tracker.h"
-
-#include "base/time/time.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/ui/views/tabs/new_tab_button.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-
-namespace {
-
-const int kDefaultNewTabPromoShowTimeInHours = 2;
-constexpr char kNewTabObservedSessionTimeKey[] =
-    "new_tab_in_product_help_observed_session_time_key";
-
-}  // namespace
-
-namespace feature_engagement {
-
-NewTabTracker::NewTabTracker(Profile* profile)
-    : FeatureTracker(
-          profile,
-          &kIPHNewTabFeature,
-          kNewTabObservedSessionTimeKey,
-          base::TimeDelta::FromHours(kDefaultNewTabPromoShowTimeInHours)) {}
-
-NewTabTracker::~NewTabTracker() = default;
-
-void NewTabTracker::OnNewTabOpened() {
-  GetTracker()->NotifyEvent(events::kNewTabOpened);
-}
-
-void NewTabTracker::OnOmniboxNavigation() {
-  GetTracker()->NotifyEvent(events::kOmniboxInteraction);
-}
-
-void NewTabTracker::OnOmniboxFocused() {
-  if (ShouldShowPromo())
-    ShowPromo();
-}
-
-void NewTabTracker::OnPromoClosed() {
-  GetTracker()->Dismissed(kIPHNewTabFeature);
-}
-
-void NewTabTracker::OnSessionTimeMet() {
-  GetTracker()->NotifyEvent(events::kNewTabSessionTimeMet);
-}
-
-void NewTabTracker::ShowPromo() {
-  NewTabButton::ShowPromoForLastActiveBrowser();
-}
-
-void NewTabTracker::CloseBubble() {
-  NewTabButton::CloseBubbleForLastActiveBrowser();
-}
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
deleted file mode 100644
index 9fc3c48d..0000000
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_H_
-
-#include "chrome/browser/feature_engagement/feature_tracker.h"
-
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-
-namespace feature_engagement {
-
-// The NewTabTracker provides a backend for displaying in-product help for the
-// new tab button. NewTabTracker is the interface through which the event
-// constants for the NewTab feature can be be altered. Once all of the event
-// constants are met, NewTabTracker calls for the NewTabPromo to be shown, along
-// with recording when the NewTabPromo is dismissed. The requirements to show
-// the NewTabPromo are as follows:
-//
-// - At least two hours of observed session time have elapsed.
-// - The user has never opened another tab through any means.
-// - The user has navigated away from the start home screen.
-// - The omnibox is in focus, which implies the user is intending on navigating
-//   to a new page.
-class NewTabTracker : public FeatureTracker {
- public:
-  explicit NewTabTracker(Profile* profile);
-
-  // Alerts the new tab tracker that a new tab was opened.
-  void OnNewTabOpened();
-  // Alerts the new tab tracker that the omnibox has been used.
-  void OnOmniboxNavigation();
-  // Checks if the promo should be displayed since the omnibox is on focus.
-  void OnOmniboxFocused();
-  // Clears the flag for whether there is any in-product help being displayed.
-  void OnPromoClosed();
-  // Shows new tab in-product help promo bubble in last active browser.
-  void ShowPromo();
-
-  // Returns whether there was a bubble that was closed. A bubble closes only
-  // when it exists.
-  void CloseBubble();
-
- protected:
-  ~NewTabTracker() override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerEventTest, TestOnSessionTimeMet);
-  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerTest, TestShouldNotShowPromo);
-  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerTest, TestShouldShowPromo);
-  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerBrowserTest, TestShowPromo);
-
-  // FeatureTracker:
-  void OnSessionTimeMet() override;
-
-  DISALLOW_COPY_AND_ASSIGN(NewTabTracker);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_H_
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc
deleted file mode 100644
index 5395c172..0000000
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2017 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/feature_engagement/new_tab/new_tab_tracker.h"
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "chrome/browser/ui/views/tabs/new_tab_button.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/interactive_test_utils.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/feature_engagement/test/mock_tracker.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/omnibox/browser/omnibox_edit_model.h"
-#include "components/omnibox/browser/omnibox_view.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "ui/views/widget/widget.h"
-#include "url/gurl.h"
-
-namespace feature_engagement {
-
-namespace {
-
-MATCHER_P(IsFeature, feature, "") {
-  return arg.name == feature.name;
-}
-
-std::unique_ptr<KeyedService> BuildTestTrackerFactory(
-    content::BrowserContext* context) {
-  return std::make_unique<testing::StrictMock<test::MockTracker>>();
-}
-
-class NewTabTrackerBrowserTest : public InProcessBrowserTest {
- public:
-  NewTabTrackerBrowserTest() = default;
-  ~NewTabTrackerBrowserTest() override = default;
-
-  void SetUpOnMainThread() override {
-    TrackerFactory::GetInstance()->SetTestingFactoryAndUse(
-        browser()->profile(), base::BindRepeating(&BuildTestTrackerFactory));
-
-    // Ensure all initialization is finished.
-    base::RunLoop().RunUntilIdle();
-
-    feature_engagement_tracker_ = static_cast<test::MockTracker*>(
-        TrackerFactory::GetForBrowserContext(browser()->profile()));
-    ON_CALL(*feature_engagement_tracker_, GetTriggerState(testing::_))
-        .WillByDefault(testing::Return(
-            feature_engagement::Tracker::TriggerState::HAS_NOT_BEEN_DISPLAYED));
-
-    EXPECT_CALL(*feature_engagement_tracker_, IsInitialized())
-        .WillOnce(::testing::Return(true));
-
-    ASSERT_TRUE(TrackerFactory::GetForBrowserContext(browser()->profile())
-                    ->IsInitialized());
-  }
-
-  GURL GetGoogleURL() { return GURL("http://www.google.com/"); }
-
- protected:
-  // Owned by the Profile.
-  test::MockTracker* feature_engagement_tracker_;
-};
-
-}  // namespace
-
-IN_PROC_BROWSER_TEST_F(NewTabTrackerBrowserTest, TestShowPromo) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  // Bypassing the 2 hour active session time requirement.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              NotifyEvent(events::kNewTabSessionTimeMet));
-  auto* new_tab_tracker =
-      NewTabTrackerFactory::GetInstance()->GetForProfile(browser()->profile());
-  new_tab_tracker->OnSessionTimeMet();
-  new_tab_tracker
-      ->UseDefaultForChromeVariationConfigurationReleaseTimeForTesting();
-
-  // Navigate in the omnibox.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              NotifyEvent(events::kOmniboxInteraction));
-  ui_test_utils::NavigateToURL(browser(), GetGoogleURL());
-  OmniboxView* omnibox_view =
-      browser()->window()->GetLocationBar()->GetOmniboxView();
-  omnibox_view->OnBeforePossibleChange();
-  omnibox_view->SetUserText(base::ASCIIToUTF16("http://www.chromium.org/"));
-  omnibox_view->OnAfterPossibleChange(true);
-  omnibox_view->model()->AcceptInput(WindowOpenDisposition::CURRENT_TAB);
-
-  // Focus on the omnibox.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              ShouldTriggerHelpUI(IsFeature(kIPHNewTabFeature)))
-      .WillOnce(::testing::Return(true))
-      .WillRepeatedly(::testing::Return(false));
-  chrome::FocusLocationBar(browser());
-
-  TabStrip* tab_strip =
-      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
-  EXPECT_TRUE(
-      tab_strip->new_tab_button()->new_tab_promo()->GetWidget()->IsVisible());
-
-  // Tracker::Dismissed() must be invoked when the promo is closed. This will
-  // clear the flag for whether there is any in-product help being displayed.
-  EXPECT_CALL(*feature_engagement_tracker_,
-              Dismissed(IsFeature(kIPHNewTabFeature)));
-
-  tab_strip->new_tab_button()->new_tab_promo()->GetWidget()->Close();
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc
deleted file mode 100644
index 70aa063..0000000
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2017 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/feature_engagement/new_tab/new_tab_tracker_factory.h"
-
-#include "base/memory/singleton.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "content/public/browser/browser_context.h"
-
-namespace feature_engagement {
-
-// static
-NewTabTrackerFactory* NewTabTrackerFactory::GetInstance() {
-  return base::Singleton<NewTabTrackerFactory>::get();
-}
-
-NewTabTracker* NewTabTrackerFactory::GetForProfile(Profile* profile) {
-  return static_cast<NewTabTracker*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-NewTabTrackerFactory::NewTabTrackerFactory()
-    : BrowserContextKeyedServiceFactory(
-          "NewTabTracker",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(TrackerFactory::GetInstance());
-}
-
-NewTabTrackerFactory::~NewTabTrackerFactory() = default;
-
-KeyedService* NewTabTrackerFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  return new NewTabTracker(Profile::FromBrowserContext(context));
-}
-
-content::BrowserContext* NewTabTrackerFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  return chrome::GetBrowserContextRedirectedInIncognito(context);
-}
-
-bool NewTabTrackerFactory::ServiceIsCreatedWithBrowserContext() const {
-  // Start NewTabTracker early so the new tab in-product help starts tracking.
-  return true;
-}
-
-bool NewTabTrackerFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h
deleted file mode 100644
index 8b0fd3a..0000000
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_FACTORY_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_FACTORY_H_
-
-#include "base/macros.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace feature_engagement {
-class NewTabTracker;
-
-// NewTabTrackerFactory is the main client class for
-// interaction with the NewTabTracker component.
-class NewTabTrackerFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns singleton instance of NewTabTrackerFactory.
-  static NewTabTrackerFactory* GetInstance();
-
-  // Returns the NewTabTracker associated with the profile.
-  NewTabTracker* GetForProfile(Profile* profile);
-
- private:
-  friend struct base::DefaultSingletonTraits<NewTabTrackerFactory>;
-
-  NewTabTrackerFactory();
-  ~NewTabTrackerFactory() override;
-
-  // BrowserContextKeyedServiceFactory overrides:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(NewTabTrackerFactory);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_FACTORY_H_
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
deleted file mode 100644
index 4f8a577..0000000
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2017 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/feature_engagement/new_tab/new_tab_tracker.h"
-
-#include <memory>
-
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/feature_engagement/feature_tracker.h"
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/feature_engagement/test/mock_tracker.h"
-#include "components/feature_engagement/test/test_tracker.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace feature_engagement {
-
-namespace {
-
-constexpr char kGroupName[] = "Enabled";
-constexpr char kNewTabTrialName[] = "NewTabTrial";
-constexpr char kTestProfileName[] = "test-profile";
-
-class FakeNewTabTracker : public NewTabTracker {
- public:
-  FakeNewTabTracker(Tracker* feature_tracker, Profile* profile)
-      : NewTabTracker(profile),
-        feature_tracker_(feature_tracker),
-        pref_service_(
-            std::make_unique<sync_preferences::TestingPrefServiceSyncable>()) {
-    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
-  }
-
-  PrefService* GetPrefs() { return pref_service_.get(); }
-
-  // feature_engagement::NewTabTracker:
-  Tracker* GetTracker() const override { return feature_tracker_; }
-
- private:
-  Tracker* const feature_tracker_;
-  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
-      pref_service_;
-};
-
-class NewTabTrackerEventTest : public testing::Test {
- public:
-  NewTabTrackerEventTest() = default;
-  ~NewTabTrackerEventTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-    mock_tracker_ = std::make_unique<testing::StrictMock<test::MockTracker>>();
-    new_tab_tracker_ = std::make_unique<FakeNewTabTracker>(
-        mock_tracker_.get(),
-        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-  }
-
-  void TearDown() override {
-    new_tab_tracker_->RemoveSessionDurationObserver();
-    testing_profile_manager_.reset();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  std::unique_ptr<test::MockTracker> mock_tracker_;
-  std::unique_ptr<FakeNewTabTracker> new_tab_tracker_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(NewTabTrackerEventTest);
-};
-
-}  // namespace
-
-// Tests to verify feature_engagement::Tracker API boundary expectations:
-
-// If OnNewTabOpened() is called, the feature_engagement::Tracker
-// receives the kNewTabOpenedEvent.
-TEST_F(NewTabTrackerEventTest, TestOnNewTabOpened) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kNewTabOpened));
-  new_tab_tracker_->OnNewTabOpened();
-}
-
-// If OnOmniboxNavigation() is called, the feature_engagement::Tracker
-// receives the kOmniboxInteraction event.
-TEST_F(NewTabTrackerEventTest, TestOnOmniboxNavigation) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kOmniboxInteraction));
-  new_tab_tracker_->OnOmniboxNavigation();
-}
-
-// If OnSessionTimeMet() is called, the feature_engagement::Tracker
-// receives the kSessionTime event.
-TEST_F(NewTabTrackerEventTest, TestOnSessionTimeMet) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kNewTabSessionTimeMet));
-  new_tab_tracker_->OnSessionTimeMet();
-}
-
-namespace {
-
-class NewTabTrackerTest : public testing::Test {
- public:
-  NewTabTrackerTest() {
-    base::FieldTrialParams new_tab_params;
-    new_tab_params["event_new_tab_opened"] =
-        "name:new_tab_opened;comparator:==0;window:3650;storage:3650";
-    new_tab_params["event_omnibox_used"] =
-        "name:omnibox_used;comparator:>=1;window:3650;storage:3650";
-    new_tab_params["event_new_tab_session_time_met"] =
-        "name:new_tab_session_time_met;comparator:>=1;window:3650;storage:3650";
-    new_tab_params["event_trigger"] =
-        "name:new_tab_trigger;comparator:any;window:3650;storage:3650";
-    new_tab_params["event_used"] =
-        "name:new_tab_clicked;comparator:any;window:3650;storage:3650";
-    new_tab_params["session_rate"] = "<=3";
-    new_tab_params["availability"] = "any";
-    new_tab_params["x_date_released_in_seconds"] =
-        base::NumberToString(static_cast<int64_t>(
-            first_run::GetFirstRunSentinelCreationTime().ToDoubleT()));
-
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(kIPHNewTabFeature,
-                                                            new_tab_params);
-  }
-  ~NewTabTrackerTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-
-    testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(testing_profile_manager_->SetUp());
-
-    feature_engagement_tracker_ = CreateTestTracker();
-
-    new_tab_tracker_ = std::make_unique<FakeNewTabTracker>(
-        feature_engagement_tracker_.get(),
-        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
-
-    // The feature engagement tracker does async initialization.
-    base::RunLoop().RunUntilIdle();
-    ASSERT_TRUE(feature_engagement_tracker_->IsInitialized());
-  }
-
-  void TearDown() override {
-    new_tab_tracker_->RemoveSessionDurationObserver();
-    testing_profile_manager_->DeleteTestingProfile(kTestProfileName);
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<FakeNewTabTracker> new_tab_tracker_;
-  std::unique_ptr<Tracker> feature_engagement_tracker_;
-
- private:
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(NewTabTrackerTest);
-};
-
-}  // namespace
-
-// Tests to verify NewTabTracker functional expectations:
-
-// Test that a promo is not shown if the user uses a New Tab.
-// If OnNewTabOpened() is called, the ShouldShowPromo() should return false.
-TEST_F(NewTabTrackerTest, TestShouldNotShowPromo) {
-  EXPECT_FALSE(new_tab_tracker_->ShouldShowPromo());
-
-  new_tab_tracker_->OnSessionTimeMet();
-  new_tab_tracker_->OnOmniboxNavigation();
-
-  EXPECT_TRUE(new_tab_tracker_->ShouldShowPromo());
-
-  new_tab_tracker_->OnNewTabOpened();
-
-  EXPECT_FALSE(new_tab_tracker_->ShouldShowPromo());
-}
-
-// Test that a promo is shown if the session time is met and an omnibox
-// navigation occurs. If OnSessionTimeMet() and OnOmniboxNavigation()
-// are called, ShouldShowPromo() should return true.
-TEST_F(NewTabTrackerTest, TestShouldShowPromo) {
-  EXPECT_FALSE(new_tab_tracker_->ShouldShowPromo());
-
-  new_tab_tracker_->OnSessionTimeMet();
-  new_tab_tracker_->OnOmniboxNavigation();
-
-  EXPECT_TRUE(new_tab_tracker_->ShouldShowPromo());
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/session_duration_updater.cc b/chrome/browser/feature_engagement/session_duration_updater.cc
deleted file mode 100644
index 2e57ffb..0000000
--- a/chrome/browser/feature_engagement/session_duration_updater.cc
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2017 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/feature_engagement/session_duration_updater.h"
-
-#include "base/timer/elapsed_timer.h"
-#include "base/values.h"
-#include "chrome/common/pref_names.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/scoped_user_pref_update.h"
-
-namespace feature_engagement {
-
-SessionDurationUpdater::SessionDurationUpdater(
-    PrefService* pref_service,
-    const char* observed_session_time_dict_key)
-    : duration_tracker_observer_(this),
-      pref_service_(pref_service),
-      observed_session_time_dict_key_(observed_session_time_dict_key) {
-  AddDurationTrackerObserver();
-}
-
-SessionDurationUpdater::~SessionDurationUpdater() = default;
-
-// static
-void SessionDurationUpdater::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterDictionaryPref(prefs::kObservedSessionTime);
-}
-
-base::TimeDelta SessionDurationUpdater::GetCumulativeElapsedSessionTime()
-    const {
-  base::TimeDelta elapsed_time = GetRecordedObservedSessionTime();
-  return current_session_timer_
-             ? elapsed_time + current_session_timer_.get()->Elapsed()
-             : elapsed_time;
-}
-
-base::TimeDelta SessionDurationUpdater::GetRecordedObservedSessionTime() const {
-  const base::DictionaryValue* dict =
-      pref_service_->GetDictionary(prefs::kObservedSessionTime);
-  const double stored_value =
-      dict->FindDoubleKey(observed_session_time_dict_key_).value_or(0);
-  return base::TimeDelta::FromSeconds(stored_value);
-}
-
-void SessionDurationUpdater::AddObserver(Observer* observer) {
-  observer_list_.AddObserver(observer);
-
-  // Re-adds SessionDurationUpdater as an observer of
-  // DesktopSessionDurationTracker if another feature is added after
-  // SessionDurationUpdater was removed.
-  if (!duration_tracker_observer_.IsObserving(
-          metrics::DesktopSessionDurationTracker::Get())) {
-    if (!current_session_timer_)
-      current_session_timer_ = std::make_unique<base::ElapsedTimer>();
-    AddDurationTrackerObserver();
-  }
-}
-
-void SessionDurationUpdater::RemoveObserver(Observer* observer) {
-  observer_list_.RemoveObserver(observer);
-  // If all the observer Features have removed themselves due to their active
-  // time limits have been reached, the SessionDurationUpdater removes itself
-  // as an observer of DesktopSessionDurationTracker.
-  if (!observer_list_.might_have_observers()) {
-    current_session_timer_.reset();
-    RemoveDurationTrackerObserver();
-  }
-}
-
-void SessionDurationUpdater::OnSessionStarted(base::TimeTicks session_start) {
-  current_session_timer_ = std::make_unique<base::ElapsedTimer>();
-}
-
-void SessionDurationUpdater::OnSessionEnded(base::TimeDelta elapsed) {
-  // This case is only used during testing as that is the only case that
-  // DesktopSessionDurationTracker isn't calling this on its observer.
-  if (!duration_tracker_observer_.IsObserving(
-          metrics::DesktopSessionDurationTracker::Get())) {
-    return;
-  }
-
-  const base::DictionaryValue* dict =
-      pref_service_->GetDictionary(prefs::kObservedSessionTime);
-  const double stored_value =
-      dict->FindDoubleKey(observed_session_time_dict_key_).value_or(0);
-
-  base::TimeDelta elapsed_session_time =
-      base::TimeDelta::FromSeconds(stored_value) + elapsed;
-
-  DictionaryPrefUpdate update(pref_service_, prefs::kObservedSessionTime);
-  update->SetKey(
-      observed_session_time_dict_key_,
-      base::Value(static_cast<double>(elapsed_session_time.InSeconds())));
-
-  current_session_timer_.reset();
-
-  for (Observer& observer : observer_list_)
-    observer.OnSessionEnded(elapsed_session_time);
-}
-
-void SessionDurationUpdater::AddDurationTrackerObserver() {
-  duration_tracker_observer_.Add(metrics::DesktopSessionDurationTracker::Get());
-}
-
-void SessionDurationUpdater::RemoveDurationTrackerObserver() {
-  duration_tracker_observer_.Remove(
-      metrics::DesktopSessionDurationTracker::Get());
-}
-
-PrefService* SessionDurationUpdater::GetPrefs() {
-  return pref_service_;
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/session_duration_updater.h b/chrome/browser/feature_engagement/session_duration_updater.h
deleted file mode 100644
index 4aaf4c5..0000000
--- a/chrome/browser/feature_engagement/session_duration_updater.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2017 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_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_H_
-#define CHROME_BROWSER_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_H_
-
-#include <memory>
-
-#include "base/observer_list.h"
-#include "base/scoped_observer.h"
-#include "base/time/time.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/prefs/pref_service.h"
-
-namespace base {
-class ElapsedTimer;
-}
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}  // namespace user_prefs
-
-namespace feature_engagement {
-
-// The SessionDurationUpdater tracks the total amount of observed time across
-// Chrome restarts. Observed time in this context is active session time that
-// occurs while there is a FeatureTracker whose active session time requirement
-// has not been satisfied. This allows subclasses of FeatureTracker to check how
-// much active time has passed, and decide whether to show their respective
-// promos accordingly.
-//
-// When an active session is closed, DesktopSessionDurationTracker calls
-// OnSessionEnded and the observed time is incremented and persisted. Then,
-// OnSessionEnded is called on all of Features observing SessionDurationUpdater.
-// If the feature has its time limit exceeded, it should remove itself as an
-// observer of SessionDurationUpdater. If SessionDurationUpdater has no
-// observers, it means that the time limits of all the observing features has
-// been exceeded, so the SessionDurationUpdater removes itself as an observer of
-// DesktopSessionDurationTracker and stops updating observed session time.
-//
-// If all observers are removed, SessionDurationUpdater doesn't continue
-// updating the observed session time. However, if another feature is added as
-// an observer later, SessionDurationUpdater starts incrementing the observed
-// session time again.
-
-class SessionDurationUpdater
-    : public metrics::DesktopSessionDurationTracker::Observer,
-      public KeyedService {
- public:
-  // The methods for the observer will be called on the UI thread.
-  class Observer {
-   public:
-    virtual ~Observer() = default;
-    virtual void OnSessionEnded(base::TimeDelta total_session_time) = 0;
-  };
-
-  SessionDurationUpdater(PrefService* pref_service,
-                         const char* observed_session_time_pref_key);
-  ~SessionDurationUpdater() override;
-
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // Returns the total amount of observed active session time of the current
-  // session plus the previously recorded observed session time. The resulting
-  // value should be cumulative session time across all Chrome restarts.
-  base::TimeDelta GetCumulativeElapsedSessionTime() const;
-
-  // Gets the recorded observed session time which is cumulative for all
-  // previous sessions.
-  base::TimeDelta GetRecordedObservedSessionTime() const;
-
-  // For observing the status of the session tracker.
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
-  // metrics::DesktopSessionDurationtracker::Observer:
-  void OnSessionStarted(base::TimeTicks delta) override;
-  void OnSessionEnded(base::TimeDelta delta) override;
-
- private:
-  void AddDurationTrackerObserver();
-  void RemoveDurationTrackerObserver();
-
-  // Returns the pref service associated with this SessionDurationUpdater.
-  PrefService* GetPrefs();
-
-  // Observes the DesktopSessionDurationTracker and notifies when a desktop
-  // session starts and ends.
-  ScopedObserver<metrics::DesktopSessionDurationTracker,
-                 metrics::DesktopSessionDurationTracker::Observer>
-      duration_tracker_observer_;
-
-  // Owned by Profile manager.
-  PrefService* const pref_service_;
-
-  // The profile dict key of |kObservedSessionTime| that tracks the observed
-  // session time for an In-Product Help feature. Needs to outlive this class.
-  const char* const observed_session_time_dict_key_;
-
-  // Tracks the elapsed active session time while the browser is open.
-  std::unique_ptr<base::ElapsedTimer> current_session_timer_;
-
-  base::ObserverList<Observer>::Unchecked observer_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(SessionDurationUpdater);
-};
-
-}  // namespace feature_engagement
-
-#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_H_
diff --git a/chrome/browser/feature_engagement/session_duration_updater_unittest.cc b/chrome/browser/feature_engagement/session_duration_updater_unittest.cc
deleted file mode 100644
index 85e485b..0000000
--- a/chrome/browser/feature_engagement/session_duration_updater_unittest.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2017 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/feature_engagement/session_duration_updater.h"
-
-#include <memory>
-
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#include "chrome/common/pref_names.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace feature_engagement {
-
-namespace {
-
-constexpr char kTestObservedSessionTimeKey[] = "test_observed_session_time_key";
-
-class TestObserver : public SessionDurationUpdater::Observer {
- public:
-  TestObserver()
-      : pref_service_(
-            std::make_unique<sync_preferences::TestingPrefServiceSyncable>()),
-        session_duration_updater_(pref_service_.get(),
-                                  kTestObservedSessionTimeKey),
-        session_duration_observer_(this) {
-    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
-  }
-
-  void AddSessionDurationObserver() {
-    session_duration_observer_.Add(&session_duration_updater_);
-  }
-
-  void RemoveSessionDurationObserver() {
-    session_duration_observer_.Remove(&session_duration_updater_);
-  }
-
-  // SessionDurationUpdater::Observer:
-  void OnSessionEnded(base::TimeDelta total_session_time) override {}
-
-  PrefService* GetPrefs() { return pref_service_.get(); }
-
-  SessionDurationUpdater* GetSessionDurationUpdater() {
-    return &session_duration_updater_;
-  }
-
-  void SetBaseObservedSessionTime(base::TimeDelta base_value) {
-    DictionaryPrefUpdate update(GetPrefs(), prefs::kObservedSessionTime);
-    update->SetKey(kTestObservedSessionTimeKey,
-                   base::Value(static_cast<double>(base_value.InSeconds())));
-  }
-
- private:
-  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
-      pref_service_;
-  SessionDurationUpdater session_duration_updater_;
-  ScopedObserver<SessionDurationUpdater, SessionDurationUpdater::Observer>
-      session_duration_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestObserver);
-};
-
-class SessionDurationUpdaterTest : public testing::Test {
- public:
-  SessionDurationUpdaterTest() = default;
-  ~SessionDurationUpdaterTest() override = default;
-
-  // testing::Test:
-  void SetUp() override {
-    // Start the DesktopSessionDurationTracker to track active session time.
-    metrics::DesktopSessionDurationTracker::Initialize();
-
-    test_observer_ = std::make_unique<TestObserver>();
-    test_observer_->AddSessionDurationObserver();
-  }
-
-  void TearDown() override {
-    test_observer_->RemoveSessionDurationObserver();
-    metrics::DesktopSessionDurationTracker::CleanupForTesting();
-  }
-
- protected:
-  std::unique_ptr<TestObserver> test_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(SessionDurationUpdaterTest);
-};
-
-}  // namespace
-
-// kObservedSessionTime should be 0 on initalization and 50 after simulation.
-TEST_F(SessionDurationUpdaterTest, TimeAdded) {
-  // Tests the pref is registered to 0 before any session time passes.
-  EXPECT_EQ(0, test_observer_->GetSessionDurationUpdater()
-                   ->GetRecordedObservedSessionTime()
-                   .InSeconds());
-
-  // Tests 50 seconds passing with an observer added.
-  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
-      base::TimeDelta::FromSeconds(50));
-
-  EXPECT_EQ(50, test_observer_->GetSessionDurationUpdater()
-                    ->GetRecordedObservedSessionTime()
-                    .InSeconds());
-}
-
-// Observed session time should be equal to base value on initalization and 100
-// after simulation.
-TEST_F(SessionDurationUpdaterTest, TimeAddedWithBaseValue) {
-  test_observer_->SetBaseObservedSessionTime(
-      base::TimeDelta::FromSeconds(50.0));
-
-  // Tests the pref is registered to 50 seconds before any session time passes.
-  EXPECT_EQ(50, test_observer_->GetSessionDurationUpdater()
-                    ->GetRecordedObservedSessionTime()
-                    .InSeconds());
-
-  // Tests 50 seconds passing with an observer added.
-  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
-      base::TimeDelta::FromSeconds(50));
-
-  EXPECT_EQ(100, test_observer_->GetSessionDurationUpdater()
-                     ->GetRecordedObservedSessionTime()
-                     .InSeconds());
-}
-
-// kObservedSessionTime should not be updated when SessionDurationUpdater has
-// no observers, but should start updating again if another observer is added.
-TEST_F(SessionDurationUpdaterTest, AddingAndRemovingObservers) {
-  // Tests 50 seconds passing with an observer added.
-  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
-      base::TimeDelta::FromSeconds(50));
-
-  EXPECT_EQ(50, test_observer_->GetSessionDurationUpdater()
-                    ->GetRecordedObservedSessionTime()
-                    .InSeconds());
-
-  // Tests 50 seconds passing without any observers. No time should be added to
-  // the pref in this case.
-  test_observer_->RemoveSessionDurationObserver();
-
-  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
-      base::TimeDelta::FromSeconds(50));
-
-  EXPECT_EQ(50, test_observer_->GetSessionDurationUpdater()
-                    ->GetRecordedObservedSessionTime()
-                    .InSeconds());
-  // Tests 50 seconds passing with an observer re-added. Time should be added
-  // again now.
-  test_observer_->AddSessionDurationObserver();
-
-  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
-      base::TimeDelta::FromSeconds(50));
-
-  EXPECT_EQ(100, test_observer_->GetSessionDurationUpdater()
-                     ->GetRecordedObservedSessionTime()
-                     .InSeconds());
-}
-
-// Cumulative elapsed session time should be 0 on initalization and 50 after
-// simulation.
-TEST_F(SessionDurationUpdaterTest, GetCumulativeElapsedSessionTime) {
-  // Tests the pref is registered to 0 before any session time passes.
-  EXPECT_EQ(0, test_observer_->GetSessionDurationUpdater()
-                   ->GetCumulativeElapsedSessionTime()
-                   .InSeconds());
-
-  // Tests 50 seconds passing with an observer added.
-  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
-      base::TimeDelta::FromSeconds(50));
-
-  EXPECT_EQ(50, test_observer_->GetSessionDurationUpdater()
-                    ->GetCumulativeElapsedSessionTime()
-                    .InSeconds());
-}
-
-}  // namespace feature_engagement
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 332326b..480e055 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3347,6 +3347,10 @@
     "owners": [ "cros-customization@google.com", "hsuregan", "khorimoto" ],
     "expiry_milestone": 90
   },
+  { "name": "os-settings-polymer3",
+    "owners": [ "cros-customization@google.com", "tjohnsonkanu", "khorimoto" ],
+    "expiry_milestone": 90
+  },
   {
     "name": "overlay-new-layout",
     "owners": [ "donnd", "jinsukkim", "contextual-search-eng" ],
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index cb0dc7aa..1c4370d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1165,9 +1165,9 @@
     "also be enabled.";
 
 const char kGlobalMediaControlsOverlayControlsName[] =
-    "Enable overlay controls for Global Nedia Controls";
+    "Enable overlay controls for Global Media Controls";
 const char kGlobalMediaControlsOverlayControlsDescription[] =
-    "Allowing controls to be dragged out from Global Media Controls dialog."
+    "Allowing controls to be dragged out from Global Media Controls dialog. "
     "Requires #global-media-controls to also be enabled.";
 
 const char kGpuRasterizationName[] = "GPU rasterization";
@@ -3959,6 +3959,10 @@
     "The toggle allows users to set whether a network should be considered "
     "metered for purposes of bandwith usage (e.g. for automatic updates).";
 
+const char kOsSettingsPolymer3Name[] = "Enable OS Settings Polymer3";
+const char kOsSettingsPolymer3Description[] =
+    "Flips chrome://os-settings to show Polymer3 version.";
+
 const char kPrintServerUiName[] = "Print Server UI";
 const char kPrintServerUiDescription[] =
     "Enables users to add their own print server in the printer settings page.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a841ce7b..8a6fbb4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2310,6 +2310,9 @@
 extern const char kMeteredShowToggleName[];
 extern const char kMeteredShowToggleDescription[];
 
+extern const char kOsSettingsPolymer3Name[];
+extern const char kOsSettingsPolymer3Description[];
+
 extern const char kPrintServerUiName[];
 extern const char kPrintServerUiDescription[];
 
diff --git a/chrome/browser/media/android/cdm/per_device_provisioning_permission.cc b/chrome/browser/media/android/cdm/per_device_provisioning_permission.cc
index 8952f54..f1bc4ae 100644
--- a/chrome/browser/media/android/cdm/per_device_provisioning_permission.cc
+++ b/chrome/browser/media/android/cdm/per_device_provisioning_permission.cc
@@ -177,6 +177,7 @@
   // The created PerDeviceProvisioningPermissionRequest deletes itself once
   // complete. See PerDeviceProvisioningPermissionRequest::RequestFinished().
   permission_request_manager->AddRequest(
+      render_frame_host,
       new PerDeviceProvisioningPermissionRequest(
           render_frame_host->GetLastCommittedOrigin(), std::move(callback)));
 }
diff --git a/chrome/browser/media/history/media_history_store.cc b/chrome/browser/media/history/media_history_store.cc
index 6acbf7a..5e3f656a 100644
--- a/chrome/browser/media/history/media_history_store.cc
+++ b/chrome/browser/media/history/media_history_store.cc
@@ -174,8 +174,6 @@
     scoped_refptr<base::UpdateableSequencedTaskRunner> db_task_runner)
     : db_task_runner_(db_task_runner),
       db_path_(GetDBPath(profile)),
-      db_(std::make_unique<sql::Database>()),
-      meta_table_(std::make_unique<sql::MetaTable>()),
       origin_table_(new MediaHistoryOriginTable(db_task_runner_)),
       playback_table_(new MediaHistoryPlaybackTable(db_task_runner_)),
       session_table_(new MediaHistorySessionTable(db_task_runner_)),
@@ -188,14 +186,7 @@
       feed_items_table_(IsMediaFeedsEnabled()
                             ? new MediaHistoryFeedItemsTable(db_task_runner_)
                             : nullptr),
-      initialization_successful_(false) {
-  db_->set_histogram_tag("MediaHistory");
-  db_->set_exclusive_locking();
-
-  // To recover from corruption.
-  db_->set_error_callback(
-      base::BindRepeating(&DatabaseErrorCallback, db_.get(), db_path_));
-}
+      initialization_successful_(false) {}
 
 MediaHistoryStore::~MediaHistoryStore() {
   // The connection pointer needs to be deleted on the DB sequence since there
@@ -313,6 +304,16 @@
 MediaHistoryStore::InitResult MediaHistoryStore::InitializeInternal() {
   DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
 
+  db_ = std::make_unique<sql::Database>();
+  db_->set_histogram_tag("MediaHistory");
+  db_->set_exclusive_locking();
+
+  // To recover from corruption.
+  db_->set_error_callback(
+      base::BindRepeating(&DatabaseErrorCallback, db_.get(), db_path_));
+
+  meta_table_ = std::make_unique<sql::MetaTable>();
+
   if (db_path_.empty()) {
     if (IsCancelled() || !db_ || !db_->OpenInMemory()) {
       LOG(ERROR) << "Failed to open the in-memory database.";
@@ -335,6 +336,8 @@
     }
   }
 
+  db_->Preload();
+
   if (IsCancelled() || !db_ || !db_->Execute("PRAGMA foreign_keys=1")) {
     LOG(ERROR) << "Failed to enable foreign keys on the media history store.";
 
diff --git a/chrome/browser/media/history/media_history_store_unittest.cc b/chrome/browser/media/history/media_history_store_unittest.cc
index c546b4c..3eaf0f6 100644
--- a/chrome/browser/media/history/media_history_store_unittest.cc
+++ b/chrome/browser/media/history/media_history_store_unittest.cc
@@ -286,7 +286,13 @@
                     TestState::kIncognito,
                     TestState::kSavingBrowserHistoryDisabled));
 
-TEST_P(MediaHistoryStoreUnitTest, SavePlayback) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_SavePlayback DISABLED_SavePlayback
+#else
+#define MAYBE_SavePlayback SavePlayback
+#endif
+TEST_P(MediaHistoryStoreUnitTest, MAYBE_SavePlayback) {
   base::HistogramTester histogram_tester;
 
   const auto now_before =
@@ -354,7 +360,13 @@
       MediaHistoryStore::PlaybackWriteResult::kSuccess, IsReadOnly() ? 0 : 2);
 }
 
-TEST_P(MediaHistoryStoreUnitTest, SavePlayback_BadOrigin) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_SavePlayback_BadOrigin DISABLED_SavePlayback_BadOrigin
+#else
+#define MAYBE_SavePlayback_BadOrigin SavePlayback_BadOrigin
+#endif
+TEST_P(MediaHistoryStoreUnitTest, MAYBE_SavePlayback_BadOrigin) {
   GURL url("http://google.com/test");
   GURL url2("http://google.co.uk/test");
   content::MediaPlayerWatchTime watch_time(url, url2.GetOrigin(),
@@ -370,7 +382,13 @@
   EXPECT_TRUE(origins.empty());
 }
 
-TEST_P(MediaHistoryStoreUnitTest, GetStats) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_GetStats DISABLED_GetStats
+#else
+#define MAYBE_GetStats GetStats
+#endif
+TEST_P(MediaHistoryStoreUnitTest, MAYBE_GetStats) {
   {
     // Check all the tables are empty.
     mojom::MediaHistoryStatsPtr stats = GetStatsSync(service());
@@ -430,7 +448,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreUnitTest, UrlShouldBeUniqueForSessions) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_UrlShouldBeUniqueForSessions DISABLED_UrlShouldBeUniqueForSessions
+#else
+#define MAYBE_UrlShouldBeUniqueForSessions UrlShouldBeUniqueForSessions
+#endif
+TEST_P(MediaHistoryStoreUnitTest, MAYBE_UrlShouldBeUniqueForSessions) {
   base::HistogramTester histogram_tester;
 
   GURL url_a("https://www.google.com");
@@ -504,7 +528,16 @@
       MediaHistoryStore::SessionWriteResult::kSuccess, IsReadOnly() ? 0 : 3);
 }
 
-TEST_P(MediaHistoryStoreUnitTest, SavePlayback_IncrementAggregateWatchtime) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_SavePlayback_IncrementAggregateWatchtime \
+  DISABLED_SavePlayback_IncrementAggregateWatchtime
+#else
+#define MAYBE_SavePlayback_IncrementAggregateWatchtime \
+  SavePlayback_IncrementAggregateWatchtime
+#endif
+TEST_P(MediaHistoryStoreUnitTest,
+       MAYBE_SavePlayback_IncrementAggregateWatchtime) {
   GURL url("http://google.com/test");
   GURL url_alt("http://example.org/test");
 
@@ -610,7 +643,13 @@
   EXPECT_EQ(origins, GetOriginRowsSync(otr_service()));
 }
 
-TEST_P(MediaHistoryStoreUnitTest, GetOriginsWithHighWatchTime) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_GetOriginsWithHighWatchTime DISABLED_GetOriginsWithHighWatchTime
+#else
+#define MAYBE_GetOriginsWithHighWatchTime GetOriginsWithHighWatchTime
+#endif
+TEST_P(MediaHistoryStoreUnitTest, MAYBE_GetOriginsWithHighWatchTime) {
   const GURL url("http://google.com/test");
   const GURL url_alt("http://example.org/test");
   const base::TimeDelta min_watch_time = base::TimeDelta::FromMinutes(30);
@@ -955,7 +994,13 @@
                          testing::Values(TestState::kNormal,
                                          TestState::kIncognito));
 
-TEST_P(MediaHistoryStoreFeedsTest, DiscoverMediaFeed) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_DiscoverMediaFeed DISABLED_DiscoverMediaFeed
+#else
+#define MAYBE_DiscoverMediaFeed DiscoverMediaFeed
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_DiscoverMediaFeed) {
   GURL url_a("https://www.google.com/feed");
   GURL url_b("https://www.google.co.uk/feed");
   GURL url_c("https://www.google.com/feed2");
@@ -1022,7 +1067,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult DISABLED_StoreMediaFeedFetchResult
+#else
+#define MAYBE_StoreMediaFeedFetchResult StoreMediaFeedFetchResult
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_StoreMediaFeedFetchResult) {
   const GURL feed_url("https://www.google.com/feed");
   DiscoverMediaFeed(feed_url);
   WaitForDB();
@@ -1177,7 +1228,15 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_WithEmpty) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult_WithEmpty \
+  DISABLED_StoreMediaFeedFetchResult_WithEmpty
+#else
+#define MAYBE_StoreMediaFeedFetchResult_WithEmpty \
+  StoreMediaFeedFetchResult_WithEmpty
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_StoreMediaFeedFetchResult_WithEmpty) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -1221,7 +1280,16 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_MultipleFeeds) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult_MultipleFeeds \
+  DISABLED_StoreMediaFeedFetchResult_MultipleFeeds
+#else
+#define MAYBE_StoreMediaFeedFetchResult_MultipleFeeds \
+  StoreMediaFeedFetchResult_MultipleFeeds
+#endif
+TEST_P(MediaHistoryStoreFeedsTest,
+       MAYBE_StoreMediaFeedFetchResult_MultipleFeeds) {
   const GURL feed_a_url("https://www.google.com/feed");
   const GURL feed_b_url("https://www.google.co.uk/feed");
 
@@ -1299,7 +1367,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, RediscoverMediaFeed) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_RediscoverMediaFeed DISABLED_RediscoverMediaFeed
+#else
+#define MAYBE_RediscoverMediaFeed RediscoverMediaFeed
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_RediscoverMediaFeed) {
   GURL feed_url("https://www.google.com/feed");
   DiscoverMediaFeed(feed_url);
   WaitForDB();
@@ -1392,7 +1466,16 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_IncreaseFailed) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult_IncreaseFailed \
+  DISABLED_StoreMediaFeedFetchResult_IncreaseFailed
+#else
+#define MAYBE_StoreMediaFeedFetchResult_IncreaseFailed \
+  StoreMediaFeedFetchResult_IncreaseFailed
+#endif
+TEST_P(MediaHistoryStoreFeedsTest,
+       MAYBE_StoreMediaFeedFetchResult_IncreaseFailed) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -1475,7 +1558,16 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_CheckLogoMax) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult_CheckLogoMax \
+  DISABLED_StoreMediaFeedFetchResult_CheckLogoMax
+#else
+#define MAYBE_StoreMediaFeedFetchResult_CheckLogoMax \
+  StoreMediaFeedFetchResult_CheckLogoMax
+#endif
+TEST_P(MediaHistoryStoreFeedsTest,
+       MAYBE_StoreMediaFeedFetchResult_CheckLogoMax) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -1552,7 +1644,16 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, StoreMediaFeedFetchResult_CheckImageMax) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult_CheckImageMax \
+  DISABLED_StoreMediaFeedFetchResult_CheckImageMax
+#else
+#define MAYBE_StoreMediaFeedFetchResult_CheckImageMax \
+  StoreMediaFeedFetchResult_CheckImageMax
+#endif
+TEST_P(MediaHistoryStoreFeedsTest,
+       MAYBE_StoreMediaFeedFetchResult_CheckImageMax) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -1632,8 +1733,16 @@
   }
 }
 
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_StoreMediaFeedFetchResult_DefaultSafeSearchResult \
+  DISABLED_StoreMediaFeedFetchResult_DefaultSafeSearchResult
+#else
+#define MAYBE_StoreMediaFeedFetchResult_DefaultSafeSearchResult \
+  StoreMediaFeedFetchResult_DefaultSafeSearchResult
+#endif
 TEST_P(MediaHistoryStoreFeedsTest,
-       StoreMediaFeedFetchResult_DefaultSafeSearchResult) {
+       MAYBE_StoreMediaFeedFetchResult_DefaultSafeSearchResult) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -1671,7 +1780,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, SafeSearchCheck) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_SafeSearchCheck DISABLED_SafeSearchCheck
+#else
+#define MAYBE_SafeSearchCheck SafeSearchCheck
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_SafeSearchCheck) {
   const GURL feed_url_a("https://www.google.com/feed");
   const GURL feed_url_b("https://www.google.co.uk/feed");
 
@@ -1798,7 +1913,16 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, GetMediaFeedsSortByWatchtimePercentile) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_GetMediaFeedsSortByWatchtimePercentile \
+  DISABLED_GetMediaFeedsSortByWatchtimePercentile
+#else
+#define MAYBE_GetMediaFeedsSortByWatchtimePercentile \
+  GetMediaFeedsSortByWatchtimePercentile
+#endif
+TEST_P(MediaHistoryStoreFeedsTest,
+       MAYBE_GetMediaFeedsSortByWatchtimePercentile) {
   // We add 111 origins with watchtime and feeds for all but one of these. Half
   // of the feeds will have items.
   const unsigned kNumberOfOrigins = 111;
@@ -2054,7 +2178,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, FeedItemsClickAndShown) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_FeedItemsClickAndShown DISABLED_FeedItemsClickAndShown
+#else
+#define MAYBE_FeedItemsClickAndShown FeedItemsClickAndShown
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_FeedItemsClickAndShown) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   WaitForDB();
 
@@ -2170,7 +2300,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, ResetMediaFeed) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_ResetMediaFeed DISABLED_ResetMediaFeed
+#else
+#define MAYBE_ResetMediaFeed ResetMediaFeed
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_ResetMediaFeed) {
   const GURL feed_url_a("https://www.google.com/feed");
   const GURL feed_url_b("https://www.google.co.uk/feed");
 
@@ -2332,7 +2468,14 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, ResetMediaFeedDueToCacheClearing) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_ResetMediaFeedDueToCacheClearing \
+  DISABLED_ResetMediaFeedDueToCacheClearing
+#else
+#define MAYBE_ResetMediaFeedDueToCacheClearing ResetMediaFeedDueToCacheClearing
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_ResetMediaFeedDueToCacheClearing) {
   const GURL feed_url_a("https://www.google.com/feed");
   const GURL feed_url_b("https://www.google.co.uk/feed");
 
@@ -2585,7 +2728,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, DeleteMediaFeed) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_DeleteMediaFeed DISABLED_DeleteMediaFeed
+#else
+#define MAYBE_DeleteMediaFeed DeleteMediaFeed
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_DeleteMediaFeed) {
   DiscoverMediaFeed(GURL("https://www.google.com/feed"));
   DiscoverMediaFeed(GURL("https://www.google.co.uk/feed"));
   WaitForDB();
@@ -2656,7 +2805,13 @@
   }
 }
 
-TEST_P(MediaHistoryStoreFeedsTest, GetMediaFeedFetchDetails) {
+// TODO(crbug.com/1087974).
+#if defined(THREAD_SANITIZER)
+#define MAYBE_GetMediaFeedFetchDetails DISABLED_GetMediaFeedFetchDetails
+#else
+#define MAYBE_GetMediaFeedFetchDetails GetMediaFeedFetchDetails
+#endif
+TEST_P(MediaHistoryStoreFeedsTest, MAYBE_GetMediaFeedFetchDetails) {
   const GURL feed_url("https://www.google.com/feed");
 
   DiscoverMediaFeed(feed_url);
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index 0b5d837..420252f4 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -335,7 +335,7 @@
   void EnablePolicyWithValue(net::AmbientAuthAllowedProfileTypes value) {
     SetPolicy(&policies_,
               policy::key::kAmbientAuthenticationInPrivateModesEnabled,
-              std::make_unique<base::Value>(static_cast<int>(value)));
+              base::Value(static_cast<int>(value)));
     UpdateProviderPolicy(policies_);
   }
 
@@ -564,7 +564,7 @@
   // the feature flag.
   policy::PolicyMap policies;
   SetPolicy(&policies, policy::key::kBuiltinCertificateVerifierEnabled,
-            std::make_unique<base::Value>(true));
+            base::Value(true));
   UpdateProviderPolicy(policies);
 
   {
@@ -579,7 +579,7 @@
   }
 
   SetPolicy(&policies, policy::key::kBuiltinCertificateVerifierEnabled,
-            std::make_unique<base::Value>(false));
+            base::Value(false));
   UpdateProviderPolicy(policies);
 
   {
@@ -632,7 +632,7 @@
   // the feature flag.
   policy::PolicyMap policies;
   SetPolicy(&policies, policy::key::kBuiltinCertificateVerifierEnabled,
-            std::make_unique<base::Value>(true));
+            base::Value(true));
   UpdateProviderPolicy(policies);
 
   {
@@ -644,7 +644,7 @@
   }
 
   SetPolicy(&policies, policy::key::kBuiltinCertificateVerifierEnabled,
-            std::make_unique<base::Value>(false));
+            base::Value(false));
   UpdateProviderPolicy(policies);
 
   {
diff --git a/chrome/browser/net/samesite_cookies_policy_browsertest.cc b/chrome/browser/net/samesite_cookies_policy_browsertest.cc
index 07fc3f2..bd82a3e5 100644
--- a/chrome/browser/net/samesite_cookies_policy_browsertest.cc
+++ b/chrome/browser/net/samesite_cookies_policy_browsertest.cc
@@ -58,7 +58,7 @@
   PolicyMap policies;
   // Set a policy to allow Legacy access for all cookies.
   SetPolicy(&policies, key::kLegacySameSiteCookieBehaviorEnabled,
-            std::make_unique<base::Value>(1));
+            base::Value(1));
   UpdateProviderPolicy(policies);
 
   GURL url(kURL);
@@ -118,7 +118,7 @@
   PolicyMap policies;
   // Set a policy to block Legacy access for all cookies.
   SetPolicy(&policies, key::kLegacySameSiteCookieBehaviorEnabled,
-            std::make_unique<base::Value>(2));
+            base::Value(2));
   UpdateProviderPolicy(policies);
 
   GURL url(kURL);
@@ -203,8 +203,8 @@
   GURL other_domain_url("http://other-domain.example");
 
   // Set a policy to allow Legacy cookie access for one domain only.
-  auto policy_value = std::make_unique<base::Value>(base::Value::Type::LIST);
-  policy_value->Append(legacy_allowed_domain_url.host());
+  base::Value policy_value(base::Value::Type::LIST);
+  policy_value.Append(legacy_allowed_domain_url.host());
 
   PolicyMap policies;
   // Set a policy to allow Legacy access for the given domain only.
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index ae771b3..6285ac8d 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -566,7 +566,7 @@
   // override the feature flag.
   policy::PolicyMap policies;
   SetPolicy(&policies, policy::key::kBuiltinCertificateVerifierEnabled,
-            std::make_unique<base::Value>(true));
+            base::Value(true));
   UpdateProviderPolicy(policies);
 
   network_context_params_ptr =
@@ -577,7 +577,7 @@
       network::mojom::CertVerifierCreationParams::CertVerifierImpl::kBuiltin);
 
   SetPolicy(&policies, policy::key::kBuiltinCertificateVerifierEnabled,
-            std::make_unique<base::Value>(false));
+            base::Value(false));
   UpdateProviderPolicy(policies);
 
   network_context_params_ptr =
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 93bad48..30ef51f 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -161,33 +161,6 @@
   }
 }
 
-optimization_guide::OptimizationGuideDecision
-OptimizationGuideKeyedService::ShouldTargetNavigation(
-    content::NavigationHandle* navigation_handle,
-    optimization_guide::proto::OptimizationTarget optimization_target) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(navigation_handle->IsInMainFrame());
-
-  if (!prediction_manager_) {
-    // We are not initialized yet, just return unknown.
-    return optimization_guide::OptimizationGuideDecision::kUnknown;
-  }
-
-  optimization_guide::OptimizationTargetDecision optimization_target_decision =
-      prediction_manager_->ShouldTargetNavigation(
-          navigation_handle, optimization_target,
-          /*override_client_model_feature_values=*/{});
-
-  base::UmaHistogramExactLinear(
-      "OptimizationGuide.TargetDecision." +
-          GetStringNameForOptimizationTarget(optimization_target),
-      static_cast<int>(optimization_target_decision),
-      static_cast<int>(
-          optimization_guide::OptimizationTargetDecision::kMaxValue));
-  return GetOptimizationGuideDecisionFromOptimizationTargetDecision(
-      optimization_target_decision);
-}
-
 void OptimizationGuideKeyedService::ShouldTargetNavigationAsync(
     content::NavigationHandle* navigation_handle,
     optimization_guide::proto::OptimizationTarget optimization_target,
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index 80d6891..978654b0 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -54,10 +54,6 @@
   void RegisterOptimizationTargets(
       const std::vector<optimization_guide::proto::OptimizationTarget>&
           optimization_targets) override;
-  optimization_guide::OptimizationGuideDecision ShouldTargetNavigation(
-      content::NavigationHandle* navigation_handle,
-      optimization_guide::proto::OptimizationTarget optimization_target)
-      override;
   void ShouldTargetNavigationAsync(
       content::NavigationHandle* navigation_handle,
       optimization_guide::proto::OptimizationTarget optimization_target,
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 599642d..fb3d34b 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -196,9 +196,6 @@
     OptimizationGuideKeyedService* service =
         OptimizationGuideKeyedServiceFactory::GetForProfile(
             Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-    last_should_target_decision_ = service->ShouldTargetNavigation(
-        navigation_handle,
-        optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
     if (callback_) {
       // Intentionally do not set client model feature values to override to
       // make sure decisions are the same in both sync and async variants.
@@ -214,15 +211,8 @@
     callback_ = std::move(callback);
   }
 
-  optimization_guide::OptimizationGuideDecision last_should_target_decision()
-      const {
-    return last_should_target_decision_;
-  }
-
  private:
   optimization_guide::OptimizationGuideTargetDecisionCallback callback_;
-  optimization_guide::OptimizationGuideDecision last_should_target_decision_ =
-      optimization_guide::OptimizationGuideDecision::kUnknown;
 };
 
 }  // namespace
@@ -537,6 +527,8 @@
       &histogram_tester,
       "OptimizationGuide.PredictionManager.PredictionModelsStored", 1);
 
+  SetCallbackOnConsumer(base::BindOnce(
+      [](optimization_guide::OptimizationGuideDecision decision) {}));
   ui_test_utils::NavigateToURL(browser(), https_url_with_content());
 
   histogram_tester.ExpectUniqueSample(
@@ -548,6 +540,8 @@
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.ClearHostModelFeatures.StoreAvailable", true, 1);
 
+  SetCallbackOnConsumer(base::BindOnce(
+      [](optimization_guide::OptimizationGuideDecision decision) {}));
   ui_test_utils::NavigateToURL(browser(), https_url_with_content());
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.PredictionManager.HasHostModelFeaturesForHost", false,
@@ -589,6 +583,8 @@
       &histogram_tester,
       "OptimizationGuide.PredictionManager.PredictionModelsStored", 1);
 
+  SetCallbackOnConsumer(base::BindOnce(
+      [](optimization_guide::OptimizationGuideDecision decision) {}));
   ui_test_utils::NavigateToURL(browser(), https_url_with_content());
   RetryForHistogramUntilCountReached(
       &histogram_tester, "OptimizationGuide.PredictionManager.IsSameOrigin", 1);
@@ -597,6 +593,8 @@
 
   // Navigate to the same URL in the same tab. This should count as a
   // same-origin navigation.
+  SetCallbackOnConsumer(base::BindOnce(
+      [](optimization_guide::OptimizationGuideDecision decision) {}));
   ui_test_utils::NavigateToURL(browser(), https_url_with_content());
   RetryForHistogramUntilCountReached(
       &histogram_tester, "OptimizationGuide.PredictionManager.IsSameOrigin", 2);
@@ -607,6 +605,8 @@
 
   // Navigate to a cross-origin URL. This should count as a cross-origin
   // navigation.
+  SetCallbackOnConsumer(base::BindOnce(
+      [](optimization_guide::OptimizationGuideDecision decision) {}));
   ui_test_utils::NavigateToURL(browser(), GURL("https://www.google.com/"));
   RetryForHistogramUntilCountReached(
       &histogram_tester, "OptimizationGuide.PredictionManager.IsSameOrigin", 3);
@@ -618,8 +618,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     PredictionManagerBrowserSameOriginTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(
-        ShouldTargetNavigationAsyncAndSyncDecisionAreTheSameWithoutOverrides)) {
+    DISABLE_ON_WIN_MAC_CHROMEOS(ShouldTargetNavigationAsync)) {
   base::HistogramTester histogram_tester;
 
   RegisterWithKeyedService();
@@ -643,7 +642,10 @@
       [](base::RunLoop* run_loop,
          OptimizationGuideConsumerWebContentsObserver* consumer,
          optimization_guide::OptimizationGuideDecision decision) {
-        EXPECT_EQ(consumer->last_should_target_decision(), decision);
+        // The model should be evaluated with an actual decision since the model
+        // and all features provided are valid.
+        EXPECT_NE(decision,
+                  optimization_guide::OptimizationGuideDecision::kUnknown);
         run_loop->Quit();
       },
       run_loop.get(), consumer()));
diff --git a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
index d60c911..702946e4 100644
--- a/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
+++ b/chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/performance_manager/policies/background_tab_loading_policy.h"
 #include "chrome/browser/performance_manager/policies/high_pmf_memory_pressure_policy.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
-#include "chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h"
 #include "chrome/browser/performance_manager/policies/working_set_trimmer_policy.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/performance_manager/embedder/performance_manager_lifetime.h"
@@ -43,6 +42,8 @@
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_ANDROID)
+#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
+#include "chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h"
 #include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
 #endif  // !defined(OS_ANDROID)
 
@@ -105,6 +106,8 @@
   if (base::FeatureList::IsEnabled(
           performance_manager::features::
               kUrgentDiscardingFromPerformanceManager)) {
+    graph->PassToGraph(std::make_unique<
+                       performance_manager::policies::PageDiscardingHelper>());
     graph->PassToGraph(
         std::make_unique<
             performance_manager::policies::UrgentPageDiscardingPolicy>());
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.cc b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
new file mode 100644
index 0000000..2412a041
--- /dev/null
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
@@ -0,0 +1,317 @@
+// 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/performance_manager/policies/page_discarding_helper.h"
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
+#include "chrome/browser/performance_manager/policies/policy_features.h"
+#include "components/performance_manager/graph/node_attached_data_impl.h"
+#include "components/performance_manager/graph/page_node_impl.h"
+#include "components/performance_manager/public/graph/frame_node.h"
+#include "components/performance_manager/public/graph/graph_operations.h"
+#include "components/performance_manager/public/graph/node_data_describer_registry.h"
+#include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/graph/process_node.h"
+#include "url/gurl.h"
+
+namespace performance_manager {
+namespace policies {
+namespace {
+
+#if !defined(OS_CHROMEOS)
+// Time during which non visible pages are protected from urgent discarding
+// (not on ChromeOS).
+constexpr base::TimeDelta kNonVisiblePagesUrgentProtectionTime =
+    base::TimeDelta::FromMinutes(10);
+#endif
+
+// Time during which a tab cannot be discarded after having played audio.
+constexpr base::TimeDelta kTabAudioProtectionTime =
+    base::TimeDelta::FromMinutes(1);
+
+// NodeAttachedData used to indicate that there's already been an attempt to
+// discard a PageNode.
+// TODO(sebmarchand): The only reason for a discard attempt to fail is if we try
+// to discard a prerenderer, remove this once we can detect if a PageNode is a
+// prerenderer in |CanUrgentlyDiscard|.
+class DiscardAttemptMarker : public NodeAttachedDataImpl<DiscardAttemptMarker> {
+ public:
+  struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
+  ~DiscardAttemptMarker() override = default;
+
+ private:
+  friend class ::performance_manager::NodeAttachedDataImpl<
+      DiscardAttemptMarker>;
+  explicit DiscardAttemptMarker(const PageNodeImpl* page_node) {}
+};
+
+const char kDescriberName[] = "PageDiscardingHelper";
+
+}  // namespace
+
+PageDiscardingHelper::PageDiscardingHelper()
+    : page_discarder_(std::make_unique<mechanism::PageDiscarder>()) {}
+PageDiscardingHelper::~PageDiscardingHelper() = default;
+
+void PageDiscardingHelper::UrgentlyDiscardAPage(
+    features::UrgentDiscardingParams::DiscardStrategy discard_strategy,
+    base::OnceCallback<void(bool)> post_discard_cb) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::flat_map<const PageNode*, uint64_t> discardable_pages;
+  base::TimeDelta oldest_bg_time;
+  const PageNode* oldest_bg_discardable_page_node = nullptr;
+  const PageNode* discard_candidate = nullptr;
+  // Find all the pages that could be discarded.
+  for (const auto* page_node : graph_->GetAllPageNodes()) {
+    if (!CanUrgentlyDiscard(page_node))
+      continue;
+    discardable_pages.emplace(page_node, 0);
+    // Track the discardable page that has been in background for the longest
+    // period of time.
+    if (page_node->GetTimeSinceLastVisibilityChange() > oldest_bg_time) {
+      oldest_bg_time = page_node->GetTimeSinceLastVisibilityChange();
+      oldest_bg_discardable_page_node = page_node;
+    }
+  }
+
+  UMA_HISTOGRAM_COUNTS_100("Discarding.DiscardCandidatesCount",
+                           discardable_pages.size());
+
+  if (discardable_pages.empty()) {
+    std::move(post_discard_cb).Run(false);
+    return;
+  }
+
+  if (discard_strategy ==
+      features::UrgentDiscardingParams::DiscardStrategy::LRU) {
+    discard_candidate = oldest_bg_discardable_page_node;
+  } else if (discard_strategy ==
+             features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS) {
+    // List all the processes associated with these page nodes.
+    base::flat_set<const ProcessNode*> process_nodes;
+    for (const auto& iter : discardable_pages) {
+      auto processes = GraphOperations::GetAssociatedProcessNodes(iter.first);
+      process_nodes.insert(processes.begin(), processes.end());
+    }
+
+    uint64_t largest_resident_set_kb = 0;
+    const PageNode* largest_page_node = nullptr;
+    // Compute the resident set of each page by simply summing up the estimated
+    // resident set of all its frames, find the largest one.
+    for (const ProcessNode* process_node : process_nodes) {
+      auto process_frames = process_node->GetFrameNodes();
+      uint64_t frame_rss_kb = 0;
+      // Get the resident set of the process and split it equally across its
+      // frames.
+      if (process_frames.size())
+        frame_rss_kb = process_node->GetResidentSetKb() / process_frames.size();
+      for (const FrameNode* frame_node : process_frames) {
+        // Check if the frame belongs to a discardable page, if so update the
+        // resident set of the page.
+        auto iter = discardable_pages.find(frame_node->GetPageNode());
+        if (iter == discardable_pages.end())
+          continue;
+        iter->second += frame_rss_kb;
+        if (iter->second > largest_resident_set_kb) {
+          largest_resident_set_kb = iter->second;
+          largest_page_node = iter->first;
+        }
+      }
+    }
+    if (largest_page_node) {
+      // Only report the memory usage metrics if we can compare them.
+      UMA_HISTOGRAM_COUNTS_1000("Discarding.LargestTabFootprint",
+                                discardable_pages[largest_page_node] / 1024);
+      UMA_HISTOGRAM_COUNTS_1000(
+          "Discarding.OldestTabFootprint",
+          discardable_pages[oldest_bg_discardable_page_node] / 1024);
+      discard_candidate = largest_page_node;
+    } else {
+      discard_candidate = oldest_bg_discardable_page_node;
+    }
+  }
+
+  // Adorns the PageNode with a discard attempt marker to make sure that we
+  // don't try to discard it multiple times if it fails to be discarded. In
+  // practice this should only happen to prerenderers.
+  DiscardAttemptMarker::GetOrCreate(PageNodeImpl::FromNode(discard_candidate));
+
+  page_discarder_->DiscardPageNode(
+      discard_candidate,
+      base::BindOnce(&PageDiscardingHelper::PostDiscardAttemptCallback,
+                     weak_factory_.GetWeakPtr(), discard_strategy,
+                     std::move(post_discard_cb)));
+}
+
+void PageDiscardingHelper::OnBeforePageNodeRemoved(const PageNode* page_node) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  last_change_to_non_audible_time_.erase(page_node);
+}
+
+void PageDiscardingHelper::OnIsAudibleChanged(const PageNode* page_node) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!page_node->IsAudible())
+    last_change_to_non_audible_time_[page_node] = base::TimeTicks::Now();
+}
+
+void PageDiscardingHelper::SetMockDiscarderForTesting(
+    std::unique_ptr<mechanism::PageDiscarder> discarder) {
+  page_discarder_ = std::move(discarder);
+}
+
+void PageDiscardingHelper::AdornsPageWithDiscardAttemptMarkerForTesting(
+    PageNode* page_node) {
+  DiscardAttemptMarker::GetOrCreate(PageNodeImpl::FromNode(page_node));
+}
+
+void PageDiscardingHelper::OnPassedToGraph(Graph* graph) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  graph_ = graph;
+  graph->AddPageNodeObserver(this);
+  graph->RegisterObject(this);
+  graph->GetNodeDataDescriberRegistry()->RegisterDescriber(this,
+                                                           kDescriberName);
+}
+
+void PageDiscardingHelper::OnTakenFromGraph(Graph* graph) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  graph->GetNodeDataDescriberRegistry()->UnregisterDescriber(this);
+  graph->UnregisterObject(this);
+  graph->RemovePageNodeObserver(this);
+  graph_ = nullptr;
+}
+
+const PageLiveStateDecorator::Data*
+PageDiscardingHelper::GetPageNodeLiveStateData(
+    const PageNode* page_node) const {
+  return PageLiveStateDecorator::Data::FromPageNode(page_node);
+}
+
+bool PageDiscardingHelper::CanUrgentlyDiscard(const PageNode* page_node) const {
+  if (page_node->IsVisible())
+    return false;
+  if (page_node->IsAudible())
+    return false;
+
+  if (DiscardAttemptMarker::Get(PageNodeImpl::FromNode(page_node)))
+    return false;
+
+  // Don't discard tabs that have recently played audio.
+  auto it = last_change_to_non_audible_time_.find(page_node);
+  if (it != last_change_to_non_audible_time_.end()) {
+    if (base::TimeTicks::Now() - it->second < kTabAudioProtectionTime)
+      return false;
+  }
+
+#if !defined(OS_CHROMEOS)
+  if (page_node->GetTimeSinceLastVisibilityChange() <
+      kNonVisiblePagesUrgentProtectionTime) {
+    return false;
+  }
+#endif
+
+  // Do not discard PDFs as they might contain entry that is not saved and they
+  // don't remember their scrolling positions. See crbug.com/547286 and
+  // crbug.com/65244.
+  if (page_node->GetContentsMimeType() == "application/pdf")
+    return false;
+
+  // Don't discard tabs that don't have a main frame yet.
+  auto* main_frame = page_node->GetMainFrameNode();
+  if (!main_frame)
+    return false;
+
+  // Only discard http(s) pages and internal pages to make sure that we don't
+  // discard extensions or other PageNode that don't correspond to a tab.
+  bool is_web_page_or_internal_page =
+      main_frame->GetURL().SchemeIsHTTPOrHTTPS() ||
+      main_frame->GetURL().SchemeIs("chrome");
+  if (!is_web_page_or_internal_page)
+    return false;
+
+  if (!main_frame->GetURL().is_valid() || main_frame->GetURL().is_empty())
+    return false;
+
+  const auto* live_state_data = GetPageNodeLiveStateData(page_node);
+
+  // The live state data won't be available if none of these events ever
+  // happened on the page.
+  if (live_state_data) {
+    if (!live_state_data->IsAutoDiscardable())
+      return false;
+    if (live_state_data->IsCapturingVideo())
+      return false;
+    if (live_state_data->IsCapturingAudio())
+      return false;
+    if (live_state_data->IsBeingMirrored())
+      return false;
+    if (live_state_data->IsCapturingDesktop())
+      return false;
+    if (live_state_data->IsConnectedToBluetoothDevice())
+      return false;
+    if (live_state_data->IsConnectedToUSBDevice())
+      return false;
+#if !defined(OS_CHROMEOS)
+    // TODO(sebmarchand): Skip this check if the Entreprise memory limit is set.
+    if (live_state_data->WasDiscarded())
+      return false;
+      // TODO(sebmarchand): Consider resetting the |WasDiscarded| value when the
+      // main frame document changes, also remove the DiscardAttemptMarker in
+      // this case.
+#endif
+  }
+
+  if (page_node->HadFormInteraction())
+    return false;
+
+  // TODO(sebmarchand): Do not discard pages if they're connected to DevTools.
+
+  // TODO(sebmarchand): Do not discard crashed tabs.
+
+  // TODO(sebmarchand): Do not discard tabs that are the active ones in a tab
+  // strip.
+
+  // TODO(sebmarchand): Do not try to discard PageNode not attached to a tab
+  // strip.
+
+  return true;
+}
+
+base::Value PageDiscardingHelper::DescribePageNodeData(
+    const PageNode* node) const {
+  auto* data = DiscardAttemptMarker::Get(PageNodeImpl::FromNode(node));
+  if (data == nullptr)
+    return base::Value();
+
+  base::Value ret(base::Value::Type::DICTIONARY);
+  ret.SetKey("has_discard_attempt_marker", base::Value("true"));
+
+  return ret;
+}
+
+void PageDiscardingHelper::PostDiscardAttemptCallback(
+    features::UrgentDiscardingParams::DiscardStrategy discard_strategy,
+    base::OnceCallback<void(bool)> post_discard_cb,
+    bool success) {
+  if (!success) {
+    // Try to discard another page.
+    UrgentlyDiscardAPage(discard_strategy, std::move(post_discard_cb));
+    return;
+  }
+
+  std::move(post_discard_cb).Run(true);
+}
+
+}  // namespace policies
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.h b/chrome/browser/performance_manager/policies/page_discarding_helper.h
new file mode 100644
index 0000000..48b3d16
--- /dev/null
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.h
@@ -0,0 +1,104 @@
+// 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_PERFORMANCE_MANAGER_POLICIES_PAGE_DISCARDING_HELPER_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_PAGE_DISCARDING_HELPER_H_
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/performance_manager/policies/policy_features.h"
+#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
+#include "components/performance_manager/public/graph/graph.h"
+#include "components/performance_manager/public/graph/graph_registered.h"
+#include "components/performance_manager/public/graph/node_data_describer.h"
+#include "components/performance_manager/public/graph/page_node.h"
+
+namespace performance_manager {
+
+namespace mechanism {
+class PageDiscarder;
+}  // namespace mechanism
+
+namespace policies {
+
+// Helper class to be used by the policies that want to discard tabs.
+//
+// This is a GraphRegistered object and should be accessed via
+// PageDiscardingHelper::GetFromGraph(graph()).
+class PageDiscardingHelper : public GraphOwned,
+                             public PageNode::ObserverDefaultImpl,
+                             public GraphRegisteredImpl<PageDiscardingHelper>,
+                             public NodeDataDescriberDefaultImpl {
+ public:
+  PageDiscardingHelper();
+  ~PageDiscardingHelper() override;
+  PageDiscardingHelper(const PageDiscardingHelper& other) = delete;
+  PageDiscardingHelper& operator=(const PageDiscardingHelper&) = delete;
+
+  // Selects a tab to discard based on |strategy| and posts to the UI thread to
+  // discard it. This will try to discard a tab until there's been a successful
+  // discard or until there's no more discard candidate.
+  void UrgentlyDiscardAPage(
+      features::UrgentDiscardingParams::DiscardStrategy discard_strategy,
+      base::OnceCallback<void(bool)> post_discard_cb);
+
+  // PageNodeObserver:
+  void OnBeforePageNodeRemoved(const PageNode* page_node) override;
+  void OnIsAudibleChanged(const PageNode* page_node) override;
+
+  void SetMockDiscarderForTesting(
+      std::unique_ptr<mechanism::PageDiscarder> discarder);
+  bool CanUrgentlyDiscardForTesting(const PageNode* page_node) const {
+    return CanUrgentlyDiscard(page_node);
+  }
+  void AdornsPageWithDiscardAttemptMarkerForTesting(PageNode* page_node);
+  void SetGraphForTesting(Graph* graph) { graph_ = graph; }
+
+ protected:
+  void OnPassedToGraph(Graph* graph) override;
+  void OnTakenFromGraph(Graph* graph) override;
+
+  // Returns the PageLiveStateDecorator::Data associated with a PageNode.
+  // Exposed and made virtual to allowed injecting some fake data in tests.
+  virtual const PageLiveStateDecorator::Data* GetPageNodeLiveStateData(
+      const PageNode* page_node) const;
+
+ private:
+  // Indicates if a PageNode can be urgently discarded.
+  bool CanUrgentlyDiscard(const PageNode* page_node) const;
+
+  // NodeDataDescriber implementation:
+  base::Value DescribePageNodeData(const PageNode* node) const override;
+
+  // Called after each discard attempt. |success| will indicate whether or not
+  // the attempt has been successful. |post_discard_cb| will be called once
+  // there's been a successful discard or if there's no more discard candidates.
+  void PostDiscardAttemptCallback(
+      features::UrgentDiscardingParams::DiscardStrategy discard_strategy,
+      base::OnceCallback<void(bool)> post_discard_cb,
+      bool success);
+
+  // Map that associates a PageNode with the last time it became non audible.
+  // PageNodes that have never been audible are not present in this map.
+  base::flat_map<const PageNode*, base::TimeTicks>
+      last_change_to_non_audible_time_;
+
+  // The mechanism used to do the actual discarding.
+  std::unique_ptr<performance_manager::mechanism::PageDiscarder>
+      page_discarder_;
+
+  Graph* graph_ = nullptr;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<PageDiscardingHelper> weak_factory_{this};
+};
+
+}  // namespace policies
+
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_PAGE_DISCARDING_HELPER_H_
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
new file mode 100644
index 0000000..d6f2f62
--- /dev/null
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
@@ -0,0 +1,390 @@
+// 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/performance_manager/policies/page_discarding_helper.h"
+
+#include <memory>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/time/time.h"
+#include "chrome/browser/performance_manager/policies/policy_features.h"
+#include "chrome/browser/performance_manager/test_support/page_discarding_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+namespace policies {
+
+using ::testing::Return;
+
+class PageDiscardingHelperTest
+    : public testing::GraphTestHarnessWithMockDiscarder {
+ public:
+  PageDiscardingHelperTest() = default;
+  ~PageDiscardingHelperTest() override = default;
+  PageDiscardingHelperTest(const PageDiscardingHelperTest& other) = delete;
+  PageDiscardingHelperTest& operator=(const PageDiscardingHelperTest&) = delete;
+
+  void SetUp() override {
+    testing::GraphTestHarnessWithMockDiscarder::SetUp();
+
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
+  }
+
+  void TearDown() override {
+    histogram_tester_.reset();
+    testing::GraphTestHarnessWithMockDiscarder::TearDown();
+  }
+
+ protected:
+  base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
+
+ private:
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardVisiblePage) {
+  page_node()->SetIsVisible(true);
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardAudiblePage) {
+  page_node()->SetIsAudible(true);
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest,
+       TestCannotDiscardPageWithDiscardAttemptMarker) {
+  PageDiscardingHelper::GetFromGraph(graph())
+      ->AdornsPageWithDiscardAttemptMarkerForTesting(page_node());
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardRecentlyAudiblePage) {
+  page_node()->SetIsAudible(true);
+  page_node()->SetIsAudible(false);
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardRecentlyVisiblePage) {
+  page_node()->SetIsVisible(true);
+  page_node()->SetIsVisible(false);
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+#endif
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPdf) {
+  page_node()->OnMainFrameNavigationCommitted(false, base::TimeTicks::Now(), 53,
+                                              GURL("https://foo.com/doc.pdf"),
+                                              "application/pdf");
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithoutMainFrame) {
+  ResetFrameNode();
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardExtension) {
+  frame_node()->OnNavigationCommitted(GURL("chrome-extention://foo"), false);
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithInvalidURL) {
+  frame_node()->OnNavigationCommitted(GURL("foo42"), false);
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageProtectedByExtension) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())
+      ->is_auto_discardable_ = false;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingVideo) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())
+      ->is_capturing_video_ = true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingAudio) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())
+      ->is_capturing_audio_ = true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageBeingMirrored) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())->is_being_mirrored_ =
+      true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageCapturingDesktop) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())
+      ->is_capturing_desktop_ = true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest,
+       TestCannotDiscardPageConnectedToBluetoothDevice) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())
+      ->is_connected_to_bluetooth_device_ = true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardIsConnectedToUSBDevice) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())
+      ->is_connected_to_usb_device_ = true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageMultipleTimes) {
+  testing::FakePageLiveStateData::GetOrCreate(page_node())->was_discarded_ =
+      true;
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+#endif
+
+TEST_F(PageDiscardingHelperTest, TestCannotDiscardPageWithFormInteractions) {
+  frame_node()->SetHadFormInteraction();
+  EXPECT_FALSE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+}
+
+class ParameterizedPageDiscardingHelperTest
+    : public PageDiscardingHelperTest,
+      public ::testing::WithParamInterface<
+          features::UrgentDiscardingParams::DiscardStrategy> {
+ public:
+  ParameterizedPageDiscardingHelperTest() = default;
+  ~ParameterizedPageDiscardingHelperTest() override = default;
+  ParameterizedPageDiscardingHelperTest(
+      const ParameterizedPageDiscardingHelperTest& other) = delete;
+  ParameterizedPageDiscardingHelperTest& operator=(
+      const ParameterizedPageDiscardingHelperTest&) = delete;
+};
+
+TEST_P(ParameterizedPageDiscardingHelperTest, UrgentlyDiscardAPageNoCandidate) {
+  page_node()->SetIsVisible(true);
+  PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+      GetParam(), base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
+}
+
+TEST_P(ParameterizedPageDiscardingHelperTest,
+       UrgentlyDiscardAPageSingleCandidate) {
+  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
+      .WillOnce(Return(true));
+  PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+      GetParam(), base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
+  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 1,
+                                        1);
+}
+
+TEST_P(ParameterizedPageDiscardingHelperTest,
+       UrgentlyDiscardAPageSingleCandidateFails) {
+  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
+      .WillOnce(Return(false));
+  PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+      GetParam(), base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
+  // There should be 2 discard attempts, during the first one an attempt will be
+  // made to discard |page_node()|, on the second attempt no discard candidate
+  // should be found.
+  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 1,
+                                        1);
+
+  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 0,
+                                        1);
+}
+
+TEST_P(ParameterizedPageDiscardingHelperTest,
+       UrgentlyDiscardAPageTwoCandidates) {
+  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
+  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
+  auto main_frame_node2 =
+      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
+  main_frame_node2->SetIsCurrent(true);
+  testing::MakePageNodeDiscardable(page_node2.get(), task_env());
+
+  // Pretend that |page_node2| is the most recently visible page.
+  page_node2->SetIsVisible(true);
+  AdvanceClock(base::TimeDelta::FromMinutes(30));
+  page_node2->SetIsVisible(false);
+  AdvanceClock(base::TimeDelta::FromMinutes(30));
+  EXPECT_TRUE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node2.get()));
+  EXPECT_GT(page_node()->TimeSinceLastVisibilityChange(),
+            page_node2->TimeSinceLastVisibilityChange());
+
+  process_node()->set_resident_set_kb(1024);
+  process_node2->set_resident_set_kb(2048);
+
+  if (GetParam() ==
+      features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS) {
+    // |page_node2| should be discarded despite being the most recently visible
+    // page as it has a bigger footprint.
+    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
+        .WillOnce(Return(true));
+
+    PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+        GetParam(), base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+    ::testing::Mock::VerifyAndClearExpectations(discarder());
+    histogram_tester()->ExpectUniqueSample("Discarding.LargestTabFootprint",
+                                           2048 / 1024, 1);
+    histogram_tester()->ExpectUniqueSample("Discarding.OldestTabFootprint",
+                                           1024 / 1024, 1);
+  } else {
+    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
+        .WillOnce(Return(true));
+
+    PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+        GetParam(), base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+    ::testing::Mock::VerifyAndClearExpectations(discarder());
+  }
+  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 2,
+                                        1);
+}
+
+TEST_P(ParameterizedPageDiscardingHelperTest,
+       UrgentlyDiscardAPageTwoCandidatesFirstFails) {
+  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
+  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
+  auto main_frame_node2 =
+      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
+  main_frame_node2->SetIsCurrent(true);
+  testing::MakePageNodeDiscardable(page_node2.get(), task_env());
+
+  process_node()->set_resident_set_kb(1024);
+  process_node2->set_resident_set_kb(2048);
+
+  // Pretends that the first discardable page hasn't been discarded
+  // successfully, the other one should be discarded in this case.
+  if (GetParam() ==
+      features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS) {
+    ::testing::InSequence in_sequence;
+    // The first candidate is the tab with the biggest RSS.
+    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
+        .WillOnce(Return(false));
+    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
+        .WillOnce(Return(true));
+  } else {
+    ::testing::InSequence in_sequence;
+    // The first candidate is the least recently used tab.
+    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
+        .WillOnce(Return(false));
+    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
+        .WillOnce(Return(true));
+  }
+
+  PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+      GetParam(), base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
+}
+
+TEST_P(ParameterizedPageDiscardingHelperTest,
+       UrgentlyDiscardAPageTwoCandidatesMultipleFrames) {
+  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
+  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
+  auto main_frame_node2 =
+      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
+  main_frame_node2->SetIsCurrent(true);
+  testing::MakePageNodeDiscardable(page_node2.get(), task_env());
+  // Adds a second frame to |page_node()| and host it in |process_node2|.
+  auto page_node1_extra_frame =
+      CreateFrameNodeAutoId(process_node2.get(), page_node(), frame_node());
+
+  process_node()->set_resident_set_kb(1024);
+  process_node2->set_resident_set_kb(2048);
+
+  // The total RSS of |page_node()| should be 1024 + 2048 / 2 = 2048 and the
+  // RSS of |page_node2| should be 2048 / 2 = 1024, so |page_node()| will get
+  // discarded despite having spent less time in background than |page_node2|.
+  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
+      .WillOnce(Return(true));
+
+  PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+      GetParam(), base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
+}
+
+TEST_P(ParameterizedPageDiscardingHelperTest,
+       UrgentlyDiscardAPageTwoCandidatesNoRSSData) {
+  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
+  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
+  auto main_frame_node2 =
+      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
+  main_frame_node2->SetIsCurrent(true);
+  testing::MakePageNodeDiscardable(page_node2.get(), task_env());
+
+  // Pretend that |page_node()| is the most recently visible page.
+  page_node()->SetIsVisible(true);
+  AdvanceClock(base::TimeDelta::FromMinutes(30));
+  page_node()->SetIsVisible(false);
+  AdvanceClock(base::TimeDelta::FromMinutes(30));
+  EXPECT_TRUE(
+      PageDiscardingHelper::GetFromGraph(graph())->CanUrgentlyDiscardForTesting(
+          page_node()));
+  EXPECT_GT(page_node2->TimeSinceLastVisibilityChange(),
+            page_node()->TimeSinceLastVisibilityChange());
+
+  // |page_node2| should be discarded as there's no RSS data for any of the
+  // pages and it's the least recently visible page.
+  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
+      .WillOnce(Return(true));
+
+  PageDiscardingHelper::GetFromGraph(graph())->UrgentlyDiscardAPage(
+      GetParam(), base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
+}
+
+INSTANTIATE_TEST_CASE_P(
+    PageDiscardingHelperWithParamTest,
+    ParameterizedPageDiscardingHelperTest,
+    ::testing::Values(
+        features::UrgentDiscardingParams::DiscardStrategy::LRU,
+        features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS));
+
+}  // namespace policies
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc
index 38b3073..1172a19 100644
--- a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc
+++ b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.cc
@@ -6,93 +6,28 @@
 
 #include <memory>
 
-#include "base/containers/flat_map.h"
-#include "base/containers/flat_set.h"
 #include "base/feature_list.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
+#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
-#include "components/performance_manager/graph/node_attached_data_impl.h"
-#include "components/performance_manager/graph/page_node_impl.h"
-#include "components/performance_manager/public/graph/frame_node.h"
-#include "components/performance_manager/public/graph/graph_operations.h"
-#include "components/performance_manager/public/graph/node_data_describer_registry.h"
-#include "components/performance_manager/public/graph/page_node.h"
-#include "components/performance_manager/public/graph/process_node.h"
-#include "url/gurl.h"
 
 namespace performance_manager {
 namespace policies {
-namespace {
 
-#if !defined(OS_CHROMEOS)
-// Time during which non visible pages are protected from urgent discarding
-// (not on ChromeOS).
-constexpr base::TimeDelta kNonVisiblePagesUrgentProtectionTime =
-    base::TimeDelta::FromMinutes(10);
-#endif
-
-// Time during which a tab cannot be discarded after having played audio.
-constexpr base::TimeDelta kTabAudioProtectionTime =
-    base::TimeDelta::FromMinutes(1);
-
-// NodeAttachedData used to indicate that there's already been an attempt to
-// discard a PageNode.
-// TODO(sebmarchand): The only reason for a discard attempt to fail is if we try
-// to discard a prerenderer, remove this once we can detect if a PageNode is a
-// prerenderer in |CanUrgentlyDiscard|.
-class DiscardAttemptMarker : public NodeAttachedDataImpl<DiscardAttemptMarker> {
- public:
-  struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
-  ~DiscardAttemptMarker() override = default;
-
- private:
-  friend class ::performance_manager::NodeAttachedDataImpl<
-      DiscardAttemptMarker>;
-  explicit DiscardAttemptMarker(const PageNodeImpl* page_node) {}
-};
-
-const char kDescriberName[] = "UrgentPageDiscardingPolicy";
-
-}  // namespace
-
-UrgentPageDiscardingPolicy::UrgentPageDiscardingPolicy()
-    : page_discarder_(std::make_unique<mechanism::PageDiscarder>()) {}
+UrgentPageDiscardingPolicy::UrgentPageDiscardingPolicy() = default;
 UrgentPageDiscardingPolicy::~UrgentPageDiscardingPolicy() = default;
 
 void UrgentPageDiscardingPolicy::OnPassedToGraph(Graph* graph) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   graph_ = graph;
   RegisterMemoryPressureListener();
-  graph->AddPageNodeObserver(this);
-  graph->GetNodeDataDescriberRegistry()->RegisterDescriber(this,
-                                                           kDescriberName);
 }
 
 void UrgentPageDiscardingPolicy::OnTakenFromGraph(Graph* graph) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  graph->GetNodeDataDescriberRegistry()->UnregisterDescriber(this);
-  graph->RemovePageNodeObserver(this);
   UnregisterMemoryPressureListener();
   graph_ = nullptr;
 }
 
-void UrgentPageDiscardingPolicy::OnBeforePageNodeRemoved(
-    const PageNode* page_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  last_change_to_non_audible_time_.erase(page_node);
-}
-
-void UrgentPageDiscardingPolicy::OnIsAudibleChanged(const PageNode* page_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!page_node->IsAudible())
-    last_change_to_non_audible_time_[page_node] = base::TimeTicks::Now();
-}
-
 void UrgentPageDiscardingPolicy::OnMemoryPressure(
     base::MemoryPressureListener::MemoryPressureLevel level) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -107,28 +42,21 @@
   // reply to multiple notifications at the same time.
   UnregisterMemoryPressureListener();
 
-  UrgentlyDiscardAPage();
-}
-
-void UrgentPageDiscardingPolicy::SetMockDiscarderForTesting(
-    std::unique_ptr<mechanism::PageDiscarder> discarder) {
-  page_discarder_ = std::move(discarder);
-}
-
-void UrgentPageDiscardingPolicy::AdornsPageWithDiscardAttemptMarkerForTesting(
-    PageNode* page_node) {
-  DiscardAttemptMarker::GetOrCreate(PageNodeImpl::FromNode(page_node));
-}
-
-const PageLiveStateDecorator::Data*
-UrgentPageDiscardingPolicy::GetPageNodeLiveStateData(
-    const PageNode* page_node) const {
-  return PageLiveStateDecorator::Data::FromPageNode(page_node);
+  PageDiscardingHelper::GetFromGraph(graph_)->UrgentlyDiscardAPage(
+      features::UrgentDiscardingParams::GetParams().discard_strategy(),
+      base::BindOnce(
+          [](UrgentPageDiscardingPolicy* policy, bool success_unused) {
+            policy->RegisterMemoryPressureListener();
+          },
+          base::Unretained(this)));
 }
 
 void UrgentPageDiscardingPolicy::RegisterMemoryPressureListener() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!memory_pressure_listener_);
+  DCHECK(PageDiscardingHelper::GetFromGraph(graph_))
+      << "A PageDiscardingHelper instance should be registered against the "
+         "graph in order to use this policy.";
 
   memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
       FROM_HERE,
@@ -141,211 +69,5 @@
   memory_pressure_listener_.reset();
 }
 
-bool UrgentPageDiscardingPolicy::CanUrgentlyDiscard(
-    const PageNode* page_node) const {
-  if (page_node->IsVisible())
-    return false;
-  if (page_node->IsAudible())
-    return false;
-
-  if (DiscardAttemptMarker::Get(PageNodeImpl::FromNode(page_node)))
-    return false;
-
-  // Don't discard tabs that have recently played audio.
-  auto it = last_change_to_non_audible_time_.find(page_node);
-  if (it != last_change_to_non_audible_time_.end()) {
-    if (base::TimeTicks::Now() - it->second < kTabAudioProtectionTime)
-      return false;
-  }
-
-#if !defined(OS_CHROMEOS)
-  if (page_node->GetTimeSinceLastVisibilityChange() <
-      kNonVisiblePagesUrgentProtectionTime) {
-    return false;
-  }
-#endif
-
-  // Do not discard PDFs as they might contain entry that is not saved and they
-  // don't remember their scrolling positions. See crbug.com/547286 and
-  // crbug.com/65244.
-  if (page_node->GetContentsMimeType() == "application/pdf")
-    return false;
-
-  // Don't discard tabs that don't have a main frame yet.
-  auto* main_frame = page_node->GetMainFrameNode();
-  if (!main_frame)
-    return false;
-
-  // Only discard http(s) pages and internal pages to make sure that we don't
-  // discard extensions or other PageNode that don't correspond to a tab.
-  bool is_web_page_or_internal_page =
-      main_frame->GetURL().SchemeIsHTTPOrHTTPS() ||
-      main_frame->GetURL().SchemeIs("chrome");
-  if (!is_web_page_or_internal_page)
-    return false;
-
-  if (!main_frame->GetURL().is_valid() || main_frame->GetURL().is_empty())
-    return false;
-
-  const auto* live_state_data = GetPageNodeLiveStateData(page_node);
-
-  // The live state data won't be available if none of these events ever
-  // happened on the page.
-  if (live_state_data) {
-    if (!live_state_data->IsAutoDiscardable())
-      return false;
-    if (live_state_data->IsCapturingVideo())
-      return false;
-    if (live_state_data->IsCapturingAudio())
-      return false;
-    if (live_state_data->IsBeingMirrored())
-      return false;
-    if (live_state_data->IsCapturingDesktop())
-      return false;
-    if (live_state_data->IsConnectedToBluetoothDevice())
-      return false;
-    if (live_state_data->IsConnectedToUSBDevice())
-      return false;
-#if !defined(OS_CHROMEOS)
-    // TODO(sebmarchand): Skip this check if the Entreprise memory limit is set.
-    if (live_state_data->WasDiscarded())
-      return false;
-      // TODO(sebmarchand): Consider resetting the |WasDiscarded| value when the
-      // main frame document changes, also remove the DiscardAttemptMarker in
-      // this case.
-#endif
-  }
-
-  if (page_node->HadFormInteraction())
-    return false;
-
-  // TODO(sebmarchand): Do not discard pages if they're connected to DevTools.
-
-  // TODO(sebmarchand): Do not discard crashed tabs.
-
-  // TODO(sebmarchand): Do not discard tabs that are the active ones in a tab
-  // strip.
-
-  // TODO(sebmarchand): Do not try to discard PageNode not attached to a tab
-  // strip.
-
-  return true;
-}
-
-void UrgentPageDiscardingPolicy::UrgentlyDiscardAPage() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // The memory pressure listener should always have been unregistered before
-  // calling this function.
-  DCHECK(!memory_pressure_listener_);
-
-  base::flat_map<const PageNode*, uint64_t> discardable_pages;
-  base::TimeDelta oldest_bg_time;
-  const PageNode* oldest_bg_discardable_page_node = nullptr;
-  const PageNode* discard_candidate = nullptr;
-  // Find all the pages that could be discarded.
-  for (const auto* page_node : graph_->GetAllPageNodes()) {
-    if (!CanUrgentlyDiscard(page_node))
-      continue;
-    discardable_pages.emplace(page_node, 0);
-    // Track the discardable page that has been in background for the longest
-    // period of time.
-    if (page_node->GetTimeSinceLastVisibilityChange() > oldest_bg_time) {
-      oldest_bg_time = page_node->GetTimeSinceLastVisibilityChange();
-      oldest_bg_discardable_page_node = page_node;
-    }
-  }
-
-  UMA_HISTOGRAM_COUNTS_100("Discarding.DiscardCandidatesCount",
-                           discardable_pages.size());
-
-  if (discardable_pages.empty()) {
-    RegisterMemoryPressureListener();
-    return;
-  }
-
-  auto discard_strategy =
-      features::UrgentDiscardingParams::GetParams().discard_strategy();
-  if (discard_strategy ==
-      features::UrgentDiscardingParams::DiscardStrategy::LRU) {
-    discard_candidate = oldest_bg_discardable_page_node;
-  } else if (discard_strategy ==
-             features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS) {
-    // List all the processes associated with these page nodes.
-    base::flat_set<const ProcessNode*> process_nodes;
-    for (const auto& iter : discardable_pages) {
-      auto processes = GraphOperations::GetAssociatedProcessNodes(iter.first);
-      process_nodes.insert(processes.begin(), processes.end());
-    }
-
-    uint64_t largest_resident_set_kb = 0;
-    const PageNode* largest_page_node = nullptr;
-    // Compute the resident set of each page by simply summing up the estimated
-    // resident set of all its frames, find the largest one.
-    for (const ProcessNode* process_node : process_nodes) {
-      auto process_frames = process_node->GetFrameNodes();
-      uint64_t frame_rss_kb = 0;
-      // Get the resident set of the process and split it equally across its
-      // frames.
-      if (process_frames.size())
-        frame_rss_kb = process_node->GetResidentSetKb() / process_frames.size();
-      for (const FrameNode* frame_node : process_frames) {
-        // Check if the frame belongs to a discardable page, if so update the
-        // resident set of the page.
-        auto iter = discardable_pages.find(frame_node->GetPageNode());
-        if (iter == discardable_pages.end())
-          continue;
-        iter->second += frame_rss_kb;
-        if (iter->second > largest_resident_set_kb) {
-          largest_resident_set_kb = iter->second;
-          largest_page_node = iter->first;
-        }
-      }
-    }
-    if (largest_page_node) {
-      // Only report the memory usage metrics if we can compare them.
-      UMA_HISTOGRAM_COUNTS_1000("Discarding.LargestTabFootprint",
-                                discardable_pages[largest_page_node] / 1024);
-      UMA_HISTOGRAM_COUNTS_1000(
-          "Discarding.OldestTabFootprint",
-          discardable_pages[oldest_bg_discardable_page_node] / 1024);
-      discard_candidate = largest_page_node;
-    } else {
-      discard_candidate = oldest_bg_discardable_page_node;
-    }
-  }
-
-  // Adorns the PageNode with a discard attempt marker to make sure that we
-  // don't try to discard it multiple times if it fails to be discarded. In
-  // practice this should only happen to prerenderers.
-  DiscardAttemptMarker::GetOrCreate(PageNodeImpl::FromNode(discard_candidate));
-
-  page_discarder_->DiscardPageNode(
-      discard_candidate,
-      base::BindOnce(&UrgentPageDiscardingPolicy::PostDiscardAttemptCallback,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void UrgentPageDiscardingPolicy::PostDiscardAttemptCallback(bool success) {
-  if (!success) {
-    // Try to discard another page.
-    UrgentlyDiscardAPage();
-    return;
-  }
-
-  RegisterMemoryPressureListener();
-}
-
-base::Value UrgentPageDiscardingPolicy::DescribePageNodeData(
-    const PageNode* node) const {
-  auto* data = DiscardAttemptMarker::Get(PageNodeImpl::FromNode(node));
-  if (data == nullptr)
-    return base::Value();
-
-  base::Value ret(base::Value::Type::DICTIONARY);
-  ret.SetKey("has_discard_attempt_marker", base::Value("true"));
-
-  return ret;
-}
-
 }  // namespace policies
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h
index 98eb41f..cae15044 100644
--- a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h
+++ b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h
@@ -5,31 +5,19 @@
 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_URGENT_PAGE_DISCARDING_POLICY_H_
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_URGENT_PAGE_DISCARDING_POLICY_H_
 
-#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/memory_pressure_listener.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
 #include "base/sequence_checker.h"
-#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
 #include "components/performance_manager/public/graph/graph.h"
-#include "components/performance_manager/public/graph/node_data_describer.h"
-#include "components/performance_manager/public/graph/page_node.h"
 
 namespace performance_manager {
 
-namespace mechanism {
-class PageDiscarder;
-}  // namespace mechanism
-
 namespace policies {
 
 // Urgently discard a tab when receiving a memory pressure signal. The discard
 // strategy used by this policy is based on a feature flag, see
 // UrgentDiscardingParams for more details.
-class UrgentPageDiscardingPolicy : public GraphOwned,
-                                   public PageNode::ObserverDefaultImpl,
-                                   public NodeDataDescriberDefaultImpl {
+class UrgentPageDiscardingPolicy : public GraphOwned {
  public:
   UrgentPageDiscardingPolicy();
   ~UrgentPageDiscardingPolicy() override;
@@ -41,23 +29,6 @@
   void OnPassedToGraph(Graph* graph) override;
   void OnTakenFromGraph(Graph* graph) override;
 
-  // PageNodeObserver:
-  void OnBeforePageNodeRemoved(const PageNode* page_node) override;
-  void OnIsAudibleChanged(const PageNode* page_node) override;
-
-  void SetMockDiscarderForTesting(
-      std::unique_ptr<mechanism::PageDiscarder> discarder);
-  bool CanUrgentlyDiscardForTesting(const PageNode* page_node) const {
-    return CanUrgentlyDiscard(page_node);
-  }
-  void AdornsPageWithDiscardAttemptMarkerForTesting(PageNode* page_node);
-
- protected:
-  // Returns the PageLiveStateDecorator::Data associated with a PageNode.
-  // Exposed and made virtual to allowed injecting some fake data in tests.
-  virtual const PageLiveStateDecorator::Data* GetPageNodeLiveStateData(
-      const PageNode* page_node) const;
-
  private:
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel level);
@@ -70,32 +41,13 @@
   // when handling a pressure event.
   void UnregisterMemoryPressureListener();
 
-  // Indicates if a PageNode can be urgently discarded.
-  bool CanUrgentlyDiscard(const PageNode* page_node) const;
-
-  // Selects a tab to discard and posts to the UI thread to discard it.
-  void UrgentlyDiscardAPage();
-
   // Callback called when a discard attempt has completed.
   void PostDiscardAttemptCallback(bool success);
 
-  // NodeDataDescriber implementation:
-  base::Value DescribePageNodeData(const PageNode* node) const override;
-
   std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
   Graph* graph_ = nullptr;
 
-  // Map that associates a PageNode with the last time it became non audible.
-  // PageNodes that have never been audible are not present in this map.
-  base::flat_map<const PageNode*, base::TimeTicks>
-      last_change_to_non_audible_time_;
-
-  std::unique_ptr<performance_manager::mechanism::PageDiscarder>
-      page_discarder_;
-
   SEQUENCE_CHECKER(sequence_checker_);
-
-  base::WeakPtrFactory<UrgentPageDiscardingPolicy> weak_factory_{this};
 };
 
 }  // namespace policies
diff --git a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy_unittest.cc b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy_unittest.cc
index de52518..770c0919 100644
--- a/chrome/browser/performance_manager/policies/urgent_page_discarding_policy_unittest.cc
+++ b/chrome/browser/performance_manager/policies/urgent_page_discarding_policy_unittest.cc
@@ -7,116 +7,21 @@
 #include <memory>
 
 #include "base/memory/memory_pressure_listener.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "base/util/memory_pressure/fake_memory_pressure_monitor.h"
 #include "chrome/browser/performance_manager/decorators/page_aggregator.h"
-#include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
-#include "components/performance_manager/graph/frame_node_impl.h"
-#include "components/performance_manager/graph/graph_impl.h"
-#include "components/performance_manager/graph/node_attached_data_impl.h"
-#include "components/performance_manager/graph/page_node_impl.h"
-#include "components/performance_manager/graph/process_node_impl.h"
-#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
-#include "components/performance_manager/test_support/graph_test_harness.h"
-#include "testing/gmock/include/gmock/gmock.h"
+#include "chrome/browser/performance_manager/test_support/page_discarding_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_manager {
 namespace policies {
 
-using ::testing::Return;
-
-// Class allowing setting some fake PageLiveStateDecorator::Data for a PageNode.
-class FakePageLiveStateData
-    : public PageLiveStateDecorator::Data,
-      public NodeAttachedDataImpl<FakePageLiveStateData> {
+class UrgentPageDiscardingPolicyTest
+    : public testing::GraphTestHarnessWithMockDiscarder {
  public:
-  struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
-  ~FakePageLiveStateData() override = default;
-  FakePageLiveStateData(const FakePageLiveStateData& other) = delete;
-  FakePageLiveStateData& operator=(const FakePageLiveStateData&) = delete;
-
-  // PageLiveStateDecorator::Data:
-  bool IsConnectedToUSBDevice() const override {
-    return is_connected_to_usb_device_;
-  }
-  bool IsConnectedToBluetoothDevice() const override {
-    return is_connected_to_bluetooth_device_;
-  }
-  bool IsCapturingVideo() const override { return is_capturing_video_; }
-  bool IsCapturingAudio() const override { return is_capturing_audio_; }
-  bool IsBeingMirrored() const override { return is_being_mirrored_; }
-  bool IsCapturingDesktop() const override { return is_capturing_desktop_; }
-  bool IsAutoDiscardable() const override { return is_auto_discardable_; }
-  bool WasDiscarded() const override { return was_discarded_; }
-
-  bool is_connected_to_usb_device_ = false;
-  bool is_connected_to_bluetooth_device_ = false;
-  bool is_capturing_video_ = false;
-  bool is_capturing_audio_ = false;
-  bool is_being_mirrored_ = false;
-  bool is_capturing_desktop_ = false;
-  bool is_auto_discardable_ = true;
-  bool was_discarded_ = false;
-
- private:
-  friend class ::performance_manager::NodeAttachedDataImpl<
-      FakePageLiveStateData>;
-
-  explicit FakePageLiveStateData(const PageNodeImpl* page_node) {}
-};
-
-// Mock version of a performance_manager::mechanism::PageDiscarder.
-class LenientMockPageDiscarder
-    : public performance_manager::mechanism::PageDiscarder {
- public:
-  LenientMockPageDiscarder() = default;
-  ~LenientMockPageDiscarder() override = default;
-  LenientMockPageDiscarder(const LenientMockPageDiscarder& other) = delete;
-  LenientMockPageDiscarder& operator=(const LenientMockPageDiscarder&) = delete;
-
-  MOCK_METHOD1(DiscardPageNodeImpl, bool(const PageNode* page_node));
-
- private:
-  void DiscardPageNode(
-      const PageNode* page_node,
-      base::OnceCallback<void(bool)> post_discard_cb) override {
-    std::move(post_discard_cb).Run(DiscardPageNodeImpl(page_node));
-  }
-};
-using MockPageDiscarder = ::testing::StrictMock<LenientMockPageDiscarder>;
-
-// Test version of the UrgentPageDiscardingPolicy.
-class TestUrgentPageDiscardingPolicy : public UrgentPageDiscardingPolicy {
- public:
-  TestUrgentPageDiscardingPolicy() = default;
-  ~TestUrgentPageDiscardingPolicy() override = default;
-  TestUrgentPageDiscardingPolicy(const TestUrgentPageDiscardingPolicy& other) =
-      delete;
-  TestUrgentPageDiscardingPolicy& operator=(
-      const TestUrgentPageDiscardingPolicy&) = delete;
-
- protected:
-  const PageLiveStateDecorator::Data* GetPageNodeLiveStateData(
-      const PageNode* page_node) const override {
-    // Returns a fake version of the PageLiveStateDecorator::Data, create it if
-    // it doesn't exist. Tests that want to set some fake live state data should
-    // call |FakePageLiveStateData::GetOrCreate|.
-    return FakePageLiveStateData::GetOrCreate(
-        PageNodeImpl::FromNode(page_node));
-  }
-};
-
-class UrgentPageDiscardingPolicyTest : public GraphTestHarness {
- public:
-  UrgentPageDiscardingPolicyTest() {
-    // Some tests depends on the existence of the PageAggregator.
-    graph()->PassToGraph(std::make_unique<PageAggregator>());
-  }
+  UrgentPageDiscardingPolicyTest() = default;
   ~UrgentPageDiscardingPolicyTest() override = default;
   UrgentPageDiscardingPolicyTest(const UrgentPageDiscardingPolicyTest& other) =
       delete;
@@ -124,33 +29,17 @@
       const UrgentPageDiscardingPolicyTest&) = delete;
 
   void SetUp() override {
+    testing::GraphTestHarnessWithMockDiscarder::SetUp();
+
     // Create the policy and pass it to the graph.
-    auto policy = std::make_unique<TestUrgentPageDiscardingPolicy>();
+    auto policy = std::make_unique<UrgentPageDiscardingPolicy>();
     policy_ = policy.get();
     graph()->PassToGraph(std::move(policy));
-
-    // Make the policy use a mock PageDiscarder.
-    auto mock_discarder = std::make_unique<MockPageDiscarder>();
-    mock_discarder_ = mock_discarder.get();
-    policy_->SetMockDiscarderForTesting(std::move(mock_discarder));
-
-    // Create a PageNode and make it discardable.
-    process_node_ = CreateNode<performance_manager::ProcessNodeImpl>();
-    page_node_ = CreateNode<performance_manager::PageNodeImpl>();
-    main_frame_node_ =
-        CreateFrameNodeAutoId(process_node_.get(), page_node_.get());
-    main_frame_node_->SetIsCurrent(true);
-    MakePageNodeDiscardable(page_node());
-
-    histogram_tester_ = std::make_unique<base::HistogramTester>();
   }
 
   void TearDown() override {
     graph()->TakeFromGraph(policy_);
-    main_frame_node_.reset();
-    page_node_.reset();
-    process_node_.reset();
-    histogram_tester_.reset();
+    testing::GraphTestHarnessWithMockDiscarder::TearDown();
   }
 
  protected:
@@ -167,350 +56,34 @@
     task_env().RunUntilIdle();
   }
 
-  // Make sure that |page_node| is discardable.
-  void MakePageNodeDiscardable(PageNodeImpl* page_node);
-
-  TestUrgentPageDiscardingPolicy* policy() { return policy_; }
-  PageNodeImpl* page_node() { return page_node_.get(); }
-  ProcessNodeImpl* process_node() { return process_node_.get(); }
-  FrameNodeImpl* frame_node() { return main_frame_node_.get(); }
-  void ResetFrameNode() { main_frame_node_.reset(); }
-  MockPageDiscarder* discarder() { return mock_discarder_; }
   util::test::FakeMemoryPressureMonitor* mem_pressure_monitor() {
     return &mem_pressure_monitor_;
   }
-  base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }
 
  private:
   util::test::FakeMemoryPressureMonitor mem_pressure_monitor_;
-  TestUrgentPageDiscardingPolicy* policy_;
-  MockPageDiscarder* mock_discarder_;
-  performance_manager::TestNodeWrapper<performance_manager::PageNodeImpl>
-      page_node_;
-  performance_manager::TestNodeWrapper<performance_manager::ProcessNodeImpl>
-      process_node_;
-  performance_manager::TestNodeWrapper<performance_manager::FrameNodeImpl>
-      main_frame_node_;
-  std::unique_ptr<base::HistogramTester> histogram_tester_;
+  UrgentPageDiscardingPolicy* policy_;
 };
 
-void UrgentPageDiscardingPolicyTest::MakePageNodeDiscardable(
-    PageNodeImpl* page_node) {
-  page_node->SetIsVisible(false);
-  page_node->SetIsAudible(false);
-  const auto kUrl = GURL("https://foo.com");
-  page_node->OnMainFrameNavigationCommitted(false, base::TimeTicks::Now(), 42,
-                                            kUrl, "text/html");
-  (*page_node->main_frame_nodes().begin())->OnNavigationCommitted(kUrl, false);
-  AdvanceClock(base::TimeDelta::FromMinutes(10));
-  EXPECT_TRUE(policy()->CanUrgentlyDiscardForTesting(page_node));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardVisiblePage) {
-  page_node()->SetIsVisible(true);
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardAudiblePage) {
-  page_node()->SetIsAudible(true);
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest,
-       TestCannotDiscardPageWithDiscardAttemptMarker) {
-  policy()->AdornsPageWithDiscardAttemptMarkerForTesting(page_node());
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardRecentlyAudiblePage) {
-  page_node()->SetIsAudible(true);
-  page_node()->SetIsAudible(false);
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-#if !defined(OS_CHROMEOS)
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardRecentlyVisiblePage) {
-  page_node()->SetIsVisible(true);
-  page_node()->SetIsVisible(false);
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-#endif
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPdf) {
-  page_node()->OnMainFrameNavigationCommitted(false, base::TimeTicks::Now(), 53,
-                                              GURL("https://foo.com/doc.pdf"),
-                                              "application/pdf");
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageWithoutMainFrame) {
-  ResetFrameNode();
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardExtension) {
-  frame_node()->OnNavigationCommitted(GURL("chrome-extention://foo"), false);
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageWithInvalidURL) {
-  frame_node()->OnNavigationCommitted(GURL("foo42"), false);
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest,
-       TestCannotDiscardPageProtectedByExtension) {
-  FakePageLiveStateData::GetOrCreate(page_node())->is_auto_discardable_ = false;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageCapturingVideo) {
-  FakePageLiveStateData::GetOrCreate(page_node())->is_capturing_video_ = true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageCapturingAudio) {
-  FakePageLiveStateData::GetOrCreate(page_node())->is_capturing_audio_ = true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageBeingMirrored) {
-  FakePageLiveStateData::GetOrCreate(page_node())->is_being_mirrored_ = true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageCapturingDesktop) {
-  FakePageLiveStateData::GetOrCreate(page_node())->is_capturing_desktop_ = true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest,
-       TestCannotDiscardPageConnectedToBluetoothDevice) {
-  FakePageLiveStateData::GetOrCreate(page_node())
-      ->is_connected_to_bluetooth_device_ = true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-TEST_F(UrgentPageDiscardingPolicyTest,
-       TestCannotDiscardIsConnectedToUSBDevice) {
-  FakePageLiveStateData::GetOrCreate(page_node())->is_connected_to_usb_device_ =
-      true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-#if !defined(OS_CHROMEOS)
-TEST_F(UrgentPageDiscardingPolicyTest, TestCannotDiscardPageMultipleTimes) {
-  FakePageLiveStateData::GetOrCreate(page_node())->was_discarded_ = true;
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-#endif
-
-TEST_F(UrgentPageDiscardingPolicyTest,
-       TestCannotDiscardPageWithFormInteractions) {
-  frame_node()->SetHadFormInteraction();
-  EXPECT_FALSE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-}
-
-class ParameterizedUrgentPageDiscardingPolicyTest
-    : public UrgentPageDiscardingPolicyTest,
-      public ::testing::WithParamInterface<
-          features::UrgentDiscardingParams::DiscardStrategy> {
- public:
-  ParameterizedUrgentPageDiscardingPolicyTest() {
-    base::FieldTrialParams params;
-    params[features::UrgentDiscardingParams::kDiscardStrategy.name] =
-        base::NumberToString(static_cast<int>(GetParam()));
-    feature_list_.InitAndEnableFeatureWithParameters(
-        features::kUrgentDiscardingFromPerformanceManager, params);
-  }
-  ~ParameterizedUrgentPageDiscardingPolicyTest() override = default;
-  ParameterizedUrgentPageDiscardingPolicyTest(
-      const ParameterizedUrgentPageDiscardingPolicyTest& other) = delete;
-  ParameterizedUrgentPageDiscardingPolicyTest& operator=(
-      const ParameterizedUrgentPageDiscardingPolicyTest&) = delete;
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageNoCandidate) {
-  page_node()->SetIsVisible(true);
-  SimulateMemoryPressure();
-  testing::Mock::VerifyAndClearExpectations(discarder());
-  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 0,
-                                        1);
-}
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageSingleCandidate) {
+TEST_F(UrgentPageDiscardingPolicyTest, DiscardOnCriticalPressure) {
   EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
-      .WillOnce(Return(true));
-  SimulateMemoryPressure();
-  testing::Mock::VerifyAndClearExpectations(discarder());
-  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 1,
-                                        1);
+      .WillOnce(::testing::Return(true));
+
+  mem_pressure_monitor()->SetAndNotifyMemoryPressure(
+      base::MemoryPressureListener::MemoryPressureLevel::
+          MEMORY_PRESSURE_LEVEL_CRITICAL);
+  task_env().RunUntilIdle();
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageSingleCandidateFails) {
-  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
-      .WillOnce(Return(false));
-  SimulateMemoryPressure();
-  testing::Mock::VerifyAndClearExpectations(discarder());
-  // There should be 2 discard attempts, during the first one an attempt will be
-  // made to discard |page_node()|, on the second attempt no discard candidate
-  // should be found.
-  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 1,
-                                        1);
-
-  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 0,
-                                        1);
-}
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageTwoCandidates) {
-  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
-  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
-  auto main_frame_node2 =
-      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
-  main_frame_node2->SetIsCurrent(true);
-  MakePageNodeDiscardable(page_node2.get());
-
-  // Pretend that |page_node2| is the most recently visible page.
-  page_node2->SetIsVisible(true);
-  AdvanceClock(base::TimeDelta::FromMinutes(30));
-  page_node2->SetIsVisible(false);
-  AdvanceClock(base::TimeDelta::FromMinutes(30));
-  EXPECT_TRUE(policy()->CanUrgentlyDiscardForTesting(page_node2.get()));
-  EXPECT_GT(page_node()->TimeSinceLastVisibilityChange(),
-            page_node2->TimeSinceLastVisibilityChange());
-
-  process_node()->set_resident_set_kb(1024);
-  process_node2->set_resident_set_kb(2048);
-
-  if (GetParam() ==
-      features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS) {
-    // |page_node2| should be discarded despite being the most recently visible
-    // page as it has a bigger footprint.
-    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
-        .WillOnce(Return(true));
-    SimulateMemoryPressure();
-    testing::Mock::VerifyAndClearExpectations(discarder());
-    histogram_tester()->ExpectUniqueSample("Discarding.LargestTabFootprint",
-                                           2048 / 1024, 1);
-    histogram_tester()->ExpectUniqueSample("Discarding.OldestTabFootprint",
-                                           1024 / 1024, 1);
-  } else {
-    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
-        .WillOnce(Return(true));
-    SimulateMemoryPressure();
-    testing::Mock::VerifyAndClearExpectations(discarder());
-  }
-  histogram_tester()->ExpectBucketCount("Discarding.DiscardCandidatesCount", 2,
-                                        1);
-}
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageTwoCandidatesFirstFails) {
-  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
-  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
-  auto main_frame_node2 =
-      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
-  main_frame_node2->SetIsCurrent(true);
-  MakePageNodeDiscardable(page_node2.get());
-
-  process_node()->set_resident_set_kb(1024);
-  process_node2->set_resident_set_kb(2048);
-
-  // Pretends that the first discardable page hasn't been discarded
-  // successfully, the other one should be discarded in this case.
-  if (GetParam() ==
-      features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS) {
-    ::testing::InSequence in_sequence;
-    // The first candidate is the tab with the biggest RSS.
-    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
-        .WillOnce(Return(false));
-    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
-        .WillOnce(Return(true));
-  } else {
-    ::testing::InSequence in_sequence;
-    // The first candidate is the least recently used tab.
-    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
-        .WillOnce(Return(false));
-    EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
-        .WillOnce(Return(true));
-  }
-  SimulateMemoryPressure();
-  testing::Mock::VerifyAndClearExpectations(discarder());
-}
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageTwoCandidatesMultipleFrames) {
-  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
-  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
-  auto main_frame_node2 =
-      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
-  main_frame_node2->SetIsCurrent(true);
-  MakePageNodeDiscardable(page_node2.get());
-  // Adds a second frame to |page_node()| and host it in |process_node2|.
-  auto page_node1_extra_frame =
-      CreateFrameNodeAutoId(process_node2.get(), page_node(), frame_node());
-
-  process_node()->set_resident_set_kb(1024);
-  process_node2->set_resident_set_kb(2048);
-
-  // The total RSS of |page_node()| should be 1024 + 2048 / 2 = 2048 and the
-  // RSS of |page_node2| should be 2048 / 2 = 1024, so |page_node()| will get
-  // discarded despite having spent less time in background than |page_node2|.
-  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node()))
-      .WillOnce(Return(true));
-  SimulateMemoryPressure();
-  testing::Mock::VerifyAndClearExpectations(discarder());
-}
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       UrgentlyDiscardAPageTwoCandidatesNoRSSData) {
-  auto process_node2 = CreateNode<performance_manager::ProcessNodeImpl>();
-  auto page_node2 = CreateNode<performance_manager::PageNodeImpl>();
-  auto main_frame_node2 =
-      CreateFrameNodeAutoId(process_node2.get(), page_node2.get());
-  main_frame_node2->SetIsCurrent(true);
-  MakePageNodeDiscardable(page_node2.get());
-
-  // Pretend that |page_node()| is the most recently visible page.
-  page_node()->SetIsVisible(true);
-  AdvanceClock(base::TimeDelta::FromMinutes(30));
-  page_node()->SetIsVisible(false);
-  AdvanceClock(base::TimeDelta::FromMinutes(30));
-  EXPECT_TRUE(policy()->CanUrgentlyDiscardForTesting(page_node()));
-  EXPECT_GT(page_node2->TimeSinceLastVisibilityChange(),
-            page_node()->TimeSinceLastVisibilityChange());
-
-  // |page_node2| should be discarded as there's no RSS data for any of the
-  // pages and it's the least recently visible page.
-  EXPECT_CALL(*discarder(), DiscardPageNodeImpl(page_node2.get()))
-      .WillOnce(Return(true));
-  SimulateMemoryPressure();
-  testing::Mock::VerifyAndClearExpectations(discarder());
-}
-
-TEST_P(ParameterizedUrgentPageDiscardingPolicyTest,
-       NoDiscardOnModeratePressure) {
+TEST_F(UrgentPageDiscardingPolicyTest, NoDiscardOnModeratePressure) {
   // No tab should be discarded on moderate pressure.
   mem_pressure_monitor()->SetAndNotifyMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel::
           MEMORY_PRESSURE_LEVEL_MODERATE);
   task_env().RunUntilIdle();
-  testing::Mock::VerifyAndClearExpectations(discarder());
+  ::testing::Mock::VerifyAndClearExpectations(discarder());
 }
 
-INSTANTIATE_TEST_CASE_P(
-    UrgentPageDiscardingPolicyWithParamTest,
-    ParameterizedUrgentPageDiscardingPolicyTest,
-    ::testing::Values(
-        features::UrgentDiscardingParams::DiscardStrategy::LRU,
-        features::UrgentDiscardingParams::DiscardStrategy::BIGGEST_RSS));
-
 }  // namespace policies
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/test_support/page_discarding_utils.cc b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
new file mode 100644
index 0000000..aa6f6228
--- /dev/null
+++ b/chrome/browser/performance_manager/test_support/page_discarding_utils.cc
@@ -0,0 +1,134 @@
+// 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/performance_manager/test_support/page_discarding_utils.h"
+
+#include "base/time/time.h"
+#include "chrome/browser/performance_manager/decorators/page_aggregator.h"
+#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
+#include "components/performance_manager/graph/frame_node_impl.h"
+#include "components/performance_manager/graph/page_node_impl.h"
+#include "components/performance_manager/test_support/graph_test_harness.h"
+
+namespace performance_manager {
+namespace testing {
+
+namespace {
+
+// Test version of the PageDiscardingHelper.
+class TestPageDiscardingHelper : public policies::PageDiscardingHelper {
+ public:
+  TestPageDiscardingHelper();
+  ~TestPageDiscardingHelper() override;
+  TestPageDiscardingHelper(const TestPageDiscardingHelper& other) = delete;
+  TestPageDiscardingHelper& operator=(const TestPageDiscardingHelper&) = delete;
+
+ protected:
+  const PageLiveStateDecorator::Data* GetPageNodeLiveStateData(
+      const PageNode* page_node) const override {
+    // Returns a fake version of the PageLiveStateDecorator::Data, create it if
+    // it doesn't exist. Tests that want to set some fake live state data should
+    // call |FakePageLiveStateData::GetOrCreate|.
+    return FakePageLiveStateData::GetOrCreate(
+        PageNodeImpl::FromNode(page_node));
+  }
+};
+
+}  // namespace
+
+FakePageLiveStateData::~FakePageLiveStateData() = default;
+
+// PageLiveStateDecorator::Data:
+bool FakePageLiveStateData::IsConnectedToUSBDevice() const {
+  return is_connected_to_usb_device_;
+}
+bool FakePageLiveStateData::IsConnectedToBluetoothDevice() const {
+  return is_connected_to_bluetooth_device_;
+}
+bool FakePageLiveStateData::IsCapturingVideo() const {
+  return is_capturing_video_;
+}
+bool FakePageLiveStateData::IsCapturingAudio() const {
+  return is_capturing_audio_;
+}
+bool FakePageLiveStateData::IsBeingMirrored() const {
+  return is_being_mirrored_;
+}
+bool FakePageLiveStateData::IsCapturingDesktop() const {
+  return is_capturing_desktop_;
+}
+bool FakePageLiveStateData::IsAutoDiscardable() const {
+  return is_auto_discardable_;
+}
+bool FakePageLiveStateData::WasDiscarded() const {
+  return was_discarded_;
+}
+
+FakePageLiveStateData::FakePageLiveStateData(const PageNodeImpl* page_node) {}
+
+LenientMockPageDiscarder::LenientMockPageDiscarder() = default;
+LenientMockPageDiscarder::~LenientMockPageDiscarder() = default;
+
+void LenientMockPageDiscarder::DiscardPageNode(
+    const PageNode* page_node,
+    base::OnceCallback<void(bool)> post_discard_cb) {
+  std::move(post_discard_cb).Run(DiscardPageNodeImpl(page_node));
+}
+
+TestPageDiscardingHelper::TestPageDiscardingHelper() = default;
+TestPageDiscardingHelper::~TestPageDiscardingHelper() = default;
+
+GraphTestHarnessWithMockDiscarder::GraphTestHarnessWithMockDiscarder() {
+  // Some tests depends on the existence of the PageAggregator.
+  graph()->PassToGraph(std::make_unique<PageAggregator>());
+}
+
+GraphTestHarnessWithMockDiscarder::~GraphTestHarnessWithMockDiscarder() =
+    default;
+
+void GraphTestHarnessWithMockDiscarder::SetUp() {
+  GraphTestHarness::SetUp();
+
+  // Make the policy use a mock PageDiscarder.
+  auto mock_discarder = std::make_unique<MockPageDiscarder>();
+  mock_discarder_ = mock_discarder.get();
+
+  // Create the helper and pass it to the graph.
+  auto page_discarding_helper = std::make_unique<TestPageDiscardingHelper>();
+  page_discarding_helper->SetMockDiscarderForTesting(std::move(mock_discarder));
+
+  graph()->PassToGraph(std::move(page_discarding_helper));
+  DCHECK(policies::PageDiscardingHelper::GetFromGraph(graph()));
+
+  // Create a PageNode and make it discardable.
+  process_node_ = CreateNode<performance_manager::ProcessNodeImpl>();
+  page_node_ = CreateNode<performance_manager::PageNodeImpl>();
+  main_frame_node_ =
+      CreateFrameNodeAutoId(process_node_.get(), page_node_.get());
+  main_frame_node_->SetIsCurrent(true);
+  MakePageNodeDiscardable(page_node(), task_env());
+}
+
+void GraphTestHarnessWithMockDiscarder::TearDown() {
+  main_frame_node_.reset();
+  page_node_.reset();
+  process_node_.reset();
+  GraphTestHarness::TearDown();
+}
+
+void MakePageNodeDiscardable(PageNodeImpl* page_node,
+                             content::BrowserTaskEnvironment& task_env) {
+  page_node->SetIsVisible(false);
+  page_node->SetIsAudible(false);
+  const auto kUrl = GURL("https://foo.com");
+  page_node->OnMainFrameNavigationCommitted(false, base::TimeTicks::Now(), 42,
+                                            kUrl, "text/html");
+  (*page_node->main_frame_nodes().begin())->OnNavigationCommitted(kUrl, false);
+  task_env.FastForwardBy(base::TimeDelta::FromMinutes(10));
+  DCHECK(policies::PageDiscardingHelper::GetFromGraph(page_node->graph())
+             ->CanUrgentlyDiscardForTesting(page_node));
+}
+
+}  // namespace testing
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/test_support/page_discarding_utils.h b/chrome/browser/performance_manager/test_support/page_discarding_utils.h
new file mode 100644
index 0000000..f11e17e
--- /dev/null
+++ b/chrome/browser/performance_manager/test_support/page_discarding_utils.h
@@ -0,0 +1,115 @@
+// 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_PERFORMANCE_MANAGER_TEST_SUPPORT_PAGE_DISCARDING_UTILS_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_TEST_SUPPORT_PAGE_DISCARDING_UTILS_H_
+
+#include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
+#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
+#include "components/performance_manager/graph/node_attached_data_impl.h"
+#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
+#include "components/performance_manager/test_support/graph_test_harness.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace performance_manager {
+
+class FrameNodeImpl;
+class PageNodeImpl;
+class ProcessNodeImpl;
+
+namespace testing {
+
+// Class allowing setting some fake PageLiveStateDecorator::Data for a PageNode.
+class FakePageLiveStateData
+    : public PageLiveStateDecorator::Data,
+      public NodeAttachedDataImpl<FakePageLiveStateData> {
+ public:
+  struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
+  ~FakePageLiveStateData() override;
+  FakePageLiveStateData(const FakePageLiveStateData& other) = delete;
+  FakePageLiveStateData& operator=(const FakePageLiveStateData&) = delete;
+
+  // PageLiveStateDecorator::Data:
+  bool IsConnectedToUSBDevice() const override;
+  bool IsConnectedToBluetoothDevice() const override;
+  bool IsCapturingVideo() const override;
+  bool IsCapturingAudio() const override;
+  bool IsBeingMirrored() const override;
+  bool IsCapturingDesktop() const override;
+  bool IsAutoDiscardable() const override;
+  bool WasDiscarded() const override;
+
+  bool is_connected_to_usb_device_ = false;
+  bool is_connected_to_bluetooth_device_ = false;
+  bool is_capturing_video_ = false;
+  bool is_capturing_audio_ = false;
+  bool is_being_mirrored_ = false;
+  bool is_capturing_desktop_ = false;
+  bool is_auto_discardable_ = true;
+  bool was_discarded_ = false;
+
+ private:
+  friend class ::performance_manager::NodeAttachedDataImpl<
+      FakePageLiveStateData>;
+
+  explicit FakePageLiveStateData(const PageNodeImpl* page_node);
+};
+
+// Mock version of a performance_manager::mechanism::PageDiscarder.
+class LenientMockPageDiscarder
+    : public performance_manager::mechanism::PageDiscarder {
+ public:
+  LenientMockPageDiscarder();
+  ~LenientMockPageDiscarder() override;
+  LenientMockPageDiscarder(const LenientMockPageDiscarder& other) = delete;
+  LenientMockPageDiscarder& operator=(const LenientMockPageDiscarder&) = delete;
+
+  MOCK_METHOD1(DiscardPageNodeImpl, bool(const PageNode* page_node));
+
+ private:
+  void DiscardPageNode(const PageNode* page_node,
+                       base::OnceCallback<void(bool)> post_discard_cb) override;
+};
+using MockPageDiscarder = ::testing::StrictMock<LenientMockPageDiscarder>;
+
+// Specialization of a GraphTestHarness that uses a MockPageDiscarder to
+// do the discard attempts.
+class GraphTestHarnessWithMockDiscarder : public GraphTestHarness {
+ public:
+  GraphTestHarnessWithMockDiscarder();
+  ~GraphTestHarnessWithMockDiscarder() override;
+  GraphTestHarnessWithMockDiscarder(
+      const GraphTestHarnessWithMockDiscarder& other) = delete;
+  GraphTestHarnessWithMockDiscarder& operator=(
+      const GraphTestHarnessWithMockDiscarder&) = delete;
+
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  PageNodeImpl* page_node() { return page_node_.get(); }
+  ProcessNodeImpl* process_node() { return process_node_.get(); }
+  FrameNodeImpl* frame_node() { return main_frame_node_.get(); }
+  void ResetFrameNode() { main_frame_node_.reset(); }
+  testing::MockPageDiscarder* discarder() { return mock_discarder_; }
+
+ private:
+  testing::MockPageDiscarder* mock_discarder_;
+  performance_manager::TestNodeWrapper<performance_manager::PageNodeImpl>
+      page_node_;
+  performance_manager::TestNodeWrapper<performance_manager::ProcessNodeImpl>
+      process_node_;
+  performance_manager::TestNodeWrapper<performance_manager::FrameNodeImpl>
+      main_frame_node_;
+};
+
+// Make sure that |page_node| is discardable.
+void MakePageNodeDiscardable(PageNodeImpl* page_node,
+                             content::BrowserTaskEnvironment& task_env);
+
+}  // namespace testing
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_TEST_SUPPORT_PAGE_DISCARDING_UTILS_H_
diff --git a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
index a871209..1d68e5f 100644
--- a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
+++ b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
@@ -137,7 +137,7 @@
     auto request = std::make_unique<permissions::MockPermissionRequest>(
         /*text*/ "test",
         permissions::PermissionRequestType::PERMISSION_GEOLOCATION, url);
-    manager_->AddRequest(request.get());
+    manager_->AddRequest(web_contents()->GetMainFrame(), request.get());
     return request;
   }
 #endif
@@ -155,7 +155,7 @@
 TEST_F(ChromePermissionRequestManagerTest, UMAForSimpleAcceptedGestureBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   histograms.ExpectUniqueSample(
       permissions::PermissionUmaUtil::kPermissionsPromptShown,
@@ -194,7 +194,7 @@
 TEST_F(ChromePermissionRequestManagerTest, UMAForSimpleDeniedNoGestureBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
   WaitForBubbleToBeShown();
 
   histograms.ExpectTotalCount(
@@ -233,8 +233,8 @@
 TEST_F(ChromePermissionRequestManagerTest, UMAForMergedAcceptedBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request_mic_);
-  manager_->AddRequest(&request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
   WaitForBubbleToBeShown();
 
   histograms.ExpectUniqueSample(
@@ -264,8 +264,8 @@
 TEST_F(ChromePermissionRequestManagerTest, UMAForMergedDeniedBubble) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request_mic_);
-  manager_->AddRequest(&request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
   WaitForBubbleToBeShown();
   histograms.ExpectTotalCount(
       "Permissions.Engagement.Denied.AudioAndVideoCapture", 0);
@@ -287,7 +287,7 @@
 TEST_F(ChromePermissionRequestManagerTest, UMAForIgnores) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   histograms.ExpectTotalCount("Permissions.Engagement.Ignored.Quota", 0);
 
@@ -299,7 +299,7 @@
   permissions::MockPermissionRequest youtube_request(
       "request2", permissions::PermissionRequestType::PERMISSION_GEOLOCATION,
       youtube);
-  manager_->AddRequest(&youtube_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &youtube_request);
   WaitForBubbleToBeShown();
 
   NavigateAndCommit(GURL("http://www.google.com/"));
@@ -342,7 +342,7 @@
     permissions::MockPermissionRequest notification_request(
         "request", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
         requesting_origin);
-    manager_->AddRequest(&notification_request);
+    manager_->AddRequest(web_contents()->GetMainFrame(), &notification_request);
     WaitForBubbleToBeShown();
     EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
     Deny();
@@ -364,7 +364,7 @@
     permissions::MockPermissionRequest notification_request(
         "request", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
         requesting_origin);
-    manager_->AddRequest(&notification_request);
+    manager_->AddRequest(web_contents()->GetMainFrame(), &notification_request);
     WaitForBubbleToBeShown();
     // Only show quiet UI after 3 consecutive denies of the permission prompt.
     EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
@@ -381,7 +381,7 @@
   permissions::MockPermissionRequest notification_request(
       "request2", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       requesting_origin);
-  manager_->AddRequest(&notification_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification_request);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   Accept();
@@ -431,7 +431,7 @@
     permissions::MockPermissionRequest notification_request(
         "request", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
         requesting_origin);
-    manager_->AddRequest(&notification_request);
+    manager_->AddRequest(web_contents()->GetMainFrame(), &notification_request);
     WaitForBubbleToBeShown();
     EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
     Deny();
@@ -448,7 +448,7 @@
   permissions::MockPermissionRequest notification_request(
       "request", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       requesting_origin);
-  manager_->AddRequest(&notification_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification_request);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
@@ -476,7 +476,7 @@
   permissions::MockPermissionRequest notification1_request(
       "request1", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification1);
-  manager_->AddRequest(&notification1_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification1_request);
   WaitForBubbleToBeShown();
   Deny();
 
@@ -485,7 +485,7 @@
   permissions::MockPermissionRequest notification2_request(
       "request2", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification2);
-  manager_->AddRequest(&notification2_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification2_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -495,7 +495,7 @@
   permissions::MockPermissionRequest notification3_request(
       "request3", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification3);
-  manager_->AddRequest(&notification3_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification3_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Accept();
@@ -506,7 +506,7 @@
   permissions::MockPermissionRequest notification4_request(
       "request4", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification4);
-  manager_->AddRequest(&notification4_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification4_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -516,7 +516,7 @@
   permissions::MockPermissionRequest notification5_request(
       "request5", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification5);
-  manager_->AddRequest(&notification5_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification5_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -525,14 +525,14 @@
   // other permissions should not.
   GURL camera_url("http://www.camera.com/");
   NavigateAndCommit(camera_url);
-  manager_->AddRequest(&request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
 
   GURL microphone_url("http://www.microphone.com/");
   NavigateAndCommit(microphone_url);
-  manager_->AddRequest(&request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -542,7 +542,7 @@
   permissions::MockPermissionRequest notification6_request(
       "request6", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification6);
-  manager_->AddRequest(&notification6_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification6_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -556,7 +556,7 @@
       notification7);
   // For the first quiet permission prompt, show a promo.
   EXPECT_TRUE(QuietNotificationPermissionUiState::ShouldShowPromo(profile()));
-  manager_->AddRequest(&notification7_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification7_request);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
@@ -576,7 +576,7 @@
       notification8);
   // For the rest of the quiet permission prompts, do not show promo.
   EXPECT_TRUE(QuietNotificationPermissionUiState::ShouldShowPromo(profile()));
-  manager_->AddRequest(&notification8_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification8_request);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
 
@@ -597,7 +597,7 @@
   permissions::MockPermissionRequest notification9_request(
       "request9", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification9);
-  manager_->AddRequest(&notification9_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification9_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -610,7 +610,7 @@
   permissions::MockPermissionRequest notification10_request(
       "request10", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification10);
-  manager_->AddRequest(&notification10_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification10_request);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Deny();
@@ -622,7 +622,7 @@
   permissions::MockPermissionRequest notification11_request(
       "request11", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       notification11);
-  manager_->AddRequest(&notification11_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &notification11_request);
   WaitForBubbleToBeShown();
   Deny();
 
diff --git a/chrome/browser/permissions/permission_prompt_android_unittest.cc b/chrome/browser/permissions/permission_prompt_android_unittest.cc
index 1f8dd672..2ddd7be 100644
--- a/chrome/browser/permissions/permission_prompt_android_unittest.cc
+++ b/chrome/browser/permissions/permission_prompt_android_unittest.cc
@@ -11,6 +11,7 @@
 #include "components/permissions/permission_request_manager.h"
 #include "components/permissions/test/mock_permission_request.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
 
 class PermissionPromptAndroidTest : public ChromeRenderViewHostTestHarness {
  public:
@@ -45,7 +46,8 @@
   // Create a notification request. This causes an infobar to appear.
   permissions::MockPermissionRequest request(
       "test", ContentSettingsType::NOTIFICATIONS);
-  permission_request_manager()->AddRequest(&request);
+  permission_request_manager()->AddRequest(web_contents()->GetMainFrame(),
+                                           &request);
 
   base::RunLoop().RunUntilIdle();
 
@@ -66,7 +68,8 @@
   // Create a notification request. This causes an infobar to appear.
   permissions::MockPermissionRequest request(
       "test", ContentSettingsType::NOTIFICATIONS);
-  permission_request_manager()->AddRequest(&request);
+  permission_request_manager()->AddRequest(web_contents()->GetMainFrame(),
+                                           &request);
 
   base::RunLoop().RunUntilIdle();
 
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index 015b9940..4d77cf2 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/back_forward_cache_util.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -110,9 +111,9 @@
     std::map<std::string, std::string> params;
     params[permissions::PermissionUtil::GetPermissionString(
         content_settings_type)] = kPermissionsKillSwitchBlockedValue;
-    variations::AssociateVariationParams(
-        kPermissionsKillSwitchFieldStudy, kPermissionsKillSwitchTestGroup,
-        params);
+    variations::AssociateVariationParams(kPermissionsKillSwitchFieldStudy,
+                                         kPermissionsKillSwitchTestGroup,
+                                         params);
     base::FieldTrialList::CreateFieldTrial(kPermissionsKillSwitchFieldStudy,
                                            kPermissionsKillSwitchTestGroup);
   }
@@ -182,6 +183,10 @@
     }
   }
 
+  content::RenderFrameHost* GetActiveMainFrame() {
+    return browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<permissions::MockPermissionPromptFactory>
@@ -221,6 +226,20 @@
   DISALLOW_COPY_AND_ASSIGN(PermissionDialogTest);
 };
 
+class PermissionRequestManagerWithBackForwardCacheBrowserTest
+    : public PermissionRequestManagerBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    PermissionRequestManagerBrowserTest::SetUpCommandLine(command_line);
+    feature_list_.InitAndEnableFeatureWithParameters(
+        features::kBackForwardCache,
+        {{"TimeToLiveInBackForwardCacheInSeconds", "3600"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 permissions::PermissionRequest*
 PermissionDialogTest::MakeRegisterProtocolHandlerRequest() {
   std::string protocol = "mailto";
@@ -273,9 +292,10 @@
   }
   permissions::PermissionRequestManager* manager =
       GetPermissionRequestManager();
+  content::RenderFrameHost* source_frame = GetActiveMainFrame();
   switch (it->type) {
     case ContentSettingsType::PROTOCOL_HANDLERS:
-      manager->AddRequest(MakeRegisterProtocolHandlerRequest());
+      manager->AddRequest(source_frame, MakeRegisterProtocolHandlerRequest());
       break;
     case ContentSettingsType::AUTOMATIC_DOWNLOADS:
       // TODO(tapted): Prompt for downloading multiple files.
@@ -291,15 +311,17 @@
     case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER:  // ChromeOS only.
     case ContentSettingsType::PPAPI_BROKER:
     case ContentSettingsType::PLUGINS:  // Flash.
-      manager->AddRequest(MakePermissionRequest(it->type));
+      manager->AddRequest(source_frame, MakePermissionRequest(it->type));
       break;
     case ContentSettingsType::DEFAULT:
       // Permissions to request for a "multiple" request. Only mic/camera
       // requests are grouped together.
       EXPECT_EQ(kMultipleName, name);
       manager->AddRequest(
+          source_frame,
           MakePermissionRequest(ContentSettingsType::MEDIASTREAM_MIC));
       manager->AddRequest(
+          source_frame,
           MakePermissionRequest(ContentSettingsType::MEDIASTREAM_CAMERA));
 
       break;
@@ -409,18 +431,14 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
-      browser(),
-      embedded_test_server()->GetURL("/empty.html"),
-      1);
+      browser(), embedded_test_server()->GetURL("/empty.html"), 1);
 
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
-      browser(),
-      embedded_test_server()->GetURL("/empty.html#0"),
-      1);
+      browser(), embedded_test_server()->GetURL("/empty.html#0"), 1);
 
   // Request 'geolocation' permission.
   ExecuteScriptAndGetValue(
-      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+      GetActiveMainFrame(),
       "navigator.geolocation.getCurrentPosition(function(){});");
   bubble_factory()->WaitForPermissionBubble();
 
@@ -704,8 +722,7 @@
       1);
   bubble_factory()->WaitForPermissionBubble();
 
-  content::RenderFrameHost* main_frame =
-      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+  content::RenderFrameHost* main_frame = GetActiveMainFrame();
   int main_frame_process_id = main_frame->GetProcess()->GetID();
   int main_frame_routing_id = main_frame->GetRoutingID();
 
@@ -747,12 +764,13 @@
 // Quiet permission requests are cancelled when a new request is made.
 IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                        DISABLED_QuietPendingRequestsKilledOnNewRequest) {
+  content::RenderFrameHost* source_frame = GetActiveMainFrame();
   // First add a quiet permission request. Ensure that this request is decided
   // by the end of this test.
   permissions::MockPermissionRequest request_quiet(
       "quiet", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       permissions::PermissionRequestGestureType::UNKNOWN);
-  GetPermissionRequestManager()->AddRequest(&request_quiet);
+  GetPermissionRequestManager()->AddRequest(source_frame, &request_quiet);
   base::RunLoop().RunUntilIdle();
 
   // Add a second permission request. This ones should cause the initial
@@ -760,7 +778,7 @@
   permissions::MockPermissionRequest request_loud(
       "loud", permissions::PermissionRequestType::PERMISSION_GEOLOCATION,
       permissions::PermissionRequestGestureType::UNKNOWN);
-  GetPermissionRequestManager()->AddRequest(&request_loud);
+  GetPermissionRequestManager()->AddRequest(source_frame, &request_loud);
   base::RunLoop().RunUntilIdle();
 
   // The first dialog should now have been decided.
@@ -810,7 +828,8 @@
     permissions::MockPermissionRequest request_quiet(
         "quiet", permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
         permissions::PermissionRequestGestureType::UNKNOWN);
-    GetPermissionRequestManager()->AddRequest(&request_quiet);
+    GetPermissionRequestManager()->AddRequest(web_contents->GetMainFrame(),
+                                              &request_quiet);
 
     bubble_factory()->WaitForPermissionBubble();
     GetPermissionRequestManager()->Closing();
@@ -838,17 +857,18 @@
 // Two loud requests are simply queued one after another.
 IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                        LoudPendingRequestsQueued) {
+  content::RenderFrameHost* source_frame = GetActiveMainFrame();
   permissions::MockPermissionRequest request1(
       "request1",
       permissions::PermissionRequestType::PERMISSION_CLIPBOARD_READ_WRITE,
       permissions::PermissionRequestGestureType::UNKNOWN);
-  GetPermissionRequestManager()->AddRequest(&request1);
+  GetPermissionRequestManager()->AddRequest(source_frame, &request1);
   base::RunLoop().RunUntilIdle();
 
   permissions::MockPermissionRequest request2(
       "request2", permissions::PermissionRequestType::PERMISSION_GEOLOCATION,
       permissions::PermissionRequestGestureType::UNKNOWN);
-  GetPermissionRequestManager()->AddRequest(&request2);
+  GetPermissionRequestManager()->AddRequest(source_frame, &request2);
   base::RunLoop().RunUntilIdle();
 
   // Both requests are still pending (though only one is active).
diff --git a/chrome/browser/policy/autoplay_policy_browsertest.cc b/chrome/browser/policy/autoplay_policy_browsertest.cc
index af8ffd3e..8d093c08 100644
--- a/chrome/browser/policy/autoplay_policy_browsertest.cc
+++ b/chrome/browser/policy/autoplay_policy_browsertest.cc
@@ -87,8 +87,7 @@
 
   // Update policy to allow autoplay.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayAllowed,
-            std::make_unique<base::Value>(true));
+  SetPolicy(&policies, key::kAutoplayAllowed, base::Value(true));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was allowed by policy.
@@ -110,8 +109,7 @@
 
   // Update policy to allow autoplay for our test origin.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayWhitelist,
-            std::make_unique<base::ListValue>(whitelist));
+  SetPolicy(&policies, key::kAutoplayWhitelist, base::Value(whitelist));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was allowed by policy.
@@ -133,8 +131,7 @@
 
   // Update policy to allow autoplay for our test origin.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayWhitelist,
-            std::make_unique<base::ListValue>(whitelist));
+  SetPolicy(&policies, key::kAutoplayWhitelist, base::Value(whitelist));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was allowed by policy.
@@ -156,8 +153,7 @@
 
   // Update policy to allow autoplay for a random origin.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayWhitelist,
-            std::make_unique<base::ListValue>(whitelist));
+  SetPolicy(&policies, key::kAutoplayWhitelist, base::Value(whitelist));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was not allowed.
@@ -175,8 +171,7 @@
 
   // Update policy to forbid autoplay.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayAllowed,
-            std::make_unique<base::Value>(false));
+  SetPolicy(&policies, key::kAutoplayAllowed, base::Value(false));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was not allowed by policy.
@@ -189,8 +184,7 @@
   whitelist.push_back(base::Value("https://www.example.com"));
 
   // Update policy to allow autoplay for a random origin.
-  SetPolicy(&policies, key::kAutoplayWhitelist,
-            std::make_unique<base::ListValue>(whitelist));
+  SetPolicy(&policies, key::kAutoplayWhitelist, base::Value(whitelist));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was not allowed.
@@ -208,8 +202,7 @@
 
   // Update policy to forbid autoplay.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayAllowed,
-            std::make_unique<base::Value>(false));
+  SetPolicy(&policies, key::kAutoplayAllowed, base::Value(false));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was not allowed by policy.
@@ -222,8 +215,7 @@
   whitelist.push_back(base::Value(embedded_test_server()->GetURL("/").spec()));
 
   // Update policy to allow autoplay for our test origin.
-  SetPolicy(&policies, key::kAutoplayWhitelist,
-            std::make_unique<base::ListValue>(whitelist));
+  SetPolicy(&policies, key::kAutoplayWhitelist, base::Value(whitelist));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was allowed by policy.
@@ -241,8 +233,7 @@
 
   // Update policy to forbid autoplay.
   PolicyMap policies;
-  SetPolicy(&policies, key::kAutoplayAllowed,
-            std::make_unique<base::Value>(false));
+  SetPolicy(&policies, key::kAutoplayAllowed, base::Value(false));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was not allowed by policy.
@@ -255,8 +246,7 @@
   whitelist.push_back(base::Value(embedded_test_server()->GetURL("/").spec()));
 
   // Update policy to allow autoplay for our test origin.
-  SetPolicy(&policies, key::kAutoplayWhitelist,
-            std::make_unique<base::ListValue>(whitelist));
+  SetPolicy(&policies, key::kAutoplayWhitelist, base::Value(whitelist));
   UpdateProviderPolicy(policies);
 
   // Check that autoplay was allowed by policy.
diff --git a/chrome/browser/policy/content_settings_policy_browsertest.cc b/chrome/browser/policy/content_settings_policy_browsertest.cc
index 8990a78..a4b67986 100644
--- a/chrome/browser/policy/content_settings_policy_browsertest.cc
+++ b/chrome/browser/policy/content_settings_policy_browsertest.cc
@@ -212,14 +212,12 @@
 
   // Update policy to change the default permission value to 'block'.
   PolicyMap policies;
-  SetPolicy(&policies, key::kDefaultWebUsbGuardSetting,
-            std::make_unique<base::Value>(2));
+  SetPolicy(&policies, key::kDefaultWebUsbGuardSetting, base::Value(2));
   UpdateProviderPolicy(policies);
   EXPECT_FALSE(context->CanRequestObjectPermission(kTestOrigin, kTestOrigin));
 
   // Update policy to change the default permission value to 'ask'.
-  SetPolicy(&policies, key::kDefaultWebUsbGuardSetting,
-            std::make_unique<base::Value>(3));
+  SetPolicy(&policies, key::kDefaultWebUsbGuardSetting, base::Value(3));
   UpdateProviderPolicy(policies);
   EXPECT_TRUE(context->CanRequestObjectPermission(kTestOrigin, kTestOrigin));
 }
@@ -254,8 +252,8 @@
   entry.SetKey("devices", std::move(devices_value));
   entry.SetKey("urls", std::move(urls_value));
 
-  auto policy_value = std::make_unique<base::Value>(base::Value::Type::LIST);
-  policy_value->Append(std::move(entry));
+  base::Value policy_value(base::Value::Type::LIST);
+  policy_value.Append(std::move(entry));
 
   SetPolicy(&policies, key::kWebUsbAllowDevicesForUrls,
             std::move(policy_value));
@@ -266,7 +264,7 @@
 
   // Remove the policy to ensure that it can be dynamically updated.
   SetPolicy(&policies, key::kWebUsbAllowDevicesForUrls,
-            std::make_unique<base::Value>(base::Value::Type::LIST));
+            base::Value(base::Value::Type::LIST));
   UpdateProviderPolicy(policies);
 
   EXPECT_FALSE(
@@ -286,11 +284,11 @@
 
 IN_PROC_BROWSER_TEST_F(DisallowWildcardPolicyTest, PluginTest) {
   PolicyMap policies;
-  auto policy_value = std::make_unique<base::ListValue>();
-  policy_value->AppendString("[*.]google.com");
-  policy_value->AppendString("http://drive.google.com:443/home");
-  policy_value->AppendString("www.foo.com:*/*");
-  policy_value->AppendString("*://[*.]bar.com:*/*");
+  base::Value policy_value(base::Value::Type::LIST);
+  policy_value.Append("[*.]google.com");
+  policy_value.Append("http://drive.google.com:443/home");
+  policy_value.Append("www.foo.com:*/*");
+  policy_value.Append("*://[*.]bar.com:*/*");
   SetPolicy(&policies, key::kPluginsAllowedForUrls, std::move(policy_value));
   UpdateProviderPolicy(policies);
 
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc
index 7a7712b..cb924f7 100644
--- a/chrome/browser/policy/extension_policy_browsertest.cc
+++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -1991,8 +1991,7 @@
     list.Append(std::move(item));
 
     PolicyMap policies;
-    SetPolicy(&policies, key::kWebAppInstallForceList,
-              base::Value::ToUniquePtrValue(std::move(list)));
+    SetPolicy(&policies, key::kWebAppInstallForceList, std::move(list));
     provider_.UpdateChromePolicy(policies);
   }
 
diff --git a/chrome/browser/policy/hsts_policy_browsertest.cc b/chrome/browser/policy/hsts_policy_browsertest.cc
index cc9ab3b..0119bdc 100644
--- a/chrome/browser/policy/hsts_policy_browsertest.cc
+++ b/chrome/browser/policy/hsts_policy_browsertest.cc
@@ -28,7 +28,7 @@
     std::vector<base::Value> bypass_list;
     bypass_list.emplace_back(base::Value("example"));
     SetPolicy(&policies, key::kHSTSPolicyBypassList,
-              std::make_unique<base::ListValue>(bypass_list));
+              base::ListValue(bypass_list));
     provider_.UpdateChromePolicy(policies);
   }
 };
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 0eefc5c..068360c 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -645,17 +645,18 @@
     // Override the default SafeSearch setting using policies.
     ApplySafeSearchPolicy(
         legacy_safe_search == 0
-            ? nullptr
-            : std::make_unique<base::Value>(legacy_safe_search == 1),
+            ? base::nullopt
+            : base::make_optional<base::Value>(legacy_safe_search == 1),
         google_safe_search == 0
-            ? nullptr
-            : std::make_unique<base::Value>(google_safe_search == 1),
+            ? base::nullopt
+            : base::make_optional<base::Value>(google_safe_search == 1),
         legacy_youtube == 0
-            ? nullptr
-            : std::make_unique<base::Value>(legacy_youtube == 1),
+            ? base::nullopt
+            : base::make_optional<base::Value>(legacy_youtube == 1),
         youtube_restrict == 0
-            ? nullptr  // subtracting 1 gives 0,1,2, see above
-            : std::make_unique<base::Value>(youtube_restrict - 1));
+            ? base::nullopt  // subtracting 1 gives
+                             // 0,1,2, see above
+            : base::make_optional<base::Value>(youtube_restrict - 1));
 
     // The legacy ForceSafeSearch policy should only have an effect if none of
     // the other 3 policies are defined.
@@ -728,12 +729,13 @@
   // ForceGoogleSafeSearch policy.
   for (int safe_search = 0; safe_search < 3; safe_search++) {
     // Override the Google safe search policy.
-    ApplySafeSearchPolicy(nullptr,          // ForceSafeSearch
-                          safe_search == 0  // ForceGoogleSafeSearch
-                              ? nullptr
-                              : std::make_unique<base::Value>(safe_search == 1),
-                          nullptr,   // ForceYouTubeSafetyMode
-                          nullptr);  // ForceYouTubeRestrict
+    ApplySafeSearchPolicy(
+        base::nullopt,    // ForceSafeSearch
+        safe_search == 0  // ForceGoogleSafeSearch
+            ? base::nullopt
+            : base::make_optional<base::Value>(safe_search == 1),
+        base::nullopt,   // ForceYouTubeSafetyMode
+        base::nullopt);  // ForceYouTubeRestrict
     // Verify that the safe search pref behaves the way we expect.
     PrefService* prefs = browser()->profile()->GetPrefs();
     EXPECT_EQ(safe_search != 0,
@@ -818,10 +820,10 @@
 };
 
 IN_PROC_BROWSER_TEST_F(PolicyTestGoogle, ForceGoogleSafeSearch) {
-  ApplySafeSearchPolicy(nullptr,  // ForceSafeSearch
-                        std::make_unique<base::Value>(true),
-                        nullptr,   // ForceYouTubeSafetyMode
-                        nullptr);  // ForceYouTubeRestrict
+  ApplySafeSearchPolicy(base::nullopt,  // ForceSafeSearch
+                        base::Value(true),
+                        base::nullopt,   // ForceYouTubeSafetyMode
+                        base::nullopt);  // ForceYouTubeRestrict
 
   GURL url = https_server()->GetURL("www.google.com",
                                     "/server-redirect?http://google.com/");
@@ -832,10 +834,10 @@
   for (int youtube_restrict_mode = safe_search_util::YOUTUBE_RESTRICT_OFF;
        youtube_restrict_mode < safe_search_util::YOUTUBE_RESTRICT_COUNT;
        ++youtube_restrict_mode) {
-    ApplySafeSearchPolicy(nullptr,  // ForceSafeSearch
-                          nullptr,  // ForceGoogleSafeSearch
-                          nullptr,  // ForceYouTubeSafetyMode
-                          std::make_unique<base::Value>(youtube_restrict_mode));
+    ApplySafeSearchPolicy(base::nullopt,  // ForceSafeSearch
+                          base::nullopt,  // ForceGoogleSafeSearch
+                          base::nullopt,  // ForceYouTubeSafetyMode
+                          base::Value(youtube_restrict_mode));
     {
       // First check frame requests.
       GURL youtube_url(https_server()->GetURL("youtube.com", "/empty.html"));
@@ -864,7 +866,7 @@
       PolicyMap policies;
       allowed_domain = "foo.com";
       SetPolicy(&policies, key::kAllowedDomainsForApps,
-                std::make_unique<base::Value>(allowed_domain));
+                base::Value(allowed_domain));
       UpdateProviderPolicy(policies);
     }
 
diff --git a/chrome/browser/policy/policy_test_utils.cc b/chrome/browser/policy/policy_test_utils.cc
index f904d23..4e557ce4 100644
--- a/chrome/browser/policy/policy_test_utils.cc
+++ b/chrome/browser/policy/policy_test_utils.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/policy/policy_test_utils.h"
 
 #include "base/bind_helpers.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/current_thread.h"
@@ -211,20 +212,16 @@
 
 void PolicyTest::SetPolicy(PolicyMap* policies,
                            const char* key,
-                           std::unique_ptr<base::Value> value) {
-  if (value) {
-    policies->Set(key, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                  POLICY_SOURCE_CLOUD, std::move(value), nullptr);
-  } else {
-    policies->Erase(key);
-  }
+                           base::Optional<base::Value> value) {
+  policies->Set(key, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                POLICY_SOURCE_CLOUD, std::move(value), nullptr);
 }
 
 void PolicyTest::ApplySafeSearchPolicy(
-    std::unique_ptr<base::Value> legacy_safe_search,
-    std::unique_ptr<base::Value> google_safe_search,
-    std::unique_ptr<base::Value> legacy_youtube,
-    std::unique_ptr<base::Value> youtube_restrict) {
+    base::Optional<base::Value> legacy_safe_search,
+    base::Optional<base::Value> google_safe_search,
+    base::Optional<base::Value> legacy_youtube,
+    base::Optional<base::Value> youtube_restrict) {
   PolicyMap policies;
   SetPolicy(&policies, key::kForceSafeSearch, std::move(legacy_safe_search));
   SetPolicy(&policies, key::kForceGoogleSafeSearch,
diff --git a/chrome/browser/policy/policy_test_utils.h b/chrome/browser/policy/policy_test_utils.h
index 5f78dd1..f6986ef8 100644
--- a/chrome/browser/policy/policy_test_utils.h
+++ b/chrome/browser/policy/policy_test_utils.h
@@ -62,12 +62,12 @@
 
   void SetPolicy(PolicyMap* policies,
                  const char* key,
-                 std::unique_ptr<base::Value> value);
+                 base::Optional<base::Value> value);
 
-  void ApplySafeSearchPolicy(std::unique_ptr<base::Value> legacy_safe_search,
-                             std::unique_ptr<base::Value> google_safe_search,
-                             std::unique_ptr<base::Value> legacy_youtube,
-                             std::unique_ptr<base::Value> youtube_restrict);
+  void ApplySafeSearchPolicy(base::Optional<base::Value> legacy_safe_search,
+                             base::Optional<base::Value> google_safe_search,
+                             base::Optional<base::Value> legacy_youtube,
+                             base::Optional<base::Value> youtube_restrict);
 
 #if defined(OS_CHROMEOS)
   void TestScreenshotFile(bool enabled);
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 1e8c823..326e702 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -100,7 +100,6 @@
 #include "components/dom_distiller/core/distilled_page_prefs.h"
 #include "components/dom_distiller/core/dom_distiller_features.h"
 #include "components/dom_distiller/core/pref_names.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
 #include "components/image_fetcher/core/cache/image_cache.h"
 #include "components/invalidation/impl/fcm_invalidation_service.h"
@@ -188,10 +187,6 @@
 #endif  // defined(OS_CHROMEOS)
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/session_duration_updater.h"
-#endif
-
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 #include "chrome/browser/offline_pages/prefetch/offline_metrics_collector_impl.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h"
@@ -562,6 +557,9 @@
 // Deprecated 7/2020
 const char kHashedAvailablePages[] = "previews.offline_helper.available_pages";
 
+// Deprecated 7/2020
+const char kObservedSessionTime[] = "profile.observed_session_time";
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -668,6 +666,8 @@
   registry->RegisterBooleanPref(kStricterMixedContentTreatmentEnabled, true);
 
   registry->RegisterDictionaryPref(kHashedAvailablePages);
+
+  registry->RegisterDictionaryPref(kObservedSessionTime);
 }
 
 }  // namespace
@@ -950,10 +950,6 @@
   web_app::WebAppProvider::RegisterProfilePrefs(registry);
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  feature_engagement::SessionDurationUpdater::RegisterProfilePrefs(registry);
-#endif
-
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
   offline_pages::OfflineMetricsCollectorImpl::RegisterPrefs(registry);
   offline_pages::prefetch_prefs::RegisterPrefs(registry);
@@ -1321,4 +1317,7 @@
 
   // Added 7/2020.
   profile_prefs->ClearPref(kHashedAvailablePages);
+
+  // Added 7/2020
+  profile_prefs->ClearPref(kObservedSessionTime);
 }
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index ef6eb82..2211eae 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -135,7 +135,6 @@
 using content::WebContentsObserver;
 using prerender::test_utils::TestPrerender;
 using prerender::test_utils::TestPrerenderContents;
-using task_manager::browsertest_util::WaitForTaskManagerRows;
 
 // crbug.com/708158
 #if !defined(OS_MACOSX) || !defined(ADDRESS_SANITIZER)
@@ -154,13 +153,6 @@
 
 namespace {
 
-const char kPrefetchJpeg[] = "/prerender/image.jpeg";
-
-std::string CreateServerRedirect(const std::string& dest_url) {
-  const char* const kServerRedirectBase = "/server-redirect?";
-  return kServerRedirectBase + net::EscapeQueryParamValue(dest_url, false);
-}
-
 // Returns true if the prerender is expected to abort on its own, before
 // attempting to swap it.
 bool ShouldAbortPrerenderBeforeSwap(FinalStatus status) {
@@ -417,11 +409,7 @@
     test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
     prerender::PrerenderManager::SetMode(
         prerender::PrerenderManager::DEPRECATED_PRERENDER_MODE_ENABLED);
-    const testing::TestInfo* const test_info =
-        testing::UnitTest::GetInstance()->current_test_info();
-    // This one test fails with the host resolver redirecting all hosts.
-    if (std::string(test_info->name()) != "PrerenderServerRedirectInIframe")
-      host_resolver()->AddRule("*", "127.0.0.1");
+    host_resolver()->AddRule("*", "127.0.0.1");
   }
 
   void TearDownOnMainThread() override {
@@ -773,210 +761,6 @@
   std::unique_ptr<content::URLLoaderInterceptor> interceptor_;
 };
 
-// Renders a page that contains a prerender link to a page that contains an
-// iframe with a source that requires http authentication. This should not
-// prerender successfully.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderHttpAuthentication) {
-  PrerenderTestURL("/prerender/prerender_http_auth_container.html",
-                   FINAL_STATUS_AUTH_NEEDED, 0);
-}
-
-// Checks that server-issued redirects within an iframe in a prerendered
-// page will not count as an "alias" for the prerendered page.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderServerRedirectInIframe) {
-  std::string redirect_path =
-      CreateServerRedirect("//prerender/prerender_embedded_content.html");
-  base::StringPairs replacement_text;
-  replacement_text.push_back(std::make_pair("REPLACE_WITH_URL", redirect_path));
-  std::string replacement_path = net::test_server::GetFilePathWithReplacements(
-      "/prerender/prerender_with_iframe.html", replacement_text);
-  PrerenderTestURL(replacement_path, FINAL_STATUS_USED, 1);
-  EXPECT_FALSE(
-      UrlIsInPrerenderManager("/prerender/prerender_embedded_content.html"));
-  NavigateToDestURL();
-}
-
-// Checks that the referrer is set when prerendering.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderReferrer) {
-  PrerenderTestURL("/prerender/prerender_referrer.html", FINAL_STATUS_USED, 1);
-  NavigateToDestURL();
-}
-
-// Checks that the referrer is not set when prerendering and the source page is
-// HTTPS.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderNoSSLReferrer) {
-  // Use http:// url for the prerendered page main resource.
-  GURL url(
-      embedded_test_server()->GetURL("/prerender/prerender_no_referrer.html"));
-
-  // Use https:// for all other resources.
-  UseHttpsSrcServer();
-
-  PrerenderTestURL(url, FINAL_STATUS_USED, 1);
-  NavigateToDestURL();
-}
-
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, OpenTaskManagerBeforePrerender) {
-  const base::string16 any_prerender = MatchTaskManagerPrerender("*");
-  const base::string16 any_tab = MatchTaskManagerTab("*");
-  const base::string16 original = MatchTaskManagerTab("Preloader");
-  const base::string16 prerender = MatchTaskManagerPrerender("Prerender Page");
-  const base::string16 final = MatchTaskManagerTab("Prerender Page");
-
-  // Show the task manager. This populates the model.
-  chrome::OpenTaskManager(current_browser());
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, any_prerender));
-
-  // Prerender a page in addition to the original tab.
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-
-  // A TaskManager entry should appear like "Prerender: Prerender Page"
-  // alongside the original tab entry. There should be just these two entries.
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, prerender));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, original));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, final));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_prerender));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
-
-  // Swap in the prerendered content.
-  NavigateToDestURL();
-
-  // The "Prerender: " TaskManager entry should disappear, being replaced by a
-  // "Tab: Prerender Page" entry, and nothing else.
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, prerender));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, original));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, final));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, any_prerender));
-}
-
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, OpenTaskManagerAfterPrerender) {
-  const base::string16 any_prerender = MatchTaskManagerPrerender("*");
-  const base::string16 any_tab = MatchTaskManagerTab("*");
-  const base::string16 original = MatchTaskManagerTab("Preloader");
-  const base::string16 prerender = MatchTaskManagerPrerender("Prerender Page");
-  const base::string16 final = MatchTaskManagerTab("Prerender Page");
-
-  // Start with two resources.
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
-
-  // Show the task manager. This populates the model. Importantly, we're doing
-  // this after the prerender WebContents already exists - the task manager
-  // needs to find it, it can't just listen for creation.
-  chrome::OpenTaskManager(current_browser());
-
-  // A TaskManager entry should appear like "Prerender: Prerender Page"
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, prerender));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, original));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, final));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_prerender));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
-
-  // Swap in the tab.
-  NavigateToDestURL();
-
-  // The "Prerender: Prerender Page" TaskManager row should disappear, being
-  // replaced by "Tab: Prerender Page"
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, prerender));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, original));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, final));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
-  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, any_prerender));
-}
-
-// Checks that the referrer policy is used when prerendering.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderReferrerPolicy) {
-  set_loader_path("/prerender/prerender_loader_with_referrer_policy.html");
-  PrerenderTestURL("/prerender/prerender_referrer_policy.html",
-                   FINAL_STATUS_USED, 1);
-  NavigateToDestURL();
-}
-
-// Checks that the referrer policy is used when prerendering on HTTPS.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderSSLReferrerPolicy) {
-  UseHttpsSrcServer();
-  set_loader_path("/prerender/prerender_loader_with_referrer_policy.html");
-  PrerenderTestURL("/prerender/prerender_referrer_policy.html",
-                   FINAL_STATUS_USED, 1);
-  NavigateToDestURL();
-}
-
-// Test interaction of Safe Browsing with prerender. Parametrized to enable
-// SafeBrowsing Delayed Warnings experiment. The experiment shouldn't delay
-// prerender page loads. Otherwise, the tests will crash or timeout.
-// The experiment only delays phishing warnings so the tests must use a phishing
-// resource.
-class PrerenderSafeBrowsingTest
-    : public PrerenderBrowserTest,
-      public testing::WithParamInterface<
-          testing::tuple<bool /* Enable delayed warnings experiment */>> {
- public:
-  PrerenderSafeBrowsingTest() {
-    if (testing::get<0>(GetParam())) {
-      scoped_feature_list_.InitWithFeatures(
-          /*enabled_features=*/{safe_browsing::kDelayedWarnings},
-          /*disabled_features=*/{});
-    }
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// Ensures that we do not prerender pages with a safe browsing
-// interstitial.
-IN_PROC_BROWSER_TEST_P(PrerenderSafeBrowsingTest, TopLevel) {
-  GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
-  GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
-      url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
-  PrerenderTestURL("/prerender/prerender_page.html", FINAL_STATUS_SAFE_BROWSING,
-                   0);
-}
-
-// Ensures that server redirects to a malware page will cancel prerenders.
-IN_PROC_BROWSER_TEST_P(PrerenderSafeBrowsingTest, ServerRedirect) {
-  GURL url = embedded_test_server()->GetURL("/prerender/prerender_page.html");
-  GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
-      url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
-  PrerenderTestURL(CreateServerRedirect("/prerender/prerender_page.html"),
-                   FINAL_STATUS_SAFE_BROWSING, 0);
-}
-
-// Ensures that we do not prerender pages which have a malware subresource.
-IN_PROC_BROWSER_TEST_P(PrerenderSafeBrowsingTest, Subresource) {
-  GURL image_url = embedded_test_server()->GetURL(kPrefetchJpeg);
-  GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
-      image_url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
-  base::StringPairs replacement_text;
-  replacement_text.push_back(
-      std::make_pair("REPLACE_WITH_IMAGE_URL", image_url.spec()));
-  std::string replacement_path = net::test_server::GetFilePathWithReplacements(
-      "/prerender/prerender_with_image.html", replacement_text);
-  PrerenderTestURL(replacement_path, FINAL_STATUS_SAFE_BROWSING, 0);
-}
-
-// Ensures that we do not prerender pages which have a malware iframe.
-IN_PROC_BROWSER_TEST_P(PrerenderSafeBrowsingTest, Iframe) {
-  GURL iframe_url = embedded_test_server()->GetURL(
-      "/prerender/prerender_embedded_content.html");
-  GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
-      iframe_url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
-  base::StringPairs replacement_text;
-  replacement_text.push_back(
-      std::make_pair("REPLACE_WITH_URL", iframe_url.spec()));
-  std::string replacement_path = net::test_server::GetFilePathWithReplacements(
-      "/prerender/prerender_with_iframe.html", replacement_text);
-  PrerenderTestURL(replacement_path, FINAL_STATUS_SAFE_BROWSING, 0);
-}
-
-INSTANTIATE_TEST_SUITE_P(PrerenderSafeBrowsingTest,
-                         PrerenderSafeBrowsingTest,
-                         testing::Combine(testing::Values(
-                             false,
-                             true)) /* Enable delayed warnings experiment */
-);
-
 }  // namespace prerender
 
 #endif  // !defined(OS_MACOSX) || !defined(ADDRESS_SANITIZER)
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 5714ad42d..3ed15e3 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -21,15 +21,14 @@
 #include "chrome/browser/prerender/prerender_handle.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
-#include "chrome/browser/prerender/prerender_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/task_manager/web_contents_tags.h"
 #include "chrome/browser/ui/tab_helpers.h"
 #include "chrome/browser/ui/web_contents_sizer.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
-#include "chrome/common/prerender_util.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/prerender/common/prerender_final_status.h"
+#include "components/prerender/common/prerender_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
diff --git a/chrome/browser/prerender/prerender_histograms.cc b/chrome/browser/prerender/prerender_histograms.cc
index 1f54505..1929e7ea 100644
--- a/chrome/browser/prerender/prerender_histograms.cc
+++ b/chrome/browser/prerender/prerender_histograms.cc
@@ -14,8 +14,8 @@
 #include "base/notreached.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/prerender/prerender_manager.h"
-#include "chrome/common/prerender_util.h"
 #include "components/google/core/common/google_util.h"
+#include "components/prerender/common/prerender_util.h"
 #include "net/http/http_cache.h"
 
 namespace prerender {
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index a78df5e..3de1424 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -76,6 +76,7 @@
 
 using prerender::test_utils::DestructionWaiter;
 using prerender::test_utils::TestPrerender;
+using task_manager::browsertest_util::WaitForTaskManagerRows;
 
 namespace {
 
@@ -1450,6 +1451,16 @@
   WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
 }
 
+// Ensures that server redirects to a malware page will cancel prerenders.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ServerRedirect) {
+  GURL url = src_server()->GetURL("/prerender/prerender_page.html");
+  GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
+      url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
+  PrefetchFromURL(src_server()->GetURL(
+                      CreateServerRedirect("/prerender/prerender_page.html")),
+                  FINAL_STATUS_SAFE_BROWSING, 0);
+}
+
 // If a subresource is unsafe, the corresponding request is cancelled.
 IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
                        PrerenderSafeBrowsingSubresource) {
@@ -1864,4 +1875,62 @@
   prerender->WaitForStop();
 }
 
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, OpenTaskManager) {
+  const base::string16 any_tab = MatchTaskManagerTab("*");
+  const base::string16 original = MatchTaskManagerTab("Prefetch Loader");
+  const base::string16 prefetch_page = MatchTaskManagerTab("Prefetch Page");
+
+  // Show the task manager. This populates the model.
+  chrome::OpenTaskManager(current_browser());
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, prefetch_page));
+
+  // Prerender a page in addition to the original tab.
+  PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
+
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, original));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
+
+  // ui_test_utils::NavigateToURL(current_browser(),
+  //                             src_server()->GetURL(kPrefetchPage));
+  // Open a new tab to replace the one closed with all the RenderProcessHosts.
+  ui_test_utils::NavigateToURLWithDisposition(
+      current_browser(), src_server()->GetURL(kPrefetchPage),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, prefetch_page));
+}
+
+// Renders a page that contains a prerender link to a page that contains an
+// img with a source that requires http authentication. This should not
+// prerender successfully.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
+                       PrerenderHttpAuthentication) {
+  GURL url =
+      src_server()->GetURL("/prerender/prerender_http_auth_container.html");
+  std::unique_ptr<TestPrerender> prerender =
+      PrefetchFromURL(url, FINAL_STATUS_AUTH_NEEDED);
+}
+
+// Checks that the referrer is not set when prerendering and the source page is
+// HTTPS.
+IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrerenderNoSSLReferrer) {
+  // Use http:// url for the prerendered page main resource.
+  GURL url(
+      embedded_test_server()->GetURL("/prerender/prerender_no_referrer.html"));
+
+  // Use https:// for all resources.
+  UseHttpsSrcServer();
+
+  PrefetchFromURL(url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
+  ui_test_utils::NavigateToURL(current_browser(), url);
+  EXPECT_TRUE(WaitForLoadStop(
+      current_browser()->tab_strip_model()->GetActiveWebContents()));
+  content::WebContents* web_contents =
+      current_browser()->tab_strip_model()->GetActiveWebContents();
+  const std::string referrer =
+      EvalJs(web_contents, "document.referrer").ExtractString();
+  EXPECT_TRUE(referrer.empty());
+}
+
 }  // namespace prerender
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index f019392..0d5db1a 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -36,13 +36,13 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/prerender_util.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/prerender/common/prerender_origin.h"
 #include "components/prerender/common/prerender_types.mojom.h"
+#include "components/prerender/common/prerender_util.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 63f3732..01460d04 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -92,7 +92,6 @@
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/common/buildflags.h"
 #include "components/captive_portal/core/buildflags.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
@@ -136,12 +135,6 @@
 #include "chrome/browser/signin/signin_manager_factory.h"
 #endif
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.h"
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "apps/browser_context_keyed_service_factories.h"
 #include "chrome/browser/apps/platform_apps/api/browser_context_keyed_service_factories.h"
@@ -265,11 +258,6 @@
   explore_sites::ExploreSitesServiceFactory::GetInstance();
 #endif
   FaviconServiceFactory::GetInstance();
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  feature_engagement::BookmarkTrackerFactory::GetInstance();
-  feature_engagement::IncognitoWindowTrackerFactory::GetInstance();
-  feature_engagement::NewTabTrackerFactory::GetInstance();
-#endif
   feature_engagement::TrackerFactory::GetInstance();
 #if !defined(OS_ANDROID)
   feedback::FeedbackUploaderFactoryChrome::GetInstance();
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index feda364..fa8645d 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -127,6 +127,7 @@
 void DidRegister(base::Closure done_callback,
                  const std::string& registration_id,
                  const GURL& endpoint,
+                 const base::Optional<base::Time>& expiration_time,
                  const std::vector<uint8_t>& p256dh,
                  const std::vector<uint8_t>& auth,
                  blink::mojom::PushRegistrationStatus status) {
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index cc88847..4ae450ac 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -717,7 +717,9 @@
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth,
     blink::mojom::PushRegistrationStatus status) {
-  std::move(callback).Run(subscription_id, endpoint, p256dh, auth, status);
+  std::move(callback).Run(subscription_id, endpoint,
+                          base::nullopt /* expiration_time*/, p256dh, auth,
+                          status);
 }
 
 void PushMessagingServiceImpl::SubscribeEndWithError(
@@ -798,6 +800,8 @@
 
 // GetSubscriptionInfo methods -------------------------------------------------
 
+// TODO(crbug.com/1104215): Get |expiration_time| from |app_identifier|, where
+// it is stored in profile preferences
 void PushMessagingServiceImpl::GetSubscriptionInfo(
     const GURL& origin,
     int64_t service_worker_registration_id,
@@ -811,6 +815,7 @@
   if (app_identifier.is_null()) {
     std::move(callback).Run(
         false /* is_valid */, GURL::EmptyGURL() /*endpoint*/,
+        base::nullopt /* expiration_time */,
         std::vector<uint8_t>() /* p256dh */, std::vector<uint8_t>() /* auth */);
     return;
   }
@@ -842,6 +847,7 @@
   if (!is_valid) {
     std::move(callback).Run(
         false /* is_valid */, GURL::EmptyGURL() /* endpoint */,
+        base::nullopt /* expiration_time */,
         std::vector<uint8_t>() /* p256dh */, std::vector<uint8_t>() /* auth */);
     return;
   }
@@ -861,7 +867,8 @@
   // I/O errors might prevent the GCM Driver from retrieving a key-pair.
   bool is_valid = !p256dh.empty();
   std::move(callback).Run(
-      is_valid, endpoint, std::vector<uint8_t>(p256dh.begin(), p256dh.end()),
+      is_valid, endpoint, base::nullopt /* expiration_time */,
+      std::vector<uint8_t>(p256dh.begin(), p256dh.end()),
       std::vector<uint8_t>(auth_secret.begin(), auth_secret.end()));
 }
 
diff --git a/chrome/browser/push_messaging/push_messaging_service_unittest.cc b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
index 3218cf5..1f65df5 100644
--- a/chrome/browser/push_messaging/push_messaging_service_unittest.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
@@ -11,9 +11,11 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
 #include "chrome/browser/permissions/permission_manager_factory.h"
@@ -105,11 +107,13 @@
   // Callback to use when the subscription may have been subscribed.
   void DidRegister(std::string* subscription_id_out,
                    GURL* endpoint_out,
+                   base::Optional<base::Time>* expiration_time_out,
                    std::vector<uint8_t>* p256dh_out,
                    std::vector<uint8_t>* auth_out,
                    base::Closure done_callback,
                    const std::string& registration_id,
                    const GURL& endpoint,
+                   const base::Optional<base::Time>& expiration_time,
                    const std::vector<uint8_t>& p256dh,
                    const std::vector<uint8_t>& auth,
                    blink::mojom::PushRegistrationStatus status) {
@@ -117,6 +121,7 @@
               status);
 
     *subscription_id_out = registration_id;
+    *expiration_time_out = expiration_time;
     *endpoint_out = endpoint;
     *p256dh_out = p256dh;
     *auth_out = auth;
@@ -164,6 +169,7 @@
 
   std::string subscription_id;
   GURL endpoint;
+  base::Optional<base::Time> expiration_time;
   std::vector<uint8_t> p256dh, auth;
 
   base::RunLoop run_loop;
@@ -179,7 +185,7 @@
       origin, kTestServiceWorkerId, std::move(options),
       base::BindOnce(&PushMessagingServiceTest::DidRegister,
                      base::Unretained(this), &subscription_id, &endpoint,
-                     &p256dh, &auth, run_loop.QuitClosure()));
+                     &expiration_time, &p256dh, &auth, run_loop.QuitClosure()));
 
   EXPECT_EQ(0u, subscription_id.size());  // this must be asynchronous
 
@@ -190,6 +196,7 @@
   ASSERT_GT(endpoint.spec().size(), 0u);
   ASSERT_GT(p256dh.size(), 0u);
   ASSERT_GT(auth.size(), 0u);
+  ASSERT_EQ(expiration_time, base::nullopt);
 
   // (3) Encrypt a message using the public key and authentication secret that
   // are associated with the subscription.
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 35128d3..680266de 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -62,7 +62,6 @@
 
 namespace {
 
-constexpr char kBlinkPageLifecycleFeature[] = "PageLifecycle";
 constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1);
 
 bool ObserveNavEntryCommitted(const GURL& expected_url,
@@ -153,11 +152,6 @@
     test_clock_.Advance(kShortDelay);
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    kBlinkPageLifecycleFeature);
-  }
-
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 0601c30..86779cd 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -146,7 +146,6 @@
 
 group("closure_compile") {
   deps = [
-    ":deep_linking_behavior",
     ":metrics_recorder",
     ":os_page_visibility",
     ":os_route",
@@ -186,15 +185,6 @@
   ]
 }
 
-js_library("deep_linking_behavior") {
-  deps = [
-    "..:router",
-    "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_js_library_for_compile",
-    "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:load_time_data",
-  ]
-}
-
 js_library("os_page_visibility") {
   deps = [
     "//ui/webui/resources/js:cr",
@@ -298,17 +288,6 @@
   ]
 }
 
-js_library("deep_linking_behavior.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/deep_linking_behavior.m.js" ]
-  deps = [
-    "..:router.m",
-    "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_js_library_for_compile",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-  extra_deps = [ ":modulize" ]
-}
-
 js_library("metrics_recorder.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/metrics_recorder.m.js" ]
   deps = [ "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile" ]
@@ -455,7 +434,6 @@
     "route_origin_behavior.js",
     "search_handler.js",
     "os_route.js",
-    "deep_linking_behavior.js",
   ]
   namespace_rewrites = os_settings_namespace_rewrites
 }
diff --git a/chrome/browser/resources/settings/chromeos/deep_linking_behavior.html b/chrome/browser/resources/settings/chromeos/deep_linking_behavior.html
deleted file mode 100644
index 154fbb6..0000000
--- a/chrome/browser/resources/settings/chromeos/deep_linking_behavior.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<link rel="import" href="../router.html">
-<link rel="import" href="chrome://resources/html/load_time_data.html">
-
-<script src="chrome://os-settings/constants/setting.mojom-lite.js"></script>
-<script src="deep_linking_behavior.js"></script>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/deep_linking_behavior.js b/chrome/browser/resources/settings/chromeos/deep_linking_behavior.js
deleted file mode 100644
index 29f9b05..0000000
--- a/chrome/browser/resources/settings/chromeos/deep_linking_behavior.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.
-
-
-/**
- * @fileoverview Polymer behavior for scrolling/focusing/indicating
- * setting elements with deep links.
- */
-
-// clang-format off
-// #import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js'
-// #import '../constants/setting.mojom-lite.js';
-
-// #import {afterNextRender, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {assert} from 'chrome://resources/js/assert.m.js';
-// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-// #import {Router} from '../router.m.js';
-// clang-format on
-
-const kDeepLinkSettingId = 'settingId';
-const kDeepLinkFocusId = 'deep-link-focus-id';
-
-/** @polymerBehavior */
-/* #export */ const DeepLinkingBehavior = {
-  properties: {
-    /**
-     * An object whose values are the kSetting mojom values which can be used to
-     * define deep link IDs on HTML elems.
-     * @type {!Object}
-     */
-    Setting: {
-      type: Object,
-      value: chromeos.settings.mojom.Setting,
-    },
-  },
-
-  /**
-   * Retrieves the settingId saved in the url's query parameter. Returns null if
-   * deep linking is disabled or if no settingId is found.
-   * @return {?chromeos.settings.mojom.Setting}
-   */
-  getDeepLinkSettingId() {
-    if (!loadTimeData.getBoolean('isDeepLinkingEnabled')) {
-      return null;
-    }
-    const settingIdStr = settings.Router.getInstance().getQueryParameters().get(
-        kDeepLinkSettingId);
-    const settingIdNum = Number(settingIdStr);
-    if (isNaN(settingIdNum)) {
-      return null;
-    }
-    return /** @type {!chromeos.settings.mojom.Setting} */ (settingIdNum);
-  },
-
-  /**
-   * Focuses the deep linked element referred to by |settindId|.
-   * @param {chromeos.settings.mojom.Setting} settingId
-   */
-  showDeepLink(settingId) {
-    assert(loadTimeData.getBoolean('isDeepLinkingEnabled'));
-    const elToFocus = this.$$(`[${kDeepLinkFocusId}~="${settingId}"]`);
-    Polymer.RenderStatus.afterNextRender(this, () => {
-      elToFocus.focus();
-    });
-  }
-};
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
index a363496a..e05a989 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
@@ -26,11 +26,9 @@
 
 js_library("os_reset_page") {
   deps = [
-    "..:deep_linking_behavior",
-    "..:os_route",
-    "../..:router",
     "//ui/webui/resources/js:assert",
     "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:load_time_data",
     "//ui/webui/resources/js/cr/ui:focus_without_ink",
   ]
 }
@@ -73,11 +71,9 @@
 js_library("os_reset_page.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.m.js" ]
   deps = [
-    "..:deep_linking_behavior.m",
-    "..:os_route.m",
-    "../..:router.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:load_time_data.m",
     "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
   ]
   extra_deps = [ ":os_reset_page_module" ]
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html
index 6b08be2..0b075bc 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html
@@ -3,10 +3,7 @@
 <link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="os_powerwash_dialog.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../os_route.html">
 <link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../../router.html">
 
 <dom-module id="os-settings-reset-page">
   <template>
@@ -25,8 +22,7 @@
             on-click="onShowPowerwashDialog_"
             aria-labelledby="title"
             aria-describedby="secondaryText"
-            aria-roledescription="$i18n{powerwashButtonRoleDescription}"
-            deep-link-focus-id$="[[Setting.kPowerwash]]">
+            aria-roledescription="$i18n{powerwashButtonRoleDescription}">
           $i18n{powerwashButton}
         </cr-button>
       </div>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js
index e6da298..effd68ce 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js
@@ -10,7 +10,6 @@
 Polymer({
   is: 'os-settings-reset-page',
 
-  behaviors: [DeepLinkingBehavior, settings.RouteObserverBehavior],
 
   properties: {
     /** @private */
@@ -32,22 +31,4 @@
     this.showPowerwashDialog_ = false;
     cr.ui.focusWithoutInk(assert(this.$.powerwash));
   },
-
-  /**
-   * settings.RouteObserverBehavior
-   * @param {!settings.Route} newRoute
-   * @param {!settings.Route} oldRoute
-   * @protected
-   */
-  currentRouteChanged(newRoute, oldRoute) {
-    // Does not apply to this page.
-    if (newRoute != settings.routes.OS_RESET) {
-      return;
-    }
-
-    const settingId = this.getDeepLinkSettingId();
-    if (settingId === chromeos.settings.mojom.Setting.kPowerwash) {
-      this.showDeepLink(settingId);
-    }
-  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 4b130af..6c3a5b6 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -32,30 +32,29 @@
       "settings.WallpaperBrowserProxy|WallpaperBrowserProxy",
     ]
 
-os_settings_auto_imports = settings_auto_imports +
-                           cr_components_chromeos_auto_imports +
-                           cr_elements_chromeos_auto_imports + [
-                             "chrome/browser/resources/settings/chromeos/ambient_mode_page/constants.html|AmbientModeTopicSource,AmbientModeSettings",
-                             "chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html|AmbientModeBrowserProxy,AmbientModeBrowserProxyImpl",
-                             "chrome/browser/resources/settings/chromeos/deep_linking_behavior.html|DeepLinkingBehavior",
-                             "chrome/browser/resources/settings/chromeos/metrics_recorder.html|recordSettingChange",
-                             "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.html|MultiDeviceSettingsMode,MultiDeviceFeature,MultiDeviceFeatureState,MultiDevicePageContentData,SmartLockSignInEnabledState",
-                             "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.html|MultiDeviceFeatureBehavior",
-                             "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.html|MultiDeviceBrowserProxy,MultiDeviceBrowserProxyImpl",
-                             "chrome/browser/resources/settings/chromeos/os_people_page/kerberos_accounts_browser_proxy.html|KerberosAccount,KerberosAccountsBrowserProxyImpl,KerberosAccountsBrowserProxy,KerberosErrorType,KerberosConfigErrorCode,ValidateKerberosConfigResult",
-                             "chrome/browser/resources/settings/chromeos/os_people_page/os_sync_browser_proxy.html|OsSyncBrowserProxy,OsSyncBrowserProxyImpl,OsSyncPrefs",
-                             "chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.html|OsResetBrowserProxy,OsResetBrowserProxyImpl",
-                             "chrome/browser/resources/settings/chromeos/os_route.html|routes",
-                             "chrome/browser/resources/settings/chromeos/os_settings_routes.html|OsSettingsRoutes",
-                             "chrome/browser/resources/settings/chromeos/personalization_page/change_picture_browser_proxy.html|ChangePictureBrowserProxy,ChangePictureBrowserProxyImpl,DefaultImage",
-                             "chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.html|WallpaperBrowserProxy,WallpaperBrowserProxyImpl",
-                             "chrome/browser/resources/settings/chromeos/route_origin_behavior.html|RouteOriginBehaviorImpl,RouteOriginBehavior",
-                             "chrome/browser/resources/settings/lifetime_browser_proxy.html|LifetimeBrowserProxy,LifetimeBrowserProxyImpl",
-                             "chrome/browser/resources/settings/people_page/account_manager_browser_proxy.html|AccountManagerBrowserProxy,AccountManagerBrowserProxyImpl",
-                             "chrome/browser/resources/settings/route.html|routes",
-                             "chrome/browser/resources/settings/router.html|Router,Route,RouteObserverBehavior",
-                             "ui/webui/resources/html/polymer.html|Polymer,html,flush",
-                             "ui/webui/resources/html/icon.html|getImage",
-                           ]
+os_settings_auto_imports =
+    settings_auto_imports + cr_components_chromeos_auto_imports +
+    cr_elements_chromeos_auto_imports + [
+      "chrome/browser/resources/settings/chromeos/ambient_mode_page/constants.html|AmbientModeTopicSource,AmbientModeSettings",
+      "chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html|AmbientModeBrowserProxy,AmbientModeBrowserProxyImpl",
+      "chrome/browser/resources/settings/chromeos/metrics_recorder.html|recordSettingChange",
+      "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.html|MultiDeviceSettingsMode,MultiDeviceFeature,MultiDeviceFeatureState,MultiDevicePageContentData,SmartLockSignInEnabledState",
+      "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.html|MultiDeviceFeatureBehavior",
+      "chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.html|MultiDeviceBrowserProxy,MultiDeviceBrowserProxyImpl",
+      "chrome/browser/resources/settings/chromeos/os_people_page/kerberos_accounts_browser_proxy.html|KerberosAccount,KerberosAccountsBrowserProxyImpl,KerberosAccountsBrowserProxy,KerberosErrorType,KerberosConfigErrorCode,ValidateKerberosConfigResult",
+      "chrome/browser/resources/settings/chromeos/os_people_page/os_sync_browser_proxy.html|OsSyncBrowserProxy,OsSyncBrowserProxyImpl,OsSyncPrefs",
+      "chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.html|OsResetBrowserProxy,OsResetBrowserProxyImpl",
+      "chrome/browser/resources/settings/chromeos/os_route.html|routes",
+      "chrome/browser/resources/settings/chromeos/os_settings_routes.html|OsSettingsRoutes",
+      "chrome/browser/resources/settings/chromeos/personalization_page/change_picture_browser_proxy.html|ChangePictureBrowserProxy,ChangePictureBrowserProxyImpl,DefaultImage",
+      "chrome/browser/resources/settings/chromeos/personalization_page/wallpaper_browser_proxy.html|WallpaperBrowserProxy,WallpaperBrowserProxyImpl",
+      "chrome/browser/resources/settings/chromeos/route_origin_behavior.html|RouteOriginBehaviorImpl,RouteOriginBehavior",
+      "chrome/browser/resources/settings/lifetime_browser_proxy.html|LifetimeBrowserProxy,LifetimeBrowserProxyImpl",
+      "chrome/browser/resources/settings/people_page/account_manager_browser_proxy.html|AccountManagerBrowserProxy,AccountManagerBrowserProxyImpl",
+      "chrome/browser/resources/settings/route.html|routes",
+      "chrome/browser/resources/settings/router.html|Router,Route,RouteObserverBehavior",
+      "ui/webui/resources/html/polymer.html|Polymer,html,flush",
+      "ui/webui/resources/html/icon.html|getImage",
+    ]
 
 os_settings_migrated_imports = settings_migrated_imports
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
index 2e8abe7..7f744ed 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
@@ -117,11 +117,6 @@
            use_base_dir="false"
            compress="false"
            type="BINDATA" />
-  <include name="IDR_OS_SETTINGS_DEEP_LINKING_BEHAVIOR_M_JS"
-           file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/deep_linking_behavior.m.js"
-           use_base_dir="false"
-           compress="false"
-           type="BINDATA" />
   <include name="IDR_OS_SETTINGS_LOCALIZED_LINK_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/localized_link/localized_link.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 2180101a..9cc11d9 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -510,12 +510,6 @@
       <structure name="IDR_OS_SETTINGS_CONTROLS_TOGGLE_BUTTON_JS"
                  file="controls/settings_toggle_button.js"
                  compress="false" type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_DEEP_LINKING_BEHAVIOR_HTML"
-                 file="chromeos/deep_linking_behavior.html"
-                 compress="false" type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_DEEP_LINKING_BEHAVIOR_JS"
-                 file="chromeos/deep_linking_behavior.js"
-                 compress="false" type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_DEVICE_BROWSER_PROXY_HTML"
                  file="chromeos/device_page/device_page_browser_proxy.html"
                  compress="false" type="chrome_html" />
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_app.html b/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
index 2457245f..baab767 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
@@ -3,7 +3,6 @@
     display: flex;
     font-size: 100%;
     margin: 0;
-    min-height: 100vh;
   }
 </style>
 <cr-view-manager id="viewManager">
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
index c9cc25e..7d55024 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html
@@ -26,10 +26,11 @@
     right: 0;
   }
 
- .profiles-container {
+  .profiles-container {
     --grid-gutter: 24px;
     --profile-item-height:178px;
     --profile-item-width: 162px;
+    --scrollbar-width: 4px;
     align-items: center;
     display: grid;
     grid-column-gap: var(--grid-gutter);
@@ -39,9 +40,29 @@
     margin: auto;
     margin-inline-end: 140px;
     margin-inline-start: 140px;
- }
+    max-height: calc((var(--profile-item-height) + var(--grid-gutter)) * 2 +
+        30px);
+    overflow-y: auto;
+    padding-inline-end: var(--scrollbar-width);
+    padding-inline-start: var(--scrollbar-width);
+  }
 
- .profile-item {
+  .profiles-container::-webkit-scrollbar {
+    width: var(--scrollbar-width);
+  }
+
+  /* Track */
+  .profiles-container::-webkit-scrollbar-track {
+    border-radius: var(--scrollbar-width);
+  }
+
+  /* Handle */
+  .profiles-container::-webkit-scrollbar-thumb {
+    background: var(--google-grey-refresh-100);
+    border-radius: var(--scrollbar-width);
+  }
+
+  .profile-item {
     align-items: center;
     border-radius: 12px;
     display: flex;
@@ -49,25 +70,25 @@
     height: var(--profile-item-height);
     justify-content: center;
     width: var(--profile-item-width);
- }
+  }
 
- #addProfile {
-  border: 1px dashed;
-  border-color:  var(--google-grey-400);
- }
+  #addProfile {
+    border: 1px dashed;
+    border-color:  var(--google-grey-400);
+  }
 
- cr-icon-button[iron-icon='profiles:add'] {
-  --cr-icon-button-icon-size: var(--avatar-icon-size);
-  --cr-icon-button-size: 84px;
-  --cr-icon-button-fill-color: var(--google-grey-refresh-100);
-  --cr-icon-button-margin-end: 0px;
-  --cr-icon-button-margin-start: 0px;
-  --cr-icon-button-stroke-color: var(--google-grey-refresh-700);
- }
+  cr-icon-button[iron-icon='profiles:add'] {
+    --cr-icon-button-icon-size: var(--avatar-icon-size);
+    --cr-icon-button-size: 84px;
+    --cr-icon-button-fill-color: var(--google-grey-refresh-100);
+    --cr-icon-button-margin-end: 0;
+    --cr-icon-button-margin-start: 0;
+    --cr-icon-button-stroke-color: var(--google-grey-refresh-700);
+  }
 
- @media (prefers-color-scheme: dark) {
-  /* TODO(msalama): Dark mode mocks not ready yet.*/
- }
+  @media (prefers-color-scheme: dark) {
+    /* TODO(msalama): Dark mode mocks not ready yet.*/
+  }
 </style>
 
 <div id="leftBanner" class="banner"></div>
diff --git a/chrome/browser/safety_check/android/java/res/xml/safety_check_preferences.xml b/chrome/browser/safety_check/android/java/res/xml/safety_check_preferences.xml
index 8f5fd07c..178d3fc 100644
--- a/chrome/browser/safety_check/android/java/res/xml/safety_check_preferences.xml
+++ b/chrome/browser/safety_check/android/java/res/xml/safety_check_preferences.xml
@@ -11,11 +11,11 @@
         android:title="@string/safety_check_description"
         app:allowDividerBelow="false" />
 
-    <!-- Safe Browsing -->
+    <!-- Updates -->
     <org.chromium.chrome.browser.safety_check.SafetyCheckElementPreference
-        android:key="safe_browsing"
-        android:title="@string/safety_check_safe_browsing_title"
-        android:icon="@drawable/ic_security_grey"
+        android:key="updates"
+        android:title="@string/safety_check_updates_title"
+        android:icon="@drawable/ic_update_grey"
         app:iconTint="@color/default_icon_color"
         app:allowDividerBelow="false" />
     <!-- Passwords -->
@@ -25,11 +25,11 @@
         android:icon="@drawable/ic_vpn_key_grey"
         app:iconTint="@color/default_icon_color"
         app:allowDividerBelow="false" />
-    <!-- Updates -->
+    <!-- Safe Browsing -->
     <org.chromium.chrome.browser.safety_check.SafetyCheckElementPreference
-        android:key="updates"
-        android:title="@string/safety_check_updates_title"
-        android:icon="@drawable/ic_update_grey"
+        android:key="safe_browsing"
+        android:title="@string/safety_check_safe_browsing_title"
+        android:icon="@drawable/ic_security_grey"
         app:iconTint="@color/default_icon_color"
         app:allowDividerBelow="false" />
 
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index bbc4177..dca9903 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -11,6 +11,7 @@
 #include "components/permissions/permission_request_id.h"
 #include "components/permissions/permission_request_manager.h"
 #include "components/permissions/test/mock_permission_prompt_factory.h"
+#include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 
@@ -61,8 +62,7 @@
   void ExhaustImplicitGrants(
       const GURL& requesting_origin,
       StorageAccessGrantPermissionContext& permission_context) {
-    permissions::PermissionRequestID fake_id(
-        /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+    permissions::PermissionRequestID fake_id = CreateFakeID();
 
     permissions::PermissionRequestManager* manager =
         permissions::PermissionRequestManager::FromWebContents(web_contents());
@@ -83,9 +83,15 @@
     }
   }
 
+  permissions::PermissionRequestID CreateFakeID() {
+    return permissions::PermissionRequestID(web_contents()->GetMainFrame(),
+                                            ++next_request_id_);
+  }
+
  private:
   std::unique_ptr<permissions::MockPermissionPromptFactory>
       mock_permission_prompt_factory_;
+  int next_request_id_ = 0;
 };
 
 TEST_F(StorageAccessGrantPermissionContextTest, InsecureOriginsAreAllowed) {
@@ -104,8 +110,7 @@
   scoped_disable.InitAndDisableFeature(blink::features::kStorageAccessAPI);
 
   StorageAccessGrantPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id(
-      /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+  permissions::PermissionRequestID fake_id = CreateFakeID();
 
   ContentSetting result = CONTENT_SETTING_DEFAULT;
   permission_context.DecidePermission(
@@ -122,8 +127,7 @@
   scoped_enable.InitAndEnableFeature(blink::features::kStorageAccessAPI);
 
   StorageAccessGrantPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id(
-      /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+  permissions::PermissionRequestID fake_id = CreateFakeID();
 
   const GURL requesting_origin(kAlternateURL);
   const GURL embedding_origin(kSecureURL);
@@ -170,8 +174,7 @@
   scoped_enable.InitAndEnableFeature(blink::features::kStorageAccessAPI);
 
   StorageAccessGrantPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id(
-      /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+  permissions::PermissionRequestID fake_id = CreateFakeID();
 
   ContentSetting result = CONTENT_SETTING_DEFAULT;
   permission_context.DecidePermission(
@@ -219,8 +222,7 @@
   histogram_tester.ExpectTotalCount(kGrantIsImplicitHistogram, 0);
 
   StorageAccessGrantPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id(
-      /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+  permissions::PermissionRequestID fake_id = CreateFakeID();
 
   const GURL requesting_origin_1(kAlternateURL);
   const GURL requesting_origin_2(kInsecureURL);
@@ -286,8 +288,7 @@
   histogram_tester.ExpectTotalCount(kPromptResultHistogram, 0);
 
   StorageAccessGrantPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id(
-      /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+  permissions::PermissionRequestID fake_id = CreateFakeID();
 
   const GURL requesting_origin_1(kAlternateURL);
   const GURL requesting_origin_2(kInsecureURL);
@@ -335,8 +336,7 @@
   histogram_tester.ExpectTotalCount(kPromptResultHistogram, 0);
 
   StorageAccessGrantPermissionContext permission_context(profile());
-  permissions::PermissionRequestID fake_id(
-      /*render_process_id=*/0, /*render_frame_id=*/0, /*request_id=*/0);
+  permissions::PermissionRequestID fake_id = CreateFakeID();
 
   const GURL requesting_origin_1(kAlternateURL);
   const GURL requesting_origin_2(kInsecureURL);
diff --git a/chrome/browser/sync/test/integration/password_manager_sync_test.cc b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
index b7e02fc..7287e9c 100644
--- a/chrome/browser/sync/test/integration/password_manager_sync_test.cc
+++ b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
@@ -5,7 +5,9 @@
 #include <memory>
 #include <string>
 
+#include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
@@ -77,6 +79,8 @@
         {password_manager::features::kEnablePasswordsAccountStorage,
          password_manager::features::kFillOnAccountSelect},
         {});
+
+    DisableVerifier();
   }
 
   ~PasswordManagerSyncTest() override = default;
@@ -104,13 +108,17 @@
 
   // Also stores the AccountInfo for the signed-in account in
   // |signed_in_account_| as a side effect.
-  void SetupSyncTransportWithPasswordAccountStorage() {
+  void SetupSyncTransportWithoutPasswordAccountStorage() {
     ASSERT_TRUE(signed_in_account_.IsEmpty());
     // Setup Sync for a secondary account (i.e. in transport mode).
     signed_in_account_ = secondary_account_helper::SignInSecondaryAccount(
         GetProfile(0), &test_url_loader_factory_, "user@email.com");
     ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
     ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureEnabled());
+  }
+
+  void SetupSyncTransportWithPasswordAccountStorage() {
+    SetupSyncTransportWithoutPasswordAccountStorage();
 
     // Let the user opt in to the account-scoped password storage, and wait for
     // it to become active.
@@ -655,6 +663,57 @@
       GetProfile(0)->GetPrefs(), GetSyncService(0)));
 }
 
+IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest,
+                       PRE_ClearAccountStoreOnStartup) {
+  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+  SetupSyncTransportWithoutPasswordAccountStorage();
+
+  ASSERT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
+      GetProfile(0)->GetPrefs(), GetSyncService(0)));
+
+  // Manually add a credential to the account store, without actually opting in.
+  // This simulates the case (for the following test) where the user revoked
+  // their opt-in, but the account store was *not* cleared correctly, e.g. due
+  // to a poorly-timed crash.
+  auto* account_store = passwords_helper::GetAccountPasswordStore(0);
+  account_store->AddLogin(CreateTestPasswordForm("accountuser", "accountpass"));
+
+  // Also add a credential to the profile store.
+  AddLocalCredential("localuser", "localpass");
+
+  ASSERT_THAT(GetAllLoginsFromProfilePasswordStore(),
+              ElementsAre(MatchesLogin("localuser", "localpass")));
+  ASSERT_THAT(GetAllLoginsFromAccountPasswordStore(),
+              ElementsAre(MatchesLogin("accountuser", "accountpass")));
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, ClearAccountStoreOnStartup) {
+  base::HistogramTester histograms;
+
+  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+
+  ASSERT_FALSE(password_manager::features_util::IsOptedInForAccountStorage(
+      GetProfile(0)->GetPrefs(), GetSyncService(0)));
+
+  // The "original" is defined in password_model_type_controller.cc.
+  const int kNotOptedInAndHadToClear = 2;
+
+  // Since there's no opt-in, the account-scoped store should have been cleared
+  // during startup, and the credential added by the PRE_ test should be gone.
+  EXPECT_THAT(GetAllLoginsFromAccountPasswordStore(), IsEmpty());
+  histograms.ExpectUniqueSample(
+      "PasswordManager.AccountStorage.ClearedOnStartup",
+      /*sample=*/kNotOptedInAndHadToClear, 1);
+
+  // Just as a sanity check: The credential in the profile-scoped store should
+  // still be there.
+  ASSERT_THAT(GetAllLoginsFromProfilePasswordStore(),
+              ElementsAre(MatchesLogin("localuser", "localpass")));
+}
+
 #endif  // !defined(OS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 2548a43..b339f07d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -13,7 +13,6 @@
 import("//chrome/common/features.gni")
 import("//chromeos/assistant/assistant.gni")
 import("//chromeos/dbus/use_real_dbus_clients.gni")
-import("//components/feature_engagement/features.gni")
 import("//components/feed/features.gni")
 import("//components/nacl/features.gni")
 import("//components/offline_pages/buildflags/features.gni")
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 7177aef..d248b10 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2266,6 +2266,12 @@
       <message name="IDS_SIGNIN_ADD_ACCOUNT_TO_DEVICE" desc="Text for adding another Google Account to device in the web sign-in flow and account picker dialog, users will be asked to add an account to his device when this text is tapped">
         Add account to device
       </message>
+      <message name="IDS_SIGNIN_INCOGNITO_MODE_PRIMARY" desc="Text for signing in incognito mode.">
+        Go Incognito mode
+      </message>
+      <message name="IDS_SIGNIN_INCOGNITO_MODE_SECONDARY" desc="Secondary text for signing in incognito mode.">
+        To sign in temporarily
+      </message>
       <message name="IDS_SIGNIN_GMS_UPDATING" desc="Message notifying user that Google Play Services is still updating.">
         Waiting for Google Play Services to finish updating
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SIGNIN_INCOGNITO_MODE_PRIMARY.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SIGNIN_INCOGNITO_MODE_PRIMARY.png.sha1
new file mode 100644
index 0000000..51e161e
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SIGNIN_INCOGNITO_MODE_PRIMARY.png.sha1
@@ -0,0 +1 @@
+00b3862c131811be24b2fed0adac72fc840cbf7e
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SIGNIN_INCOGNITO_MODE_SECONDARY.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SIGNIN_INCOGNITO_MODE_SECONDARY.png.sha1
new file mode 100644
index 0000000..db670dd
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SIGNIN_INCOGNITO_MODE_SECONDARY.png.sha1
@@ -0,0 +1 @@
+d4233fcfc1d4055cc76e52420d63fac31a2eb110
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index f6952fd..014b80f 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "base/macros.h"
+#include "chrome/browser/apps/app_service/app_service_metrics.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
@@ -620,6 +621,9 @@
   apps::LaunchPlatformAppWithUrl(profile, extension,
                                  /*handler_id=*/std::string(), url,
                                  /*referrer_url=*/GURL());
+
+  apps::RecordAppLaunch(extension_misc::kCameraAppId,
+                        apps::mojom::LaunchSource::kFromArc);
 }
 
 void ChromeNewWindowClient::CloseCameraApp() {
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
index 38edfe79..78e95bb 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
@@ -640,7 +640,7 @@
   auto is_hidden = [](aura::Window* w) {
     return w->GetProperty(ash::kHideInShelfKey);
   };
-  EXPECT_EQ(1u, std::count_if(windows.begin(), windows.end(), is_hidden));
+  EXPECT_EQ(1, std::count_if(windows.begin(), windows.end(), is_hidden));
 
   // The hidden window should be task_id 2.
   aura::Window* window1 =
@@ -670,7 +670,7 @@
   app_host()->OnTaskDestroyed(1);
   windows = app_service_proxy_->InstanceRegistry().GetWindows(app_id);
   EXPECT_EQ(1u, windows.size());
-  EXPECT_EQ(0u, std::count_if(windows.begin(), windows.end(), is_hidden));
+  EXPECT_EQ(0, std::count_if(windows.begin(), windows.end(), is_hidden));
 
   // Close second window.
   app_host()->OnTaskDestroyed(2);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index be3fc3d8..a9ad1a8 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1960,14 +1960,18 @@
   return blink::mojom::DisplayMode::kBrowser;
 }
 
-void Browser::RegisterProtocolHandler(WebContents* web_contents,
-                                      const std::string& protocol,
-                                      const GURL& url,
-                                      bool user_gesture) {
-  content::BrowserContext* context = web_contents->GetBrowserContext();
+void Browser::RegisterProtocolHandler(
+    content::RenderFrameHost* requesting_frame,
+    const std::string& protocol,
+    const GURL& url,
+    bool user_gesture) {
+  content::BrowserContext* context = requesting_frame->GetBrowserContext();
   if (context->IsOffTheRecord())
     return;
 
+  auto* web_contents =
+      content::WebContents::FromRenderFrameHost(requesting_frame);
+
   // Permission request UI cannot currently be rendered binocularly in VR mode,
   // so we suppress the UI. crbug.com/736568
   if (vr::VrTabHelper::IsInVr(web_contents))
@@ -1984,6 +1988,8 @@
   if (registry->SilentlyHandleRegisterHandlerRequest(handler))
     return;
 
+  // TODO(carlscab): This should probably be FromFrame() once it becomes
+  // PageSpecificContentSettingsDelegate
   auto* tab_content_settings_delegate =
       chrome::TabSpecificContentSettingsDelegate::FromWebContents(web_contents);
   if (!user_gesture && window_) {
@@ -2010,18 +2016,20 @@
         web_contents->ForSecurityDropFullscreen();
 
     permission_request_manager->AddRequest(
+        requesting_frame,
         new RegisterProtocolHandlerPermissionRequest(
             registry, handler, url, user_gesture, std::move(fullscreen_block)));
   }
 }
 
-void Browser::UnregisterProtocolHandler(WebContents* web_contents,
-                                        const std::string& protocol,
-                                        const GURL& url,
-                                        bool user_gesture) {
+void Browser::UnregisterProtocolHandler(
+    content::RenderFrameHost* requesting_frame,
+    const std::string& protocol,
+    const GURL& url,
+    bool user_gesture) {
   // user_gesture will be used in case we decide to have confirmation bubble
   // for user while un-registering the handler.
-  content::BrowserContext* context = web_contents->GetBrowserContext();
+  content::BrowserContext* context = requesting_frame->GetBrowserContext();
   if (context->IsOffTheRecord())
     return;
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 9b84d806..7e57839 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -807,11 +807,11 @@
       const content::WebContents* web_contents) override;
   blink::mojom::DisplayMode GetDisplayMode(
       const content::WebContents* web_contents) override;
-  void RegisterProtocolHandler(content::WebContents* web_contents,
+  void RegisterProtocolHandler(content::RenderFrameHost* requesting_frame,
                                const std::string& protocol,
                                const GURL& url,
                                bool user_gesture) override;
-  void UnregisterProtocolHandler(content::WebContents* web_contents,
+  void UnregisterProtocolHandler(content::RenderFrameHost* requesting_frame,
                                  const std::string& protocol,
                                  const GURL& url,
                                  bool user_gesture) override;
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index dc0b428..c756a6b 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -50,7 +50,6 @@
 #include "chrome/common/url_constants.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/dom_distiller/core/dom_distiller_features.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/prefs/pref_service.h"
 #include "components/sessions/core/tab_restore_service.h"
@@ -87,13 +86,6 @@
 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/bookmark/bookmark_tracker.h"
-#include "chrome/browser/feature_engagement/bookmark/bookmark_tracker_factory.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
 #if defined(USE_OZONE)
 #include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -401,16 +393,6 @@
       break;
     case IDC_NEW_TAB: {
       NewTab(browser_);
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-      // This is not in NewTab() to avoid tracking programmatic creation of new
-      // tabs by extensions.
-      auto* new_tab_tracker =
-          feature_engagement::NewTabTrackerFactory::GetInstance()
-              ->GetForProfile(profile());
-
-      new_tab_tracker->OnNewTabOpened();
-      new_tab_tracker->CloseBubble();
-#endif
       break;
     }
     case IDC_CLOSE_TAB:
@@ -516,19 +498,9 @@
       SavePage(browser_);
       break;
     case IDC_BOOKMARK_THIS_TAB:
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-      feature_engagement::BookmarkTrackerFactory::GetInstance()
-          ->GetForProfile(profile())
-          ->OnBookmarkAdded();
-#endif
       BookmarkCurrentTab(browser_);
       break;
     case IDC_BOOKMARK_ALL_TABS:
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-      feature_engagement::BookmarkTrackerFactory::GetInstance()
-          ->GetForProfile(profile())
-          ->OnBookmarkAdded();
-#endif
       BookmarkAllTabs(browser_);
       break;
     case IDC_VIEW_SOURCE:
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 7fc8c7c..ad14031f 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -81,7 +81,6 @@
 #include "components/browsing_data/content/browsing_data_helper.h"
 #include "components/dom_distiller/core/url_utils.h"
 #include "components/favicon/content/content_favicon_driver.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/find_in_page/find_tab_helper.h"
 #include "components/find_in_page/find_types.h"
 #include "components/google/core/common/google_util.h"
@@ -145,11 +144,6 @@
 #include "components/rlz/rlz_tracker.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h"
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h"
-#endif
-
 namespace {
 
 const char kOsOverrideForTabletSite[] = "Linux; Android 9; Chrome tablet";
@@ -630,11 +624,6 @@
 }
 
 void NewIncognitoWindow(Profile* profile) {
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  feature_engagement::IncognitoWindowTrackerFactory::GetInstance()
-      ->GetForProfile(profile)
-      ->OnIncognitoWindowOpened();
-#endif
   NewEmptyWindow(profile->GetPrimaryOTRProfile());
 }
 
diff --git a/chrome/browser/ui/browser_tab_restorer.cc b/chrome/browser/ui/browser_tab_restorer.cc
index c56811ed..4276303 100644
--- a/chrome/browser/ui/browser_tab_restorer.cc
+++ b/chrome/browser/ui/browser_tab_restorer.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/ui/browser_live_tab_context.h"
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/sessions/core/tab_restore_service_observer.h"
 
diff --git a/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
index d23e8a0..c080488 100644
--- a/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
+++ b/chrome/browser/ui/cocoa/permission_bubble/permission_bubble_views_cocoa_browsertest.mm
@@ -30,7 +30,9 @@
       std::make_unique<test::PermissionRequestManagerTestApi>(browser);
   EXPECT_TRUE(test_api->manager());
 
-  test_api->AddSimpleRequest(ContentSettingsType::GEOLOCATION);
+  test_api->AddSimpleRequest(
+      browser->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+      ContentSettingsType::GEOLOCATION);
 
   // The PermissionRequestManager displays prompts asynchronously.
   EXPECT_FALSE(test_api->GetPromptWindow());
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
index d3144b4f5..0658050 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
@@ -468,7 +468,7 @@
       ContentSettingImageModel::CreateForContentType(
           ContentSettingImageModel::ImageType::NOTIFICATIONS_QUIET_PROMPT);
   EXPECT_FALSE(content_setting_image_model->is_visible());
-  manager_->AddRequest(&request_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   content_setting_image_model->Update(web_contents());
@@ -492,7 +492,7 @@
       std::make_unique<TestQuietNotificationPermissionUiSelector>(
           permissions::NotificationPermissionUiSelector::QuietUiReason::
               kTriggeredByCrowdDeny));
-  manager_->AddRequest(&request_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   content_setting_image_model->Update(web_contents());
@@ -513,7 +513,7 @@
       std::make_unique<TestQuietNotificationPermissionUiSelector>(
           permissions::NotificationPermissionUiSelector::QuietUiReason::
               kTriggeredDueToAbusiveRequests));
-  manager_->AddRequest(&request_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   content_setting_image_model->Update(web_contents());
@@ -534,7 +534,7 @@
       std::make_unique<TestQuietNotificationPermissionUiSelector>(
           permissions::NotificationPermissionUiSelector::QuietUiReason::
               kTriggeredDueToAbusiveContent));
-  manager_->AddRequest(&request_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   content_setting_image_model->Update(web_contents());
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 777ce1a..c3d9f19 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -53,7 +53,6 @@
 #include "chrome/common/url_constants.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/favicon/core/favicon_service.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/location_bar_model.h"
@@ -79,11 +78,6 @@
 #include "ui/gfx/vector_icon_types.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
 using predictors::AutocompleteActionPredictor;
 
 ChromeOmniboxClient::ChromeOmniboxClient(OmniboxEditController* controller,
@@ -383,20 +377,6 @@
 }
 
 void ChromeOmniboxClient::OnURLOpenedFromOmnibox(OmniboxLog* log) {
-// The new tab tracker tracks when a user starts a session in the same
-// tab as a previous one. If ShouldDisplayURL() is true, that's a good
-// signal that the previous page was part of some other session.
-// We could go further to try to analyze the difference between the previous
-// and current URLs, but users edit URLs rarely enough that this is a
-// reasonable approximation.
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  if (controller_->GetLocationBarModel()->ShouldDisplayURL()) {
-    feature_engagement::NewTabTrackerFactory::GetInstance()
-        ->GetForProfile(profile_)
-        ->OnOmniboxNavigation();
-  }
-#endif
-
   predictors::AutocompleteActionPredictorFactory::GetForProfile(profile_)
       ->OnOmniboxOpenedUrl(*log);
 }
diff --git a/chrome/browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller_unittest.cc b/chrome/browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller_unittest.cc
index ed3c33c..d87170c 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate_mock.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/password_manager/core/browser/mock_password_feature_manager.h"
@@ -28,6 +29,7 @@
 #include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/core/common/password_manager_ui.h"
+#include "components/sync/driver/test_sync_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
@@ -59,6 +61,11 @@
 constexpr char kUIDismissalReasonUpdateMetric[] =
     "PasswordManager.UpdateUIDismissalReason";
 
+std::unique_ptr<KeyedService> BuildTestSyncService(
+    content::BrowserContext* context) {
+  return std::make_unique<syncer::TestSyncService>();
+}
+
 }  // namespace
 
 class SaveUpdateWithAccountStoreBubbleControllerTest : public ::testing::Test {
@@ -86,6 +93,8 @@
             &password_manager::BuildPasswordStore<
                 content::BrowserContext,
                 testing::StrictMock<password_manager::MockPasswordStore>>));
+    ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(
+        profile(), base::BindRepeating(&BuildTestSyncService));
     pending_password_.url = GURL(kSiteOrigin);
     pending_password_.signon_realm = kSiteOrigin;
     pending_password_.username_value = base::ASCIIToUTF16(kUsername);
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 5de94e9..ea318864 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -42,7 +42,6 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
@@ -55,11 +54,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/text_elider.h"
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
 using base::UserMetricsAction;
 using content::WebContents;
 
@@ -1247,13 +1241,6 @@
                                 TabStripModel::NEW_TAB_ENUM_COUNT);
       delegate()->AddTabAt(GURL(), context_index + 1, true,
                            GetTabGroupForTab(context_index));
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-      auto* new_tab_tracker =
-          feature_engagement::NewTabTrackerFactory::GetInstance()
-              ->GetForProfile(profile_);
-      new_tab_tracker->OnNewTabOpened();
-      new_tab_tracker->CloseBubble();
-#endif
       break;
     }
 
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index 8528a63a..8cdfc6f 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -33,7 +33,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/favicon/core/history_ui_favicon_request_handler.h"
 #include "components/favicon_base/favicon_types.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/strings/grit/components_strings.h"
diff --git a/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc b/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc
index b9c36674..f04653e 100644
--- a/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc
@@ -22,7 +22,7 @@
                                 ->GetAppMenuButton();
     // We use one of the strings for the new tab feature promo since there is
     // currently no infrastructure for test-only string resources.
-    int placeholder_string = IDS_NEWTAB_PROMO_0;
+    int placeholder_string = IDS_REOPEN_TAB_PROMO;
     FeaturePromoBubbleView::CreateOwned(
         app_menu_button, views::BubbleBorder::TOP_RIGHT,
         FeaturePromoBubbleView::ActivationAction::ACTIVATE,
diff --git a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
index 14eb938..af601a91 100644
--- a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos_browsertest.cc
@@ -33,6 +33,7 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/permissions/permission_request_impl.h"
 #include "components/permissions/permission_request_manager.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -1358,7 +1359,8 @@
   auto* permission_manager =
       permissions::PermissionRequestManager::FromWebContents(active_contents);
   TopControlsShownRatioWaiter waiter(top_controls_slide_controller());
-  permission_manager->AddRequest(&permission_request);
+  permission_manager->AddRequest(active_contents->GetMainFrame(),
+                                 &permission_request);
   waiter.WaitForRatio(1.f);
   EXPECT_FLOAT_EQ(top_controls_slide_controller()->GetShownRatio(), 1.f);
   CheckBrowserLayout(browser_view(), TopChromeShownState::kFullyShown);
diff --git a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
index 32dbe87..3aab5544 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
@@ -179,7 +179,8 @@
       "notifications",
       permissions::PermissionRequestType::PERMISSION_NOTIFICATIONS,
       GURL("https://example.com"));
-  permission_request_manager->AddRequest(&*notification_permission_request_);
+  permission_request_manager->AddRequest(web_contents->GetMainFrame(),
+                                         &*notification_permission_request_);
   base::RunLoop().RunUntilIdle();
 }
 
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index c5b478e..0d31cd4 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -375,16 +375,16 @@
 void LocationBarView::FocusLocation(bool is_user_initiated) {
   const bool omnibox_already_focused = omnibox_view_->HasFocus();
 
-  if (is_user_initiated)
-    omnibox_view()->model()->Unelide();
-
   omnibox_view_->SetFocus(is_user_initiated);
 
   if (omnibox_already_focused)
     omnibox_view()->model()->ClearKeyword();
 
-  if (is_user_initiated)
-    omnibox_view_->SelectAll(true);
+  if (!is_user_initiated)
+    return;
+
+  omnibox_view_->SelectAll(true);
+  omnibox_view()->model()->Unelide();
 }
 
 void LocationBarView::Revert() {
diff --git a/chrome/browser/ui/views/location_bar/permission_chip_browsertest.cc b/chrome/browser/ui/views/location_bar/permission_chip_browsertest.cc
index c6ee15a..b65d818 100644
--- a/chrome/browser/ui/views/location_bar/permission_chip_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/permission_chip_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/run_loop.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -13,6 +14,7 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/permissions/permission_request_manager_test_api.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 
 class PermissionChipBrowserTest : public UiBrowserTest {
@@ -30,7 +32,8 @@
     std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_ =
         std::make_unique<test::PermissionRequestManagerTestApi>(browser());
     EXPECT_TRUE(test_api_->manager());
-    test_api_->AddSimpleRequest(ContentSettingsType::GEOLOCATION);
+    test_api_->AddSimpleRequest(GetActiveMainFrame(),
+                                ContentSettingsType::GEOLOCATION);
 
     base::RunLoop().RunUntilIdle();
   }
@@ -49,6 +52,10 @@
     ui_test_utils::WaitForBrowserToClose();
   }
 
+  content::RenderFrameHost* GetActiveMainFrame() {
+    return browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
diff --git a/chrome/browser/ui/views/location_bar/permission_chip_prompt_browsertest.cc b/chrome/browser/ui/views/location_bar/permission_chip_prompt_browsertest.cc
index e19a9eb..604af08 100644
--- a/chrome/browser/ui/views/location_bar/permission_chip_prompt_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/permission_chip_prompt_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/run_loop.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -12,6 +13,7 @@
 #include "chrome/browser/ui/views/location_bar/permission_chip.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/test/permissions/permission_request_manager_test_api.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "ui/events/base_event_utils.h"
 
@@ -31,7 +33,8 @@
     std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_ =
         std::make_unique<test::PermissionRequestManagerTestApi>(browser());
     EXPECT_TRUE(test_api_->manager());
-    test_api_->AddSimpleRequest(ContentSettingsType::GEOLOCATION);
+    test_api_->AddSimpleRequest(GetActiveMainFrame(),
+                                ContentSettingsType::GEOLOCATION);
 
     base::RunLoop().RunUntilIdle();
 
@@ -47,6 +50,10 @@
                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
   }
 
+  content::RenderFrameHost* GetActiveMainFrame() {
+    return browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index 5297484..6309f51 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -28,25 +28,6 @@
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/paint_vector_icon.h"
 
-namespace {
-
-// For bookmark in-product help.
-int GetBookmarkPromoStringSpecifier() {
-  static constexpr int kTextIds[] = {IDS_BOOKMARK_PROMO_0, IDS_BOOKMARK_PROMO_1,
-                                     IDS_BOOKMARK_PROMO_2};
-  const std::string& str = variations::GetVariationParamValue(
-      "BookmarkInProductHelp", "x_promo_string");
-  size_t text_specifier;
-  if (!base::StringToSizeT(str, &text_specifier) ||
-      text_specifier >= base::size(kTextIds)) {
-    text_specifier = 0;
-  }
-
-  return kTextIds[text_specifier];
-}
-
-}  // namespace
-
 StarView::StarView(CommandUpdater* command_updater,
                    Browser* browser,
                    IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
@@ -67,21 +48,6 @@
 
 StarView::~StarView() {}
 
-void StarView::ShowPromo() {
-  FeaturePromoBubbleView* bookmark_promo_bubble =
-      FeaturePromoBubbleView::CreateOwned(
-          this, views::BubbleBorder::TOP_RIGHT,
-          FeaturePromoBubbleView::ActivationAction::ACTIVATE,
-          /*title_string_specifier=*/base::nullopt,
-          GetBookmarkPromoStringSpecifier());
-  if (!bookmark_promo_observer_.IsObserving(
-          bookmark_promo_bubble->GetWidget())) {
-    bookmark_promo_observer_.Add(bookmark_promo_bubble->GetWidget());
-    SetActive(false);
-    UpdateIconImage();
-  }
-}
-
 void StarView::UpdateImpl() {
   SetVisible(browser_defaults::bookmarks_enabled &&
              edit_bookmarks_enabled_.GetValue());
@@ -126,21 +92,6 @@
   return "StarView";
 }
 
-SkColor StarView::GetInkDropBaseColor() const {
-  return bookmark_promo_observer_.IsObservingSources()
-             ? GetNativeTheme()->GetSystemColor(
-                   ui::NativeTheme::kColorId_ProminentButtonColor)
-             : PageActionIconView::GetInkDropBaseColor();
-}
-
-void StarView::OnWidgetDestroying(views::Widget* widget) {
-  if (bookmark_promo_observer_.IsObserving(widget)) {
-    bookmark_promo_observer_.Remove(widget);
-    SetActive(false);
-    UpdateIconImage();
-  }
-}
-
 void StarView::EditBookmarksPrefUpdated() {
   Update();
 }
diff --git a/chrome/browser/ui/views/location_bar/star_view.h b/chrome/browser/ui/views/location_bar/star_view.h
index 787e841..2c8c611e 100644
--- a/chrome/browser/ui/views/location_bar/star_view.h
+++ b/chrome/browser/ui/views/location_bar/star_view.h
@@ -9,14 +9,12 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "components/prefs/pref_member.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_observer.h"
 
 class Browser;
 class CommandUpdater;
 
 // The star icon to show a bookmark bubble.
-class StarView : public PageActionIconView, public views::WidgetObserver {
+class StarView : public PageActionIconView {
  public:
   StarView(CommandUpdater* command_updater,
            Browser* browser,
@@ -24,23 +22,16 @@
            PageActionIconView::Delegate* page_action_icon_delegate);
   ~StarView() override;
 
-  // Shows the BookmarkPromoBubbleView when the BookmarkTracker calls for it.
-  void ShowPromo();
-
  protected:
   // PageActionIconView:
   void UpdateImpl() override;
   void OnExecuting(PageActionIconView::ExecuteSource execute_source) override;
   void ExecuteCommand(ExecuteSource source) override;
   views::BubbleDialogDelegate* GetBubble() const override;
-  SkColor GetInkDropBaseColor() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
   base::string16 GetTextForTooltipAndAccessibleName() const override;
   const char* GetClassName() const override;
 
-  // views::WidgetObserver:
-  void OnWidgetDestroying(views::Widget* widget) override;
-
  private:
   void EditBookmarksPrefUpdated();
   bool IsBookmarkStarHiddenByExtension() const;
@@ -49,11 +40,6 @@
 
   BooleanPrefMember edit_bookmarks_enabled_;
 
-  // Observes the BookmarkPromoBubbleView's widget. Used to tell whether the
-  // promo is open and gets called back when it closes.
-  ScopedObserver<views::Widget, views::WidgetObserver> bookmark_promo_observer_{
-      this};
-
   DISALLOW_COPY_AND_ASSIGN(StarView);
 };
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index f4697da..743d27f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_result_view.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/location_bar_model.h"
@@ -97,11 +96,6 @@
 #include "chrome/browser/browser_process.h"
 #endif
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
 using metrics::OmniboxEventProto;
 
 namespace {
@@ -1609,21 +1603,6 @@
   if (location_bar_view_ && model()->is_keyword_hint())
     location_bar_view_->Layout();
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  // The user must be starting a session in the same tab as a previous one in
-  // order to display the new tab in-product help promo.  While focusing the
-  // omnibox is not always a precursor to starting a new session, we don't
-  // want to wait until the user is in the middle of editing or navigating,
-  // because we'd like to show them the promo at the time when it would be
-  // immediately useful.
-  if (location_bar_view_ &&
-      controller()->GetLocationBarModel()->ShouldDisplayURL()) {
-    feature_engagement::NewTabTrackerFactory::GetInstance()
-        ->GetForProfile(location_bar_view_->profile())
-        ->OnOmniboxFocused();
-  }
-#endif
-
   if (location_bar_view_)
     location_bar_view_->OnOmniboxFocused();
 }
diff --git a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view_unittest.cc b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view_unittest.cc
index 494f3e7..5c86a5c 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view_unittest.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view_unittest.cc
@@ -12,12 +12,14 @@
 #include "build/build_config.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/views/passwords/password_bubble_view_test_base.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/password_manager/core/browser/mock_password_feature_manager.h"
 #include "components/password_manager/core/browser/mock_password_store.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/common/password_manager_features.h"
+#include "components/sync/driver/test_sync_service.h"
 #include "content/public/test/navigation_simulator.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -27,6 +29,15 @@
 using ::testing::Return;
 using ::testing::ReturnRef;
 
+namespace {
+
+std::unique_ptr<KeyedService> BuildTestSyncService(
+    content::BrowserContext* context) {
+  return std::make_unique<syncer::TestSyncService>();
+}
+
+}  // namespace
+
 class PasswordSaveUpdateWithAccountStoreViewTest
     : public PasswordBubbleViewTestBase {
  public:
@@ -82,6 +93,8 @@
           &password_manager::BuildPasswordStore<
               content::BrowserContext,
               testing::NiceMock<password_manager::MockPasswordStore>>));
+  ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile(), base::BindRepeating(&BuildTestSyncService));
 }
 
 void PasswordSaveUpdateWithAccountStoreViewTest::CreateViewAndShow() {
diff --git a/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc b/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc
index 946e7b9cd..3d9f8f0 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_bubble_interactive_uitest.cc
@@ -14,6 +14,7 @@
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/permissions/permission_request_manager_test_api.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "ui/base/test/ui_controls.h"
 #include "ui/views/test/widget_test.h"
@@ -63,7 +64,9 @@
         std::make_unique<test::PermissionRequestManagerTestApi>(browser());
     EXPECT_TRUE(test_api_->manager());
 
-    test_api_->AddSimpleRequest(ContentSettingsType::GEOLOCATION);
+    test_api_->AddSimpleRequest(
+        browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+        ContentSettingsType::GEOLOCATION);
 
     EXPECT_TRUE(browser()->window()->IsActive());
 
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc
index 679ca0f4..967da15 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view_browsertest.cc
@@ -5,8 +5,10 @@
 #include "base/run_loop.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/test/permissions/permission_request_manager_test_api.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 
 class PermissionPromptBubbleViewBrowserTest : public DialogBrowserTest {
@@ -23,7 +25,9 @@
     std::unique_ptr<test::PermissionRequestManagerTestApi> test_api_ =
         std::make_unique<test::PermissionRequestManagerTestApi>(browser());
     EXPECT_TRUE(test_api_->manager());
-    test_api_->AddSimpleRequest(ContentSettingsType::GEOLOCATION);
+    test_api_->AddSimpleRequest(
+        browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+        ContentSettingsType::GEOLOCATION);
 
     base::RunLoop().RunUntilIdle();
   }
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 9c87ddcf..7b3fd04f 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -43,7 +43,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/prefs/pref_service.h"
@@ -66,11 +65,6 @@
 #include "ui/views/widget/widget.h"
 #include "url/origin.h"
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
 using base::UserMetricsAction;
 using content::WebContents;
 
@@ -446,14 +440,6 @@
   reopen_tab_iph->NewTabOpened();
 
   model_->delegate()->AddTabAt(GURL(), -1, true);
-
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  auto* new_tab_tracker =
-      feature_engagement::NewTabTrackerFactory::GetInstance()->GetForProfile(
-          browser_view_->browser()->profile());
-  new_tab_tracker->OnNewTabOpened();
-  new_tab_tracker->CloseBubble();
-#endif
 }
 
 void BrowserTabStripController::CreateNewTabWithLocation(
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index b72428c..1496357 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -14,11 +14,9 @@
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/tabs/tab_types.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/variations/variations_associated_data.h"
 #include "ui/base/pointer/touch_ui_controller.h"
 #include "ui/base/theme_provider.h"
@@ -36,30 +34,6 @@
 #include "ui/views/win/hwnd_util.h"
 #endif
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
-#endif
-
-namespace {
-
-// For new tab in-product help.
-int GetNewTabPromoStringSpecifier() {
-  static constexpr int kTextIds[] = {IDS_NEWTAB_PROMO_0, IDS_NEWTAB_PROMO_1,
-                                     IDS_NEWTAB_PROMO_2};
-  const std::string& str = variations::GetVariationParamValue(
-      "NewTabInProductHelp", "x_promo_string");
-  size_t text_specifier;
-  if (!base::StringToSizeT(str, &text_specifier) ||
-      text_specifier >= base::size(kTextIds)) {
-    text_specifier = 0;
-  }
-
-  return kTextIds[text_specifier];
-}
-
-}  // namespace
-
 // static
 constexpr char NewTabButton::kClassName[];
 
@@ -106,37 +80,6 @@
     *destroyed_ = true;
 }
 
-// static
-void NewTabButton::ShowPromoForLastActiveBrowser() {
-  BrowserView* browser = static_cast<BrowserView*>(
-      BrowserList::GetInstance()->GetLastActive()->window());
-  browser->tabstrip()->new_tab_button()->ShowPromo();
-}
-
-// static
-void NewTabButton::CloseBubbleForLastActiveBrowser() {
-  BrowserView* browser = static_cast<BrowserView*>(
-      BrowserList::GetInstance()->GetLastActive()->window());
-  browser->tabstrip()->new_tab_button()->CloseBubble();
-}
-
-void NewTabButton::ShowPromo() {
-  DCHECK(!new_tab_promo_);
-  // Owned by its native widget. Will be destroyed as its widget is destroyed.
-  new_tab_promo_ = FeaturePromoBubbleView::CreateOwned(
-      this, views::BubbleBorder::LEFT_CENTER,
-      FeaturePromoBubbleView::ActivationAction::DO_NOT_ACTIVATE,
-      /*title_string_specifier=*/base::nullopt,
-      GetNewTabPromoStringSpecifier());
-  new_tab_promo_observer_.Add(new_tab_promo_->GetWidget());
-  SchedulePaint();
-}
-
-void NewTabButton::CloseBubble() {
-  if (new_tab_promo_)
-    new_tab_promo_->CloseBubble();
-}
-
 void NewTabButton::FrameColorsChanged() {
   UpdateInkDropBaseColor();
   SchedulePaint();
@@ -220,18 +163,6 @@
   return true;
 }
 
-void NewTabButton::OnWidgetDestroying(views::Widget* widget) {
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  feature_engagement::NewTabTrackerFactory::GetInstance()
-      ->GetForProfile(tab_strip_->controller()->GetProfile())
-      ->OnPromoClosed();
-#endif
-  new_tab_promo_observer_.Remove(widget);
-  new_tab_promo_ = nullptr;
-  // When the promo widget is destroyed, the NewTabButton needs to be recolored.
-  SchedulePaint();
-}
-
 int NewTabButton::GetCornerRadius() const {
   return ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
       views::EMPHASIS_MAXIMUM, GetContentsBounds().size());
@@ -246,7 +177,7 @@
   const float scale = canvas->image_scale();
   const base::Optional<int> bg_id =
       tab_strip_->GetCustomBackgroundId(BrowserFrameActiveState::kUseCurrent);
-  if (bg_id.has_value() && !new_tab_promo_observer_.IsObservingSources()) {
+  if (bg_id.has_value()) {
     float x_scale = scale;
     const gfx::Rect& contents_bounds = GetContentsBounds();
     int x = GetMirroredX() + contents_bounds.x() +
@@ -299,11 +230,6 @@
 }
 
 SkColor NewTabButton::GetButtonFillColor() const {
-  if (new_tab_promo_observer_.IsObservingSources()) {
-    return GetNativeTheme()->GetSystemColor(
-        ui::NativeTheme::kColorId_ProminentButtonColor);
-  }
-
   return GetThemeProvider()->GetDisplayProperty(
              ThemeProperties::SHOULD_FILL_BACKGROUND_TAB_COLOR)
              ? tab_strip_->GetTabBackgroundColor(
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h
index be79ae1..ea2081a 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.h
+++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -10,9 +10,6 @@
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/view.h"
-#include "ui/views/widget/widget_observer.h"
-
-class FeaturePromoBubbleView;
 
 namespace views {
 class InkDropContainerView;
@@ -26,8 +23,7 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 class NewTabButton : public views::ImageButton,
-                     public views::MaskedTargeterDelegate,
-                     public views::WidgetObserver {
+                     public views::MaskedTargeterDelegate {
  public:
   static constexpr char kClassName[] = "NewTabButton";
 
@@ -36,20 +32,6 @@
   NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener);
   ~NewTabButton() override;
 
-  // Retrieves the last active BrowserView instance to display the NewTabPromo.
-  static void ShowPromoForLastActiveBrowser();
-
-  // Returns whether there was a bubble that was closed. A bubble closes only
-  // when it exists.
-  static void CloseBubbleForLastActiveBrowser();
-
-  // Shows the NewTabPromo when the NewTabFeatureEngagementTracker calls for it.
-  void ShowPromo();
-
-  // Returns whether there was a bubble that was closed. A bubble closes only
-  // when it exists.
-  void CloseBubble();
-
   // Called when the tab strip transitions to/from single tab mode, the frame
   // state changes or the accent color changes.  Updates the glyph colors for
   // the best contrast on the background.
@@ -57,8 +39,6 @@
 
   void AnimateInkDropToStateForTesting(views::InkDropState state);
 
-  FeaturePromoBubbleView* new_tab_promo() { return new_tab_promo_; }
-
   // views::View:
   const char* GetClassName() const override;
   void AddLayerBeneathView(ui::Layer* new_layer) override;
@@ -83,9 +63,6 @@
   // views::MaskedTargeterDelegate:
   bool GetHitTestMask(SkPath* mask) const override;
 
-  // views::WidgetObserver:
-  void OnWidgetDestroying(views::Widget* widget) override;
-
   // Returns the radius to use for the button corners.
   int GetCornerRadius() const;
 
@@ -111,17 +88,9 @@
   // Contains our ink drop layer so it can paint above our background.
   views::InkDropContainerView* ink_drop_container_;
 
-  // Promotional UI that appears next to the NewTabButton and encourages its
-  // use. Owned by its NativeWidget.
-  FeaturePromoBubbleView* new_tab_promo_ = nullptr;
-
   // were we destroyed?
   bool* destroyed_ = nullptr;
 
-  // Observes the NewTabPromo's Widget.  Used to tell whether the promo is
-  // open and get called back when it closes.
-  ScopedObserver<views::Widget, WidgetObserver> new_tab_promo_observer_{this};
-
   DISALLOW_COPY_AND_ASSIGN(NewTabButton);
 };
 
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
index aa3a100..670ecca 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -13,7 +13,6 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
-#include "components/feature_engagement/buildflags.h"
 #include "ui/views/view.h"
 
 class ToolbarView;
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index cd44688e..9b4ad48 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -1028,8 +1028,10 @@
 
   // Toggles the Chrome OS Kerberos Accounts submenu in the People section.
   // Note that the handler is also dependent on this pref.
-  html_source->AddBoolean("isKerberosEnabled",
-                          kerberos_credentials_manager_->IsKerberosEnabled());
+  html_source->AddBoolean(
+      "isKerberosEnabled",
+      kerberos_credentials_manager_ != nullptr &&
+          kerberos_credentials_manager_->IsKerberosEnabled());
 
   PrefService* local_state = g_browser_process->local_state();
 
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index cfe4a65b..30844c9 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -289,7 +289,7 @@
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "SyncSetupShowSetupUI",
-      base::BindRepeating(&PeopleHandler::HandleShowSetupUI,
+      base::BindRepeating(&PeopleHandler::HandleShowSyncSetupUI,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "SyncSetupGetSyncStatus",
@@ -618,7 +618,7 @@
     ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
 }
 
-void PeopleHandler::HandleShowSetupUI(const base::ListValue* args) {
+void PeopleHandler::HandleShowSyncSetupUI(const base::ListValue* args) {
   AllowJavascript();
 
   syncer::SyncService* service = GetSyncService();
@@ -638,6 +638,8 @@
   // Observe the web contents for a before unload event.
   Observe(web_ui()->GetWebContents());
 
+  MaybeMarkSyncConfiguring();
+
   PushSyncPrefs();
 
   // Focus the web contents in case the location bar was focused before. This
@@ -865,9 +867,10 @@
 
 void PeopleHandler::OnStateChanged(syncer::SyncService* sync) {
   UpdateSyncStatus();
-
-  // When the SyncService changes its state, we should also push the updated
-  // sync preferences.
+  // TODO(crbug.com/1106764): Re-evaluate marking sync as configuring here,
+  // since this gets called whenever ProfileSyncService changes state. Inline
+  // MaybeMarkSyncConfiguring() then.
+  MaybeMarkSyncConfiguring();
   PushSyncPrefs();
 }
 
@@ -955,21 +958,13 @@
 }
 
 void PeopleHandler::PushSyncPrefs() {
-#if !defined(OS_CHROMEOS)
-  // Early exit if the user has not signed in yet.
-  if (IsProfileAuthNeededOrHasErrors())
-    return;
-#endif
-
   syncer::SyncService* service = GetSyncService();
   // The sync service may be nullptr if it has been just disabled by policy.
   if (!service || !service->IsEngineInitialized()) {
     return;
   }
 
-  configuring_sync_ = true;
-
-  // Setup args for the sync configure screen:
+  // Setup values for the JSON response:
   //   syncAllDataTypes: true if the user wants to sync everything
   //   <data_type>Registered: true if the associated data type is supported
   //   <data_type>Synced: true if the user wants to sync that specific data type
@@ -1067,6 +1062,17 @@
   FireWebUIListener("sync-settings-saved");
 }
 
+void PeopleHandler::MaybeMarkSyncConfiguring() {
+#if !defined(OS_CHROMEOS)
+  if (IsProfileAuthNeededOrHasErrors())
+    return;
+#endif
+  syncer::SyncService* service = GetSyncService();
+  // The sync service may be nullptr if it has been just disabled by policy.
+  if (service && service->IsEngineInitialized())
+    configuring_sync_ = true;
+}
+
 bool PeopleHandler::IsProfileAuthNeededOrHasErrors() {
   return !IdentityManagerFactory::GetForProfile(profile_)
               ->HasPrimaryAccount() ||
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index 50093e1..a27f0a1 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -152,7 +152,7 @@
   void OnDidClosePage(const base::ListValue* args);
   void HandleSetDatatypes(const base::ListValue* args);
   void HandleSetEncryption(const base::ListValue* args);
-  void HandleShowSetupUI(const base::ListValue* args);
+  void HandleShowSyncSetupUI(const base::ListValue* args);
   void HandleSyncPrefsDispatch(const base::ListValue* args);
 #if defined(OS_CHROMEOS)
   void HandleAttemptUserExit(const base::ListValue* args);
@@ -190,6 +190,9 @@
   // Suppresses any further signin promos, since the user has signed in once.
   void MarkFirstSetupComplete();
 
+  // If sync is indeed being configured, sets |configuring_sync_| to true.
+  void MaybeMarkSyncConfiguring();
+
   // True if profile needs authentication before sync can run.
   bool IsProfileAuthNeededOrHasErrors();
 
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index e35907d..7865dd3 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -413,7 +413,7 @@
   // engine will try to download control data types (e.g encryption info), but
   // that won't finish for this test as we're simulating cancelling while the
   // spinner is showing.
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   EXPECT_EQ(
       handler_.get(),
@@ -447,7 +447,7 @@
               SetSyncRequested(true));
   SetDefaultExpectationsForConfigPage();
 
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   // No data is sent yet, because the engine is not initialized.
   EXPECT_EQ(0U, web_ui_.call_data().size());
@@ -488,7 +488,7 @@
   EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
               SetSyncRequested(true));
   SetDefaultExpectationsForConfigPage();
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   // Sync engine becomes active, so |handler_| is notified.
   NotifySyncStateChanged();
@@ -530,7 +530,7 @@
                 Return(syncer::SyncService::TransportState::INITIALIZING));
       });
 
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   // Since the engine is not initialized yet, no data should be sent.
   EXPECT_EQ(0U, web_ui_.call_data().size());
@@ -563,7 +563,7 @@
                 Return(syncer::SyncService::TransportState::CONFIGURING));
       });
 
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
   // Since the engine was already running, we should *not* get a spinner - all
   // the necessary values are already available.
   ExpectSyncPrefsChanged();
@@ -614,7 +614,7 @@
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsFirstSetupComplete())
       .WillByDefault(Return(false));
   // Open the web UI.
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   ASSERT_FALSE(handler_->is_configuring_sync());
 }
@@ -627,7 +627,7 @@
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsFirstSetupComplete())
       .WillByDefault(Return(false));
   // Open the web UI.
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   ASSERT_FALSE(handler_->is_configuring_sync());
 }
@@ -888,7 +888,7 @@
   SetupInitializedSyncService();
   // This should display the sync setup dialog (not login).
   SetDefaultExpectationsForConfigPage();
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   ExpectSyncPrefsChanged();
 }
@@ -904,7 +904,7 @@
   SetupInitializedSyncService();
   SetDefaultExpectationsForConfigPage();
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "syncAllDataTypes", true);
@@ -936,7 +936,7 @@
   ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncEverythingEnabled())
       .WillByDefault(Return(false));
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, GetAllTypes());
@@ -961,7 +961,7 @@
         .WillByDefault(Return(types));
 
     // This should display the sync setup dialog (not login).
-    handler_->HandleShowSetupUI(nullptr);
+    handler_->HandleShowSyncSetupUI(nullptr);
 
     // Close the config overlay.
     LoginUIServiceFactory::GetForProfile(profile())->LoginUIClosed(
@@ -986,7 +986,7 @@
   SetDefaultExpectationsForConfigPage();
 
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "passphraseRequired", true);
@@ -1004,7 +1004,7 @@
   SetDefaultExpectationsForConfigPage();
 
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "passphraseRequired", true);
@@ -1023,7 +1023,7 @@
   SetDefaultExpectationsForConfigPage();
 
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "passphraseRequired", false);
@@ -1046,7 +1046,7 @@
       .WillByDefault(Return(true));
 
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "encryptAllData", true);
@@ -1067,7 +1067,7 @@
       .WillByDefault(Return(false));
 
   // This should display the sync setup dialog (not login).
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
   CheckBool(dictionary, "encryptAllData", false);
@@ -1113,7 +1113,7 @@
   // Sync starts out fully enabled.
   SetDefaultExpectationsForConfigPage();
 
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   // Now sync gets reset from the dashboard (the user clicked the "Manage synced
   // data" link), which results in the sync-requested and first-setup-complete
@@ -1168,7 +1168,7 @@
   // Sync starts out fully enabled.
   SetDefaultExpectationsForConfigPage();
 
-  handler_->HandleShowSetupUI(nullptr);
+  handler_->HandleShowSyncSetupUI(nullptr);
 
   // Now sync gets reset from the dashboard (the user clicked the "Manage synced
   // data" link), which results in the sync-requested and first-setup-complete
diff --git a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
index 5811fdf..23443f9 100644
--- a/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -30,7 +30,6 @@
 #include "components/browsing_data/core/browsing_data_utils.h"
 #include "components/browsing_data/core/history_notice_utils.h"
 #include "components/browsing_data/core/pref_names.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
@@ -40,11 +39,6 @@
 #include "content/public/browser/web_ui.h"
 #include "ui/base/text/bytes_formatting.h"
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h"
-#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h"
-#endif
-
 using BrowsingDataType = browsing_data::BrowsingDataType;
 
 namespace {
@@ -376,12 +370,6 @@
   browsing_data_important_sites_util::Remove(
       remove_mask, origin_mask, time_period, std::move(filter_builder), remover,
       std::move(callback));
-
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-  feature_engagement::IncognitoWindowTrackerFactory::GetInstance()
-      ->GetForProfile(profile_)
-      ->OnBrowsingDataCleared();
-#endif
 }
 
 void ClearBrowsingDataHandler::OnClearingTaskFinished(
diff --git a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
index 0ef8df9..32e47f1 100644
--- a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
+++ b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
@@ -140,7 +140,7 @@
   }
 
   if (modification_is_removal) {
-    host_zoom_dictionary_weak->RemoveWithoutPathExpansion(change.host, nullptr);
+    host_zoom_dictionary_weak->RemoveKey(change.host);
   } else {
     base::DictionaryValue dict;
     dict.SetDouble(kZoomLevelPath, level);
@@ -212,7 +212,7 @@
     host_zoom_dictionaries->GetDictionary(partition_key_,
                                           &host_zoom_dictionary);
     for (const std::string& s : keys_to_remove)
-      host_zoom_dictionary->RemoveWithoutPathExpansion(s, nullptr);
+      host_zoom_dictionary->RemoveKey(s);
   }
 }
 
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index bac2b98..b0695e7 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1595224759-e1768d31ec00790ba2a6883a1e8e7f5013b1d5d0.profdata
+chrome-mac-master-1595246042-98b0c1a3c65c5614c7a736e7fb083fada959e352.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 604f32d..5d975e5 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -126,8 +126,6 @@
     "pref_names_util.h",
     "prerender_url_loader_throttle.cc",
     "prerender_url_loader_throttle.h",
-    "prerender_util.cc",
-    "prerender_util.h",
     "ref_counted_util.h",
     "render_messages.h",
     "search/instant_mojom_traits.h",
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 59481ee..214f072d 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -96,10 +96,6 @@
 // shutdown. Used to determine the exit type the last time the profile was open.
 const char kSessionExitType[] = "profile.exit_type";
 
-// Stores the total amount of observed active session time for the user while
-// in-product help is active. Observed time is active session time in seconds.
-const char kObservedSessionTime[] = "profile.observed_session_time";
-
 // The last time that the site engagement service recorded an engagement event
 // for this profile for any URL. Recorded only during shutdown. Used to prevent
 // the service from decaying engagement when a user does not use Chrome at all
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 6599387..d3d4daf 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -41,7 +41,6 @@
 extern const char kRestoreOnStartup[];
 extern const char kSessionExitedCleanly[];
 extern const char kSessionExitType[];
-extern const char kObservedSessionTime[];
 extern const char kSiteEngagementLastUpdateTime[];
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kSupervisedUserApprovedExtensions[];
diff --git a/chrome/common/prerender_url_loader_throttle.cc b/chrome/common/prerender_url_loader_throttle.cc
index ab30fba..c3ecc81 100644
--- a/chrome/common/prerender_url_loader_throttle.cc
+++ b/chrome/common/prerender_url_loader_throttle.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "build/build_config.h"
-#include "chrome/common/prerender_util.h"
+#include "components/prerender/common/prerender_util.h"
 #include "content/public/common/content_constants.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
index 0d6f0d3..c16fecc 100644
--- a/chrome/installer/util/shell_util.cc
+++ b/chrome/installer/util/shell_util.cc
@@ -1482,6 +1482,15 @@
   return true;
 }
 
+// Return a shortened version of |component|. Cut in the middle to try
+// to avoid losing the unique parts of |component| (which are usually
+// at the beginning or end for things like usernames and paths).
+base::string16 ShortenAppModelIdComponent(const base::string16& component,
+                                          int desired_length) {
+  return component.substr(0, desired_length / 2) +
+         component.substr(component.length() - ((desired_length + 1) / 2));
+}
+
 }  // namespace
 
 const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
@@ -1856,11 +1865,10 @@
   } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) {
     NOTREACHED();
   }
-  // There is only one component (i.e. the suffixed appid) in this case, but it
-  // is still necessary to go through the appid constructor to make sure the
-  // returned appid is truncated if necessary.
-  std::vector<base::string16> components(1, app_id.append(suffix));
-  return BuildAppModelId(components);
+  app_id.append(suffix);
+  if (app_id.length() <= installer::kMaxAppModelIdLength)
+    return app_id;
+  return ShortenAppModelIdComponent(app_id, installer::kMaxAppModelIdLength);
 }
 
 base::string16 ShellUtil::BuildAppModelId(
@@ -1890,13 +1898,8 @@
     const base::string16& component = *it;
     DCHECK(!component.empty());
     if (component.length() > max_component_length) {
-      // Append a shortened version of this component. Cut in the middle to try
-      // to avoid losing the unique parts of this component (which are usually
-      // at the beginning or end for things like usernames and paths).
-      app_id.append(component, 0, max_component_length / 2);
-      app_id.append(component,
-                    component.length() - ((max_component_length + 1) / 2),
-                    base::string16::npos);
+      app_id.append(
+          ShortenAppModelIdComponent(component, max_component_length));
     } else {
       app_id.append(component);
     }
diff --git a/chrome/services/sharing/nearby/decoder/nearby_decoder.cc b/chrome/services/sharing/nearby/decoder/nearby_decoder.cc
index a96e967..ece880d 100644
--- a/chrome/services/sharing/nearby/decoder/nearby_decoder.cc
+++ b/chrome/services/sharing/nearby/decoder/nearby_decoder.cc
@@ -44,7 +44,8 @@
        proto_frame.file_metadata()) {
     mojo_file_metadatas.push_back(mojom::FileMetadata::New(
         metadata.name(), ConvertFileMetadataType(metadata.type()),
-        metadata.payload_id(), metadata.size(), metadata.mime_type()));
+        metadata.payload_id(), metadata.size(), metadata.mime_type(),
+        metadata.id()));
   }
 
   return mojo_file_metadatas;
@@ -75,7 +76,7 @@
        proto_frame.text_metadata()) {
     mojo_text_metadatas.push_back(mojom::TextMetadata::New(
         metadata.text_title(), ConvertTextMetadataType(metadata.type()),
-        metadata.payload_id(), metadata.size()));
+        metadata.payload_id(), metadata.size(), metadata.id()));
   }
 
   return mojo_text_metadatas;
@@ -104,7 +105,7 @@
        proto_frame.wifi_credentials_metadata()) {
     mojo_wifi_metadatas.push_back(mojom::WifiCredentialsMetadata::New(
         metadata.ssid(), ConvertSecurityType(metadata.security_type()),
-        metadata.payload_id()));
+        metadata.payload_id(), metadata.id()));
   }
 
   return mojo_wifi_metadatas;
@@ -304,6 +305,9 @@
       mojo_v1frame->set_certificate_info(
           GetCertificateInfoFrame(proto_frame.v1().certificate_info()));
       break;
+    case sharing::nearby::V1Frame_FrameType_CANCEL:
+      mojo_v1frame->set_cancel_frame(mojom::CancelFrame::New());
+      break;
     default:
       LOG(ERROR) << "Unknown type of v1frame, unable to process.";
       std::move(callback).Run(nullptr);
diff --git a/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc b/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc
index d316e37b..643455c 100644
--- a/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc
+++ b/chrome/services/sharing/nearby/decoder/nearby_decoder_unittest.cc
@@ -109,6 +109,7 @@
     EXPECT_EQ(base::checked_cast<uint64_t>(file_metadata[i].size()),
               file->size);
     EXPECT_EQ(file_metadata[i].mime_type(), file->mime_type);
+    EXPECT_EQ(file_metadata[i].id(), file->id);
   }
 
   // Verify contents of TextMetadata vector.
@@ -121,6 +122,7 @@
     EXPECT_EQ(text_metadata[i].payload_id(), text->payload_id);
     EXPECT_EQ(base::checked_cast<uint64_t>(text_metadata[i].size()),
               text->size);
+    EXPECT_EQ(text_metadata[i].id(), text->id);
   }
 
   EXPECT_EQ(required_package, intro->required_package);
@@ -135,6 +137,7 @@
     EXPECT_EQ(wifi_credentials_metadata[i].security_type(),
               ConvertWifiCredentialsMetadataType(wifi->security_type));
     EXPECT_EQ(wifi_credentials_metadata[i].payload_id(), wifi->payload_id);
+    EXPECT_EQ(wifi_credentials_metadata[i].id(), wifi->id);
   }
 }
 
@@ -171,6 +174,12 @@
             frame->get_v1()->get_paired_key_encryption()->secret_id_hash);
 }
 
+void ExpectFrameContainsCancelFrame(const mojom::FramePtr& frame) {
+  ASSERT_TRUE(frame);
+  ASSERT_TRUE(frame->is_v1());
+  EXPECT_TRUE(frame->get_v1()->is_cancel_frame());
+}
+
 std::unique_ptr<sharing::nearby::Frame> BuildPairedKeyResultFrame(
     sharing::nearby::PairedKeyResultFrame_Status status) {
   std::unique_ptr<sharing::nearby::Frame> frame =
@@ -328,6 +337,7 @@
     file->set_payload_id(i);
     file->set_size(i);
     file->set_mime_type("mime " + base::NumberToString(i));
+    file->set_id(i);
     files.push_back(*file);
   }
 
@@ -342,6 +352,7 @@
     text->set_type(static_cast<sharing::nearby::TextMetadata_Type>(i));
     text->set_payload_id(i);
     text->set_size(i);
+    text->set_id(i);
     texts.push_back(*text);
   }
 
@@ -362,6 +373,7 @@
     wifi->set_security_type(
         static_cast<sharing::nearby::WifiCredentialsMetadata_SecurityType>(i));
     wifi->set_payload_id(i);
+    wifi->set_id(i);
     wifis.push_back(*wifi);
   }
 
@@ -524,6 +536,24 @@
   ExpectNullFrame(frame);
 }
 
+TEST_F(NearbySharingDecoderTest, CancelFrameSuccessDecoding) {
+  sharing::nearby::Frame frame = sharing::nearby::Frame();
+  sharing::nearby::V1Frame* v1frame = frame.mutable_v1();
+  v1frame->set_type(sharing::nearby::V1Frame_FrameType_CANCEL);
+
+  std::vector<uint8_t> data;
+  data.resize(frame.ByteSize());
+  ASSERT_TRUE(frame.SerializeToArray(&data[0], frame.ByteSize()));
+
+  base::RunLoop run_loop;
+  auto callback = base::BindLambdaForTesting([&](const mojom::FramePtr answer) {
+    ExpectFrameContainsCancelFrame(answer);
+    run_loop.Quit();
+  });
+  decoder()->DecodeFrame(std::move(data), std::move(callback));
+  run_loop.Run();
+}
+
 TEST_F(NearbySharingDecoderTest, PairedKeyResultFrameSuccessDecoding) {
   std::unique_ptr<sharing::nearby::Frame> frame = BuildPairedKeyResultFrame(
       sharing::nearby::PairedKeyResultFrame_Status_SUCCESS);
diff --git a/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom b/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom
index 7c59fb1..12bd12b 100644
--- a/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom
+++ b/chrome/services/sharing/public/mojom/nearby_decoder_types.mojom
@@ -35,6 +35,8 @@
   PairedKeyResultFrame paired_key_result;
 
   CertificateInfoFrame certificate_info;
+
+  CancelFrame cancel_frame;
 };
 
 // An introduction packet sent by the sending side. Contains a list of files
@@ -81,6 +83,9 @@
   // The mimeType of file (eg. 'image/jpeg' from 'dog.jpg'). Specifying a
   // mimeType helps provide a richer experience on receiving side.
   string mime_type = "application/octet-stream";
+
+  // A uuid for the attachment. Should be unique across all attachments.
+  int64 id;
 };
 
 // Text metadata. Does not include the actual bytes of the text in transit.
@@ -108,6 +113,9 @@
 
   // The size of the text content.
   uint64 size;
+
+  // A uuid for the attachment. Should be unique across all attachments.
+  int64 id;
 };
 
 // Wifi credentails metadata. Describes the wifi network to be used by the
@@ -129,6 +137,9 @@
   // The BYTE payload id that will be sent as a follow up containing the
   // password.
   int64 payload_id;
+
+  // A uuid for the attachment. Should be unique across all attachments.
+  int64 id;
 };
 
 // A response packet sent by the receiving side. Accepts or rejects the list of
@@ -208,3 +219,7 @@
   // The size should be 32.
   array<uint8> metadata_encryption_key_tag;
 };
+
+// Frame sent by remote device to cancel the share action.
+struct CancelFrame {
+};
diff --git a/chrome/services/sharing/public/proto/wire_format.proto b/chrome/services/sharing/public/proto/wire_format.proto
index eb7cfd0..29d88f0 100644
--- a/chrome/services/sharing/public/proto/wire_format.proto
+++ b/chrome/services/sharing/public/proto/wire_format.proto
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // Brought from: //depot/google3/location/nearby/sharing/proto/wire_format.proto
-// At CL 311037085
+// At CL 317565061
 
 syntax = "proto2";
 
@@ -40,6 +40,9 @@
   // The mimeType of file (eg. 'image/jpeg' from 'dog.jpg'). Specifying a
   // mimeType helps provide a richer experience on receiving side.
   optional string mime_type = 5 [default = "application/octet-stream"];
+
+  // A uuid for the attachment. Should be unique across all attachments.
+  optional int64 id = 6;
 }
 
 // NEXT_ID=5
@@ -67,6 +70,9 @@
 
   // The size of the text content.
   optional int64 size = 5;
+
+  // A uuid for the attachment. Should be unique across all attachments.
+  optional int64 id = 6;
 }
 
 // NEXT_ID=5
@@ -87,6 +93,9 @@
   // The BYTE payload id that will be sent as a follow up containing the
   // password.
   optional int64 payload_id = 4;
+
+  // A uuid for the attachment. Should be unique across all attachments.
+  optional int64 id = 5;
 }
 
 // A frame used when sending messages over the wire.
@@ -112,6 +121,7 @@
     PAIRED_KEY_ENCRYPTION = 3;
     PAIRED_KEY_RESULT = 4;
     CERTIFICATE_INFO = 5;
+    CANCEL = 6;
   }
 
   optional FrameType type = 1;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 88f9f719..95382d7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -20,7 +20,6 @@
 import("//chrome/test/include_js_tests.gni")
 import("//chromeos/assistant/assistant.gni")
 import("//components/captive_portal/core/features.gni")
-import("//components/feature_engagement/features.gni")
 import("//components/feed/features.gni")
 import("//components/gwp_asan/buildflags/buildflags.gni")
 import("//components/nacl/features.gni")
@@ -2595,12 +2594,6 @@
     if (enable_kaleidoscope) {
       deps += [ "../browser/media/kaleidoscope/internal:browser_tests" ]
     }
-    if (enable_legacy_desktop_in_product_help) {
-      sources += [
-        "../browser/feature_engagement/incognito_window/incognito_window_tracker_browsertest.cc",
-        "../browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc",
-      ]
-    }
     if (safe_browsing_mode == 1) {
       sources += [
         "../browser/safe_browsing/ad_redirect_trigger_browsertest.cc",
@@ -3635,6 +3628,8 @@
       "../browser/nearby_sharing/nearby_process_manager_unittest.cc",
       "../browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc",
       "../browser/nearby_sharing/webrtc_signaling_messenger_unittest.cc",
+      "../browser/performance_manager/test_support/page_discarding_utils.cc",
+      "../browser/performance_manager/test_support/page_discarding_utils.h",
       "../browser/profiles/profile_avatar_icon_util_unittest.cc",
       "../browser/profiles/profile_destroyer_unittest.cc",
       "../browser/safe_browsing/generated_safe_browsing_pref_unittest.cc",
@@ -4116,6 +4111,7 @@
       "../browser/performance_manager/persistence/site_data/unittest_utils.h",
 
       # Urgent discarding from performance_manager isn't supported on Android.
+      "../browser/performance_manager/policies/page_discarding_helper_unittest.cc",
       "../browser/performance_manager/policies/urgent_page_discarding_policy_unittest.cc",
 
       # Background tab loading from performance_manager isn't supported on Android.
@@ -4479,16 +4475,6 @@
     }
   }
 
-  if (enable_legacy_desktop_in_product_help) {
-    sources += [
-      "../browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc",
-      "../browser/feature_engagement/feature_tracker_unittest.cc",
-      "../browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc",
-      "../browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc",
-      "../browser/feature_engagement/session_duration_updater_unittest.cc",
-    ]
-  }
-
   if (is_chromeos) {
     sources -= [
       "../browser/notifications/notification_ui_manager_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
index f9a58e49..10e9e9c 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/AccountManagerTestRule.java
@@ -95,9 +95,9 @@
      * ProfileDataSource of the fake AccountManagerFacade.
      * @return The account added.
      */
-    public Account addAccount(String accountName, ProfileDataSource.ProfileData profileData) {
-        Account account = addAccount(accountName);
-        mFakeAccountManagerFacade.setProfileData(accountName, profileData);
+    public Account addAccount(ProfileDataSource.ProfileData profileData) {
+        Account account = addAccount(profileData.getAccountName());
+        mFakeAccountManagerFacade.setProfileData(profileData.getAccountName(), profileData);
         return account;
     }
 
diff --git a/chrome/test/data/prerender/prefetch_page.html b/chrome/test/data/prerender/prefetch_page.html
index 311f9f1..8da2382 100644
--- a/chrome/test/data/prerender/prefetch_page.html
+++ b/chrome/test/data/prerender/prefetch_page.html
@@ -14,6 +14,7 @@
 to execute.
 -->
 <body>
+  <title>Prefetch Page</title>
   <script src="prefetch.js"></script>
   <script>
     var s = document.createElement("script");
diff --git a/chrome/test/data/prerender/prerender_embedded_content.html b/chrome/test/data/prerender/prerender_embedded_content.html
deleted file mode 100644
index 914f5f6..0000000
--- a/chrome/test/data/prerender/prerender_embedded_content.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<html>
-<head>
-  <title>Embedded iframe</title>
-</head>
-<body>
-  Embedded iframe.
-</body>
-</html>
diff --git a/chrome/test/data/prerender/prerender_http_auth_container.html b/chrome/test/data/prerender/prerender_http_auth_container.html
index c7e7780b..d58a5cf 100644
--- a/chrome/test/data/prerender/prerender_http_auth_container.html
+++ b/chrome/test/data/prerender/prerender_http_auth_container.html
@@ -1,9 +1,6 @@
 <html>
   <head></head>
   <body>
-    <iframe src="/auth-basic/index.html?password=boggins&amp;realm=bog"
-      width="80%" height="600">
-      iframe not supported.
-    </iframe>
+    <img src="/auth-basic/index.html?password=boggins&amp;realm=bog">
   </body>
 </html>
diff --git a/chrome/test/data/prerender/prerender_referrer.html b/chrome/test/data/prerender/prerender_referrer.html
deleted file mode 100644
index bd05a6f..0000000
--- a/chrome/test/data/prerender/prerender_referrer.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<html>
-<!-- 
-This test makes sure that referrer is properly set.
--->
-<head>
-<title>Prerender Referrer Test</title>
-<script>
-// Check referrer.
-function DidPrerenderPass() {
-  return document.referrer.indexOf("prerender_loader") >= 0;
-}
-
-// Check referrer.
-function DidDisplayPass() {
-  return document.referrer.indexOf("prerender_loader") >= 0;
-}
-</script>
-</head>
-<body></body>
-</html>
diff --git a/chrome/test/data/prerender/prerender_with_iframe.html b/chrome/test/data/prerender/prerender_with_iframe.html
deleted file mode 100644
index e265118..0000000
--- a/chrome/test/data/prerender/prerender_with_iframe.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<html>
-<head>
-  <title>Prerender with iframe</title>
-  <script>
-    var pageWasPrerendered = false;
-
-    function DidPrerenderPass() {
-      pageWasPrerendered = true;
-      return true;
-    }
-
-    // Make sure DidPrerenderPass() was called first.  Otherwise, the page was
-    // most likely reloaded instead of using the prerendered page.
-    function DidDisplayPass() {
-      return pageWasPrerendered;
-    }
-  </script>
-</head>
-<body>
-  <iframe name="iframe" 
-          src="REPLACE_WITH_URL" 
-          width="0" 
-          height="0"
-          frameborder="0">
-  </iframe>
-</body>
-</html>
diff --git a/chrome/test/data/prerender/prerender_with_image.html b/chrome/test/data/prerender/prerender_with_image.html
deleted file mode 100644
index 70c6c5fd..0000000
--- a/chrome/test/data/prerender/prerender_with_image.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<html>
-<!--
-This test checks to make sure that a prerendered page is loaded.
--->
-<head>
-<title>Prerendered Page</title>
-
-<script>
-var pageWasPrerendered = false;
-
-// Make sure plugin was not loaded while prerendering.
-function DidPrerenderPass() {
-  pageWasPrerendered = true;
-  return true;
-}
-
-// Make sure DidPrerenderPass() was called first.  Otherwise, the page was
-// most likely reloaded instead of using the prerendered page.
-function DidDisplayPass() {
-  return pageWasPrerendered;
-}
-</script>
-
-</head>
-<body>
-
-<img src="REPLACE_WITH_IMAGE_URL"/>
-</body>
-</html>
diff --git a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
index 47db94f..2d15c1a 100644
--- a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
@@ -2,23 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// clang-format off
 // #import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
 // #import {LifetimeBrowserProxy, LifetimeBrowserProxyImpl, OsResetBrowserProxyImpl} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {TestOsResetBrowserProxy} from './test_os_reset_browser_proxy.m.js';
 // #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
-// clang-format on
 
 cr.define('settings_reset_page', function() {
   /** @enum {string} */
   const TestNames = {
     PowerwashDialogAction: 'PowerwashDialogAction',
     PowerwashDialogOpenClose: 'PowerwashDialogOpenClose',
-    PowerwashFocusDeepLink: 'PowerwashFocusDeepLink',
-    PowerwashFocusDeepLinkNoFlag: 'PowerwashFocusDeepLinkNoFlag',
-    PowerwashFocusDeepLinkWrongId: 'PowerwashFocusDeepLinkWrongId',
   };
 
   suite('DialogTests', function() {
@@ -44,7 +38,6 @@
     });
 
     teardown(function() {
-      settings.Router.getInstance().resetRouteForTesting();
       resetPage.remove();
     });
 
@@ -76,23 +69,6 @@
       ]);
     }
 
-    /**
-     * Navigates to the deep link provided by |settingId| and returns true if
-     * the focused element is |deepLinkElement|.
-     * @param {!Element} deepLinkElement
-     * @param {!string} settingId
-     * @returns {!boolean}
-     */
-    async function isDeepLinkFocusedForSettingId(deepLinkElement, settingId) {
-      const params = new URLSearchParams;
-      params.append('settingId', settingId);
-      settings.Router.getInstance().navigateTo(
-          settings.routes.OS_RESET, params);
-
-      await test_util.waitAfterNextRender(deepLinkElement);
-      return deepLinkElement === getDeepActiveElement();
-    }
-
     // Tests that the powerwash dialog opens and closes correctly, and
     // that chrome.send calls are propagated as expected.
     test(TestNames.PowerwashDialogOpenClose, function() {
@@ -115,36 +91,6 @@
           await lifetimeBrowserProxy.whenCalled('factoryReset');
       assertFalse(requestTpmFirmwareUpdate);
     });
-
-    // Tests that when the route changes to one containing a deep link to
-    // powerwash, powerwash is focused.
-    test(TestNames.PowerwashFocusDeepLink, async () => {
-      loadTimeData.overrideValues({isDeepLinkingEnabled: true});
-      assertTrue(loadTimeData.getBoolean('isDeepLinkingEnabled'));
-      assertTrue(
-          await isDeepLinkFocusedForSettingId(resetPage.$.powerwash, '1600'),
-          'Powerwash should be focused for settingId=1600.');
-    });
-
-    // Tests that when the deep linking flag is disabled, no focusing of deep
-    // links occurs.
-    test(TestNames.PowerwashFocusDeepLinkNoFlag, async () => {
-      loadTimeData.overrideValues({isDeepLinkingEnabled: false});
-      assertFalse(loadTimeData.getBoolean('isDeepLinkingEnabled'));
-      assertFalse(
-          await isDeepLinkFocusedForSettingId(resetPage.$.powerwash, '1600'),
-          'Powerwash should not be focused with flag disabled.');
-    });
-
-    // Tests that when the route changes to one containing a deep link not equal
-    // to powerwash, no focusing of powerwash occurs.
-    test(TestNames.PowerwashFocusDeepLinkWrongId, async () => {
-      loadTimeData.overrideValues({isDeepLinkingEnabled: true});
-      assertTrue(loadTimeData.getBoolean('isDeepLinkingEnabled'));
-      assertFalse(
-          await isDeepLinkFocusedForSettingId(resetPage.$.powerwash, '1234'),
-          'Powerwash should not be focused for settingId=1234.');
-    });
   });
 
   // #cr_define_end
diff --git a/chrome/test/permissions/permission_request_manager_test_api.cc b/chrome/test/permissions/permission_request_manager_test_api.cc
index 82720b3b..a0c0a0c 100644
--- a/chrome/test/permissions/permission_request_manager_test_api.cc
+++ b/chrome/test/permissions/permission_request_manager_test_api.cc
@@ -55,10 +55,11 @@
               browser->tab_strip_model()->GetActiveWebContents())) {}
 
 void PermissionRequestManagerTestApi::AddSimpleRequest(
+    content::RenderFrameHost* source_frame,
     ContentSettingsType type) {
   TestPermissionRequestOwner* request_owner =
       new TestPermissionRequestOwner(type);
-  manager_->AddRequest(request_owner->request());
+  manager_->AddRequest(source_frame, request_owner->request());
 }
 
 views::Widget* PermissionRequestManagerTestApi::GetPromptWindow() {
diff --git a/chrome/test/permissions/permission_request_manager_test_api.h b/chrome/test/permissions/permission_request_manager_test_api.h
index d743f78..5c7ae922 100644
--- a/chrome/test/permissions/permission_request_manager_test_api.h
+++ b/chrome/test/permissions/permission_request_manager_test_api.h
@@ -11,6 +11,10 @@
 
 class Browser;
 
+namespace content {
+class RenderFrameHost;
+}
+
 namespace views {
 class Widget;
 }  // namespace views
@@ -27,10 +31,11 @@
 
   permissions::PermissionRequestManager* manager() { return manager_; }
 
-  // Add a "simple" permission request. One that uses PermissionRequestImpl,
-  // such as for ContentSettingsType including MIDI_SYSEX, PUSH_MESSAGING,
-  // NOTIFICATIONS, GEOLOCATON, or PLUGINS.
-  void AddSimpleRequest(ContentSettingsType type);
+  // Add a "simple" permission request originating from the given frame. One
+  // that uses PermissionRequestImpl, such as for ContentSettingsType including
+  // MIDI_SYSEX, PUSH_MESSAGING, NOTIFICATIONS, GEOLOCATON, or PLUGINS.
+  void AddSimpleRequest(content::RenderFrameHost* source_frame,
+                        ContentSettingsType type);
 
   // Return the Widget for the permission prompt bubble, or nullptr if
   // there is no prompt currently showing.
diff --git a/chrome/updater/app/app_server.cc b/chrome/updater/app/app_server.cc
index 4015a370..0a29d49 100644
--- a/chrome/updater/app/app_server.cc
+++ b/chrome/updater/app/app_server.cc
@@ -25,25 +25,23 @@
 }
 
 base::OnceClosure AppServer::ModeCheck() {
-  std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-  if (!local_prefs->GetPrefService()->GetBoolean(kPrefQualified))
+  std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+  if (!local_prefs->GetQualified())
     return base::BindOnce(&AppServer::Qualify, this, std::move(local_prefs));
 
-  std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
+  std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
   if (!global_prefs) {
     return base::BindOnce(&AppServer::Shutdown, this,
                           kErrorFailedToLockPrefsMutex);
   }
 
   base::Version this_version(UPDATER_VERSION_STRING);
-  base::Version active_version(
-      global_prefs->GetPrefService()->GetString(kPrefActiveVersion));
+  base::Version active_version(global_prefs->GetActiveVersion());
   if (this_version < active_version)
     return base::BindOnce(&AppServer::UninstallSelf, this);
 
-  if (this_version > active_version ||
-      global_prefs->GetPrefService()->GetBoolean(kPrefSwapping)) {
-    if (!SwapVersions(global_prefs->GetPrefService()))
+  if (this_version > active_version || global_prefs->GetSwapping()) {
+    if (!SwapVersions(global_prefs.get()))
       return base::BindOnce(&AppServer::Shutdown, this, kErrorFailedToSwap);
   }
 
@@ -60,22 +58,22 @@
   std::move(first_task_).Run();
 }
 
-void AppServer::Qualify(std::unique_ptr<UpdaterPrefs> local_prefs) {
+void AppServer::Qualify(std::unique_ptr<LocalPrefs> local_prefs) {
   // For now, assume qualification succeeds.
-  local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+  local_prefs->SetQualified(true);
   PrefsCommitPendingWrites(local_prefs->GetPrefService());
   Shutdown(kErrorOk);
 }
 
-bool AppServer::SwapVersions(PrefService* global_prefs) {
-  global_prefs->SetBoolean(kPrefSwapping, true);
-  PrefsCommitPendingWrites(global_prefs);
+bool AppServer::SwapVersions(GlobalPrefs* global_prefs) {
+  global_prefs->SetSwapping(true);
+  PrefsCommitPendingWrites(global_prefs->GetPrefService());
   bool result = SwapRPCInterfaces();
   if (!result)
     return false;
-  global_prefs->SetString(kPrefActiveVersion, UPDATER_VERSION_STRING);
-  global_prefs->SetBoolean(kPrefSwapping, false);
-  PrefsCommitPendingWrites(global_prefs);
+  global_prefs->SetActiveVersion(UPDATER_VERSION_STRING);
+  global_prefs->SetSwapping(false);
+  PrefsCommitPendingWrites(global_prefs->GetPrefService());
   return true;
 }
 
diff --git a/chrome/updater/app/app_server.h b/chrome/updater/app/app_server.h
index 2dea11f..d8d7e98 100644
--- a/chrome/updater/app/app_server.h
+++ b/chrome/updater/app/app_server.h
@@ -11,12 +11,11 @@
 #include "base/memory/scoped_refptr.h"
 #include "chrome/updater/app/app.h"
 
-class PrefService;
-
 namespace updater {
 
 class Configurator;
-class UpdaterPrefs;
+class GlobalPrefs;
+class LocalPrefs;
 
 // AppServer runs as the updater server process. Multiple servers of different
 // application versions can be run side-by-side. Each such server is called a
@@ -60,8 +59,8 @@
   // the system is consistent with an incomplete swap, ModeCheck may have the
   // side effect of promoting this candidate to the active candidate.
   base::OnceClosure ModeCheck();
-  void Qualify(std::unique_ptr<UpdaterPrefs> local_prefs);
-  bool SwapVersions(PrefService* global_prefs);
+  void Qualify(std::unique_ptr<LocalPrefs> local_prefs);
+  bool SwapVersions(GlobalPrefs* global_prefs);
 
   base::OnceClosure first_task_;
 };
diff --git a/chrome/updater/app/app_server_unittest.cc b/chrome/updater/app/app_server_unittest.cc
index c270116..10e5280 100644
--- a/chrome/updater/app/app_server_unittest.cc
+++ b/chrome/updater/app/app_server_unittest.cc
@@ -83,18 +83,18 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).Times(0);
   EXPECT_CALL(*app, UninstallSelf).Times(0);
   EXPECT_EQ(app->Run(), 0);
-  EXPECT_TRUE(CreateLocalPrefs()->GetPrefService()->GetBoolean(kPrefQualified));
+  EXPECT_TRUE(CreateLocalPrefs()->GetQualified());
 }
 
 TEST_F(AppServerTestCase, SelfUninstall) {
   {
     base::SingleThreadTaskExecutor main_task_executor(
         base::MessagePumpType::UI);
-    std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-    global_prefs->GetPrefService()->SetString(kPrefActiveVersion, "9999999");
+    std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+    global_prefs->SetActiveVersion("9999999");
     PrefsCommitPendingWrites(global_prefs->GetPrefService());
-    std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-    local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+    std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+    local_prefs->SetQualified(true);
     PrefsCommitPendingWrites(local_prefs->GetPrefService());
   }
   auto app = base::MakeRefCounted<AppServerTest>();
@@ -104,15 +104,15 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).Times(0);
   EXPECT_CALL(*app, UninstallSelf).Times(1);
   EXPECT_EQ(app->Run(), 0);
-  EXPECT_TRUE(CreateLocalPrefs()->GetPrefService()->GetBoolean(kPrefQualified));
+  EXPECT_TRUE(CreateLocalPrefs()->GetQualified());
 }
 
 TEST_F(AppServerTestCase, SelfPromote) {
   {
     base::SingleThreadTaskExecutor main_task_executor(
         base::MessagePumpType::UI);
-    std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-    local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+    std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+    local_prefs->SetQualified(true);
     PrefsCommitPendingWrites(local_prefs->GetPrefService());
   }
   auto app = base::MakeRefCounted<AppServerTest>();
@@ -122,18 +122,17 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).WillOnce(Return(true));
   EXPECT_CALL(*app, UninstallSelf).Times(0);
   EXPECT_EQ(app->Run(), 0);
-  std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-  EXPECT_FALSE(global_prefs->GetPrefService()->GetBoolean(kPrefSwapping));
-  EXPECT_EQ(global_prefs->GetPrefService()->GetString(kPrefActiveVersion),
-            UPDATER_VERSION_STRING);
+  std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+  EXPECT_FALSE(global_prefs->GetSwapping());
+  EXPECT_EQ(global_prefs->GetActiveVersion(), UPDATER_VERSION_STRING);
 }
 
 TEST_F(AppServerTestCase, SelfPromoteFails) {
   {
     base::SingleThreadTaskExecutor main_task_executor(
         base::MessagePumpType::UI);
-    std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-    local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+    std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+    local_prefs->SetQualified(true);
     PrefsCommitPendingWrites(local_prefs->GetPrefService());
   }
   auto app = base::MakeRefCounted<AppServerTest>();
@@ -143,21 +142,20 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).WillOnce(Return(false));
   EXPECT_CALL(*app, UninstallSelf).Times(0);
   EXPECT_EQ(app->Run(), 2);
-  std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-  EXPECT_TRUE(global_prefs->GetPrefService()->GetBoolean(kPrefSwapping));
-  EXPECT_EQ(global_prefs->GetPrefService()->GetString(kPrefActiveVersion), "0");
+  std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+  EXPECT_TRUE(global_prefs->GetSwapping());
+  EXPECT_EQ(global_prefs->GetActiveVersion(), "0");
 }
 
 TEST_F(AppServerTestCase, ActiveDutyAlready) {
   {
     base::SingleThreadTaskExecutor main_task_executor(
         base::MessagePumpType::UI);
-    std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-    global_prefs->GetPrefService()->SetString(kPrefActiveVersion,
-                                              UPDATER_VERSION_STRING);
+    std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+    global_prefs->SetActiveVersion(UPDATER_VERSION_STRING);
     PrefsCommitPendingWrites(global_prefs->GetPrefService());
-    std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-    local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+    std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+    local_prefs->SetQualified(true);
     PrefsCommitPendingWrites(local_prefs->GetPrefService());
   }
   auto app = base::MakeRefCounted<AppServerTest>();
@@ -167,23 +165,21 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).Times(0);
   EXPECT_CALL(*app, UninstallSelf).Times(0);
   EXPECT_EQ(app->Run(), 0);
-  std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-  EXPECT_FALSE(global_prefs->GetPrefService()->GetBoolean(kPrefSwapping));
-  EXPECT_EQ(global_prefs->GetPrefService()->GetString(kPrefActiveVersion),
-            UPDATER_VERSION_STRING);
+  std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+  EXPECT_FALSE(global_prefs->GetSwapping());
+  EXPECT_EQ(global_prefs->GetActiveVersion(), UPDATER_VERSION_STRING);
 }
 
 TEST_F(AppServerTestCase, StateDirty) {
   {
     base::SingleThreadTaskExecutor main_task_executor(
         base::MessagePumpType::UI);
-    std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-    global_prefs->GetPrefService()->SetString(kPrefActiveVersion,
-                                              UPDATER_VERSION_STRING);
-    global_prefs->GetPrefService()->SetBoolean(kPrefSwapping, true);
+    std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+    global_prefs->SetActiveVersion(UPDATER_VERSION_STRING);
+    global_prefs->SetSwapping(true);
     PrefsCommitPendingWrites(global_prefs->GetPrefService());
-    std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-    local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+    std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+    local_prefs->SetQualified(true);
     PrefsCommitPendingWrites(local_prefs->GetPrefService());
   }
   auto app = base::MakeRefCounted<AppServerTest>();
@@ -194,23 +190,21 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).WillOnce(Return(true));
   EXPECT_CALL(*app, UninstallSelf).Times(0);
   EXPECT_EQ(app->Run(), 0);
-  std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-  EXPECT_FALSE(global_prefs->GetPrefService()->GetBoolean(kPrefSwapping));
-  EXPECT_EQ(global_prefs->GetPrefService()->GetString(kPrefActiveVersion),
-            UPDATER_VERSION_STRING);
+  std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+  EXPECT_FALSE(global_prefs->GetSwapping());
+  EXPECT_EQ(global_prefs->GetActiveVersion(), UPDATER_VERSION_STRING);
 }
 
 TEST_F(AppServerTestCase, StateDirtySwapFails) {
   {
     base::SingleThreadTaskExecutor main_task_executor(
         base::MessagePumpType::UI);
-    std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-    global_prefs->GetPrefService()->SetString(kPrefActiveVersion,
-                                              UPDATER_VERSION_STRING);
-    global_prefs->GetPrefService()->SetBoolean(kPrefSwapping, true);
+    std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+    global_prefs->SetActiveVersion(UPDATER_VERSION_STRING);
+    global_prefs->SetSwapping(true);
     PrefsCommitPendingWrites(global_prefs->GetPrefService());
-    std::unique_ptr<UpdaterPrefs> local_prefs = CreateLocalPrefs();
-    local_prefs->GetPrefService()->SetBoolean(kPrefQualified, true);
+    std::unique_ptr<LocalPrefs> local_prefs = CreateLocalPrefs();
+    local_prefs->SetQualified(true);
     PrefsCommitPendingWrites(local_prefs->GetPrefService());
   }
   auto app = base::MakeRefCounted<AppServerTest>();
@@ -220,10 +214,9 @@
   EXPECT_CALL(*app, SwapRPCInterfaces).WillOnce(Return(false));
   EXPECT_CALL(*app, UninstallSelf).Times(0);
   EXPECT_EQ(app->Run(), 2);
-  std::unique_ptr<UpdaterPrefs> global_prefs = CreateGlobalPrefs();
-  EXPECT_TRUE(global_prefs->GetPrefService()->GetBoolean(kPrefSwapping));
-  EXPECT_EQ(global_prefs->GetPrefService()->GetString(kPrefActiveVersion),
-            UPDATER_VERSION_STRING);
+  std::unique_ptr<GlobalPrefs> global_prefs = CreateGlobalPrefs();
+  EXPECT_TRUE(global_prefs->GetSwapping());
+  EXPECT_EQ(global_prefs->GetActiveVersion(), UPDATER_VERSION_STRING);
 }
 
 }  // namespace updater
diff --git a/chrome/updater/prefs.cc b/chrome/updater/prefs.cc
index 1522c11..061307a3 100644
--- a/chrome/updater/prefs.cc
+++ b/chrome/updater/prefs.cc
@@ -21,21 +21,49 @@
 
 namespace updater {
 
+namespace {
+
 const char kPrefQualified[] = "qualified";
 const char kPrefSwapping[] = "swapping";
 const char kPrefActiveVersion[] = "active_version";
 
-UpdaterPrefs::UpdaterPrefs(std::unique_ptr<ScopedPrefsLock> lock,
-                           std::unique_ptr<PrefService> prefs)
+}  // namespace
+
+UpdaterPrefsImpl::UpdaterPrefsImpl(std::unique_ptr<ScopedPrefsLock> lock,
+                                   std::unique_ptr<PrefService> prefs)
     : lock_(std::move(lock)), prefs_(std::move(prefs)) {}
 
-UpdaterPrefs::~UpdaterPrefs() = default;
+UpdaterPrefsImpl::~UpdaterPrefsImpl() = default;
 
-PrefService* UpdaterPrefs::GetPrefService() const {
+PrefService* UpdaterPrefsImpl::GetPrefService() const {
   return prefs_.get();
 }
 
-std::unique_ptr<UpdaterPrefs> CreateGlobalPrefs() {
+bool UpdaterPrefsImpl::GetQualified() const {
+  return prefs_->GetBoolean(kPrefQualified);
+}
+
+void UpdaterPrefsImpl::SetQualified(bool value) {
+  prefs_->SetBoolean(kPrefQualified, value);
+}
+
+std::string UpdaterPrefsImpl::GetActiveVersion() const {
+  return prefs_->GetString(kPrefActiveVersion);
+}
+
+void UpdaterPrefsImpl::SetActiveVersion(std::string value) {
+  prefs_->SetString(kPrefActiveVersion, value);
+}
+
+bool UpdaterPrefsImpl::GetSwapping() const {
+  return prefs_->GetBoolean(kPrefSwapping);
+}
+
+void UpdaterPrefsImpl::SetSwapping(bool value) {
+  prefs_->SetBoolean(kPrefSwapping, value);
+}
+
+std::unique_ptr<GlobalPrefs> CreateGlobalPrefs() {
   std::unique_ptr<ScopedPrefsLock> lock =
       AcquireGlobalPrefsLock(base::TimeDelta::FromMinutes(2));
   if (!lock)
@@ -54,11 +82,11 @@
   pref_registry->RegisterBooleanPref(kPrefSwapping, false);
   pref_registry->RegisterStringPref(kPrefActiveVersion, "0");
 
-  return std::make_unique<UpdaterPrefs>(
+  return std::make_unique<UpdaterPrefsImpl>(
       std::move(lock), pref_service_factory.Create(pref_registry));
 }
 
-std::unique_ptr<UpdaterPrefs> CreateLocalPrefs() {
+std::unique_ptr<LocalPrefs> CreateLocalPrefs() {
   base::FilePath local_prefs_dir;
   if (!GetVersionedDirectory(&local_prefs_dir))
     return nullptr;
@@ -71,7 +99,7 @@
   update_client::RegisterPrefs(pref_registry.get());
   pref_registry->RegisterBooleanPref(kPrefQualified, false);
 
-  return std::make_unique<UpdaterPrefs>(
+  return std::make_unique<UpdaterPrefsImpl>(
       nullptr, pref_service_factory.Create(pref_registry));
 }
 
diff --git a/chrome/updater/prefs.h b/chrome/updater/prefs.h
index 168990e..68d66a6 100644
--- a/chrome/updater/prefs.h
+++ b/chrome/updater/prefs.h
@@ -11,34 +11,43 @@
 
 namespace updater {
 
-class ScopedPrefsLock;
-
-extern const char kPrefQualified[];
-extern const char kPrefSwapping[];
-extern const char kPrefActiveVersion[];
-
 class UpdaterPrefs {
  public:
-  UpdaterPrefs(std::unique_ptr<ScopedPrefsLock> lock,
-               std::unique_ptr<PrefService> prefs);
+  UpdaterPrefs() = default;
   UpdaterPrefs(const UpdaterPrefs&) = delete;
   UpdaterPrefs& operator=(const UpdaterPrefs&) = delete;
-  ~UpdaterPrefs();
+  virtual ~UpdaterPrefs() = default;
 
-  PrefService* GetPrefService() const;
+  virtual PrefService* GetPrefService() const = 0;
+};
 
- private:
-  std::unique_ptr<ScopedPrefsLock> lock_;
-  std::unique_ptr<PrefService> prefs_;
+class LocalPrefs : public UpdaterPrefs {
+ public:
+  LocalPrefs() = default;
+  ~LocalPrefs() override = default;
+
+  virtual bool GetQualified() const = 0;
+  virtual void SetQualified(bool value) = 0;
+};
+
+class GlobalPrefs : public UpdaterPrefs {
+ public:
+  GlobalPrefs() = default;
+  ~GlobalPrefs() override = default;
+
+  virtual std::string GetActiveVersion() const = 0;
+  virtual void SetActiveVersion(std::string value) = 0;
+  virtual bool GetSwapping() const = 0;
+  virtual void SetSwapping(bool value) = 0;
 };
 
 // Open the global prefs. These prefs are protected by a mutex, and shared by
 // all updaters on the system. Returns nullptr if the mutex cannot be acquired.
-std::unique_ptr<UpdaterPrefs> CreateGlobalPrefs();
+std::unique_ptr<GlobalPrefs> CreateGlobalPrefs();
 
 // Open the version-specific prefs. These prefs are not protected by any mutex
 // and not shared with other versions of the updater.
-std::unique_ptr<UpdaterPrefs> CreateLocalPrefs();
+std::unique_ptr<LocalPrefs> CreateLocalPrefs();
 
 // Commits prefs changes to storage. This function should only be called
 // when the changes must be written immediately, for instance, during program
diff --git a/chrome/updater/prefs_impl.h b/chrome/updater/prefs_impl.h
index 985e0db..e17f278 100644
--- a/chrome/updater/prefs_impl.h
+++ b/chrome/updater/prefs_impl.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+#include "prefs.h"
+
 namespace base {
 class TimeDelta;
 }  // namespace base
@@ -29,6 +31,30 @@
   std::unique_ptr<ScopedPrefsLockImpl> impl_;
 };
 
+class UpdaterPrefsImpl : public LocalPrefs, public GlobalPrefs {
+ public:
+  UpdaterPrefsImpl(std::unique_ptr<ScopedPrefsLock> lock,
+                   std::unique_ptr<PrefService> prefs);
+  ~UpdaterPrefsImpl() override;
+
+  // Overrides for UpdaterPrefs.
+  PrefService* GetPrefService() const override;
+
+  // Overrides for LocalPrefs
+  bool GetQualified() const override;
+  void SetQualified(bool value) override;
+
+  // Overrides for GlobalPrfs
+  std::string GetActiveVersion() const override;
+  void SetActiveVersion(std::string value) override;
+  bool GetSwapping() const override;
+  void SetSwapping(bool value) override;
+
+ private:
+  std::unique_ptr<ScopedPrefsLock> lock_;
+  std::unique_ptr<PrefService> prefs_;
+};
+
 // Returns a ScopedPrefsLock, or nullptr if the lock could not be acquired
 // within the timeout. While the ScopedPrefsLock exists, no other process on
 // the machine may access global prefs.
diff --git a/chromecast/base/alarm_manager.cc b/chromecast/base/alarm_manager.cc
index f25eba58..0168cd8 100644
--- a/chromecast/base/alarm_manager.cc
+++ b/chromecast/base/alarm_manager.cc
@@ -50,9 +50,9 @@
 }
 
 AlarmManager::AlarmManager(
-    std::unique_ptr<base::Clock> clock,
+    const base::Clock* clock,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : clock_(std::move(clock)),
+    : clock_(clock),
       task_runner_(std::move(task_runner)),
       weak_factory_(this) {
   DCHECK(clock_);
@@ -66,7 +66,7 @@
 }
 
 AlarmManager::AlarmManager()
-    : AlarmManager(std::make_unique<base::DefaultClock>(),
+    : AlarmManager(base::DefaultClock::GetInstance(),
                    base::ThreadTaskRunnerHandle::Get()) {}
 
 AlarmManager::~AlarmManager() {}
diff --git a/chromecast/base/alarm_manager.h b/chromecast/base/alarm_manager.h
index 70ec2869..7060e91 100644
--- a/chromecast/base/alarm_manager.h
+++ b/chromecast/base/alarm_manager.h
@@ -41,7 +41,7 @@
 
   // For testing only. Allows setting a fake clock and using a custom task
   // runner.
-  AlarmManager(std::unique_ptr<base::Clock> clock,
+  AlarmManager(const base::Clock* clock,
                scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   // Add an alarm.
@@ -105,7 +105,7 @@
       next_alarm_;
 
   // Poller for wall clock time.
-  std::unique_ptr<base::Clock> clock_;
+  const base::Clock* const clock_;
   base::RepeatingTimer clock_tick_timer_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
diff --git a/chromecast/base/alarm_manager_unittest.cc b/chromecast/base/alarm_manager_unittest.cc
index 86e4aed1..5c36d71b 100644
--- a/chromecast/base/alarm_manager_unittest.cc
+++ b/chromecast/base/alarm_manager_unittest.cc
@@ -44,7 +44,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(10);
   std::unique_ptr<AlarmHandle> handle(manager->PostAlarmTask(
@@ -67,7 +67,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add an alarm.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(10);
@@ -97,7 +97,7 @@
       std::make_unique<base::SimpleTestClock>();
   test_clock->SetNow(now);
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      test_clock.get(), base::ThreadTaskRunnerHandle::Get());
 
   // Add an alarm in the past. Should fire right away.
   base::Time alarm_time = base::Time::Now() - base::TimeDelta::FromMinutes(10);
@@ -120,7 +120,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add an alarm. The time jumps to the future.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(10);
@@ -144,7 +144,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add an alarm. The time jumps far into the future.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(10);
@@ -170,7 +170,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(10);
@@ -216,7 +216,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(12);
@@ -264,7 +264,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(12);
@@ -308,7 +308,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(15);
@@ -357,7 +357,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(15);
@@ -413,7 +413,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(12);
@@ -462,7 +462,7 @@
   test_clock->SetNow(now);
   base::SimpleTestClock* clock = test_clock.get();
   std::unique_ptr<AlarmManager> manager = std::make_unique<AlarmManager>(
-      std::move(test_clock), base::ThreadTaskRunnerHandle::Get());
+      clock, base::ThreadTaskRunnerHandle::Get());
 
   // Add first task.
   base::Time alarm_time = now + base::TimeDelta::FromMinutes(12);
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 5fa873b..1269b1b 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -321,6 +321,10 @@
 const base::Feature kOsSettingsDeepLinking{"OsSettingsDeepLinking",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Flips chrome://os-settings to show Polymer 3 version
+const base::Feature kOsSettingsPolymer3{"OsSettingsPolymer3",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to enable the Parental Controls section of settings.
 const base::Feature kParentalControlsSettings{
     "ChromeOSParentalControlsSettings", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 64cf887..c5de7aa 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -141,6 +141,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kOsSettingsDeepLinking;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kOsSettingsPolymer3;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kParentalControlsSettings;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kPluginVmShowCameraPermissions;
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index ea4f616..4fa984f 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-86-4181.3-1594638142-benchmark-86.0.4204.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-86-4181.3-1594638142-benchmark-86.0.4205.0-r1.orderfile.xz
diff --git a/components/autofill_assistant/browser/client.h b/components/autofill_assistant/browser/client.h
index bbcba87..5398ef8 100644
--- a/components/autofill_assistant/browser/client.h
+++ b/components/autofill_assistant/browser/client.h
@@ -84,8 +84,16 @@
 
   // Stops autofill assistant for the current WebContents, both controller
   // and UI.
+  // The reason is ignored if RecordDropOut has been previously called.
   virtual void Shutdown(Metrics::DropOutReason reason) = 0;
 
+  // Records the reason of the drop out. Any subsequent reason for the current
+  // run will be ignored.
+  virtual void RecordDropOut(Metrics::DropOutReason reason) = 0;
+
+  // Whether this client has had an UI.
+  virtual bool HasHadUI() const = 0;
+
  protected:
   Client() = default;
 };
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 746c5bf..058c6f8 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -78,27 +78,6 @@
   return false;
 }
 
-// Returns true if reaching that state signals the end of a flow.
-bool StateEndsFlow(AutofillAssistantState state) {
-  switch (state) {
-    case AutofillAssistantState::TRACKING:
-    case AutofillAssistantState::STOPPED:
-      return true;
-
-    case AutofillAssistantState::INACTIVE:
-    case AutofillAssistantState::STARTING:
-    case AutofillAssistantState::PROMPT:
-    case AutofillAssistantState::RUNNING:
-    case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
-    case AutofillAssistantState::MODAL_DIALOG:
-    case AutofillAssistantState::BROWSE:
-      return false;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 // Check whether a domain is a subdomain of another domain.
 bool IsSubdomainOf(const std::string& subdomain,
                    const std::string& parent_domain) {
@@ -646,6 +625,15 @@
   return settings_;
 }
 
+void Controller::ShutdownIfNecessary() {
+  if (!tracking_) {
+    // We expect the DropOutReason to be already reported when we reach this
+    // point and therefore the reason we pass here in the argument should be
+    // ignored.
+    client_->Shutdown(Metrics::DropOutReason::UI_CLOSED_UNEXPECTEDLY);
+  }
+}
+
 void Controller::ReportNavigationStateChanged() {
   for (auto& listener : navigation_listeners_) {
     listener.OnNavigationStateChanged();
@@ -683,7 +671,7 @@
 
   if (!needs_ui_ && StateNeedsUI(state)) {
     RequireUI();
-  } else if (needs_ui_ && StateEndsFlow(state)) {
+  } else if (needs_ui_ && state == AutofillAssistantState::TRACKING) {
     needs_ui_ = false;
   }
 
@@ -948,7 +936,7 @@
     case ScriptExecutor::SHUTDOWN_GRACEFULLY:
       if (!tracking_) {
         EnterStoppedState();
-        client_->Shutdown(Metrics::DropOutReason::SCRIPT_SHUTDOWN);
+        RecordDropOutOrShutdown(Metrics::DropOutReason::SCRIPT_SHUTDOWN);
         return;
       }
       end_state = AutofillAssistantState::TRACKING;
@@ -1521,7 +1509,7 @@
     return;
   }
 
-  client_->Shutdown(reason);
+  RecordDropOutOrShutdown(reason);
 }
 
 void Controller::OnFatalError(const std::string& error_message,
@@ -1548,7 +1536,20 @@
     return;
   }
 
-  client_->Shutdown(reason);
+  RecordDropOutOrShutdown(reason);
+}
+
+void Controller::RecordDropOutOrShutdown(Metrics::DropOutReason reason) {
+  // If there is an UI, we wait for it to be closed before shutting down (the UI
+  // will call |ShutdownIfNecessary|).
+  if (client_->HasHadUI()) {
+    // We report right away to make sure we don't lose this reason if the client
+    // is unexpectedly destroyed while the error message is showing (for example
+    // if the tab is closed).
+    client_->RecordDropOut(reason);
+  } else {
+    client_->Shutdown(reason);
+  }
 }
 
 void Controller::PerformDelayedShutdownIfNecessary() {
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 2b63e582..681397d 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -208,7 +208,6 @@
   void GetVisualViewport(RectF* visual_viewport) const override;
   void OnFatalError(const std::string& error_message,
                     Metrics::DropOutReason reason) override;
-  void PerformDelayedShutdownIfNecessary();
   void MaybeReportFirstCheckDone();
   ViewportMode GetViewportMode() override;
   ConfigureBottomSheetProto::PeekMode GetPeekMode() override;
@@ -229,6 +228,7 @@
   BasicInteractions* GetBasicInteractions() override;
   const GenericUserInterfaceProto* GetGenericUiProto() const override;
   bool ShouldShowOverlay() const override;
+  void ShutdownIfNecessary() override;
 
  private:
   friend ControllerTest;
@@ -327,6 +327,9 @@
   ScriptTracker* script_tracker();
   bool allow_autostart() { return state_ == AutofillAssistantState::STARTING; }
 
+  void RecordDropOutOrShutdown(Metrics::DropOutReason reason);
+  void PerformDelayedShutdownIfNecessary();
+
   ClientSettings settings_;
   Client* const client_;
   const base::TickClock* const tick_clock_;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 9b16df3..30e6450 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -106,6 +106,7 @@
     ON_CALL(mock_client_, GetWebContents).WillByDefault(Return(web_contents()));
     ON_CALL(mock_client_, GetPasswordManagerClient)
         .WillByDefault(Return(&mock_password_manager_client_));
+    ON_CALL(mock_client_, HasHadUI()).WillByDefault(Return(true));
 
     controller_ = std::make_unique<Controller>(
         web_contents(), &mock_client_, task_environment()->GetMockTickClock(),
@@ -395,7 +396,7 @@
   SetNextScriptResponse(empty);
 
   EXPECT_CALL(mock_client_,
-              Shutdown(Metrics::DropOutReason::NO_INITIAL_SCRIPTS));
+              RecordDropOut(Metrics::DropOutReason::NO_INITIAL_SCRIPTS));
   Start("http://a.example.com/path");
   EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
 }
@@ -409,7 +410,7 @@
   SetNextScriptResponse(script_response);
 
   EXPECT_CALL(mock_client_,
-              Shutdown(Metrics::DropOutReason::NO_INITIAL_SCRIPTS));
+              RecordDropOut(Metrics::DropOutReason::NO_INITIAL_SCRIPTS));
   Start("http://a.example.com/path");
   EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
 }
@@ -591,7 +592,8 @@
   SetupActionsForScript("runnable", runnable_script);
 
   // The script "runnable" stops the flow and shutdowns the controller.
-  EXPECT_CALL(mock_client_, Shutdown(Metrics::DropOutReason::SCRIPT_SHUTDOWN));
+  EXPECT_CALL(mock_client_,
+              RecordDropOut(Metrics::DropOutReason::SCRIPT_SHUTDOWN));
   controller_->PerformUserAction(0);
   EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
 
@@ -707,8 +709,10 @@
   EXPECT_CALL(mock_client_, AttachUI());
   SimulateWebContentsFocused();  // must call AttachUI
 
+  EXPECT_CALL(mock_client_, AttachUI());
   controller_->OnFatalError("test", Metrics::DropOutReason::TAB_CHANGED);
-  SimulateWebContentsFocused();  // must not call AttachUI
+  EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
+  SimulateWebContentsFocused();  // must call AttachUI
 }
 
 TEST_F(ControllerTest, KeepCheckingForElement) {
@@ -1371,7 +1375,7 @@
   // Shut down once the user moves to a different domain
   EXPECT_CALL(
       mock_client_,
-      Shutdown(Metrics::DropOutReason::DOMAIN_CHANGE_DURING_BROWSE_MODE));
+      RecordDropOut(Metrics::DropOutReason::DOMAIN_CHANGE_DURING_BROWSE_MODE));
   SimulateNavigateToUrl(GURL("http://other-example.com/"));
 }
 
@@ -1451,7 +1455,7 @@
   // Make sure the whitelist got reset with the second prompt action.
   EXPECT_CALL(
       mock_client_,
-      Shutdown(Metrics::DropOutReason::DOMAIN_CHANGE_DURING_BROWSE_MODE));
+      RecordDropOut(Metrics::DropOutReason::DOMAIN_CHANGE_DURING_BROWSE_MODE));
   SimulateNavigateToUrl(GURL("http://c.example.com/"));
 }
 
@@ -1481,7 +1485,7 @@
   EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
 
   // go back.
-  EXPECT_CALL(mock_client_, Shutdown(Metrics::DropOutReason::NAVIGATION));
+  EXPECT_CALL(mock_client_, RecordDropOut(Metrics::DropOutReason::NAVIGATION));
   SetLastCommittedUrl(GURL("http://b.example.com"));
   content::NavigationSimulator::GoBack(web_contents());
 }
@@ -1571,19 +1575,21 @@
 
   // Renderer (Document) initiated navigation is allowed.
   EXPECT_CALL(mock_client_, Shutdown(_)).Times(0);
+  EXPECT_CALL(mock_client_, RecordDropOut(_)).Times(0);
   content::NavigationSimulator::NavigateAndCommitFromDocument(
       GURL("http://a.example.com/page"), web_contents()->GetMainFrame());
   EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
 
   // Expected browser initiated navigation is allowed.
   EXPECT_CALL(mock_client_, Shutdown(_)).Times(0);
+  EXPECT_CALL(mock_client_, RecordDropOut(_)).Times(0);
   controller_->ExpectNavigation();
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://b.example.com/page"));
   EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
 
   // Unexpected browser initiated navigation will cause an error.
-  EXPECT_CALL(mock_client_, Shutdown(Metrics::DropOutReason::NAVIGATION));
+  EXPECT_CALL(mock_client_, RecordDropOut(Metrics::DropOutReason::NAVIGATION));
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://c.example.com/page"));
   EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
@@ -1617,6 +1623,7 @@
   // Document (not user) initiated navigation while in RUNNING state:
   // The controller keeps going.
   EXPECT_CALL(mock_client_, Shutdown(_)).Times(0);
+  EXPECT_CALL(mock_client_, RecordDropOut(_)).Times(0);
   content::NavigationSimulator::NavigateAndCommitFromDocument(
       GURL("http://a.example.com/page"), web_contents()->GetMainFrame());
   EXPECT_EQ(AutofillAssistantState::RUNNING, controller_->GetState());
@@ -1624,6 +1631,7 @@
   // Expected browser initiated navigation while in RUNNING state:
   // The controller keeps going.
   EXPECT_CALL(mock_client_, Shutdown(_)).Times(0);
+  EXPECT_CALL(mock_client_, RecordDropOut(_)).Times(0);
   controller_->ExpectNavigation();
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://b.example.com/page"));
@@ -1632,7 +1640,7 @@
   // Unexpected browser initiated navigation while in RUNNING state:
   // The controller stops the scripts, shows an error and shuts down.
   EXPECT_CALL(mock_client_,
-              Shutdown(Metrics::DropOutReason::NAVIGATION_WHILE_RUNNING));
+              RecordDropOut(Metrics::DropOutReason::NAVIGATION_WHILE_RUNNING));
   EXPECT_CALL(mock_observer_, OnStatusMessageChanged(_));
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://c.example.com/page"));
@@ -1662,7 +1670,7 @@
   Start();
   EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
 
-  EXPECT_CALL(mock_client_, Shutdown(Metrics::DropOutReason::NAVIGATION));
+  EXPECT_CALL(mock_client_, RecordDropOut(Metrics::DropOutReason::NAVIGATION));
   EXPECT_CALL(mock_client_, DestroyUI);
   GURL google("https://google.com/search");
   SetLastCommittedUrl(google);
@@ -1699,7 +1707,7 @@
 
   EXPECT_CALL(
       mock_client_,
-      Shutdown(Metrics::DropOutReason::DOMAIN_CHANGE_DURING_BROWSE_MODE));
+      RecordDropOut(Metrics::DropOutReason::DOMAIN_CHANGE_DURING_BROWSE_MODE));
   EXPECT_CALL(mock_client_, DestroyUI);
   GURL google("https://google.com/search");
   SetLastCommittedUrl(google);
@@ -2424,4 +2432,33 @@
   EXPECT_FALSE(processed_actions_capture[1].prompt_choice().navigation_ended());
 }
 
+TEST_F(ControllerTest, CallingShutdownIfNecessaryShutsDownTheFlow) {
+  SupportsScriptResponseProto empty;
+  SetNextScriptResponse(empty);
+
+  EXPECT_CALL(mock_client_,
+              RecordDropOut(Metrics::DropOutReason::NO_INITIAL_SCRIPTS));
+  Start("http://a.example.com/path");
+  EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
+
+  // Note that even if we expect Shutdown to be called with
+  // UI_CLOSED_UNEXPECTEDLY, the reported reason in this case would be
+  // NO_INITIAL_SCRIPTS since the reason passed as argument in Shutdown is
+  // ignore if another reason has been previously reported.
+  EXPECT_CALL(mock_client_,
+              Shutdown(Metrics::DropOutReason::UI_CLOSED_UNEXPECTEDLY));
+  controller_->ShutdownIfNecessary();
+}
+
+TEST_F(ControllerTest, ShutdownDirectlyWhenNeverHadUi) {
+  SupportsScriptResponseProto empty;
+  SetNextScriptResponse(empty);
+
+  EXPECT_CALL(mock_client_, HasHadUI()).WillOnce(Return(false));
+  EXPECT_CALL(mock_client_,
+              Shutdown(Metrics::DropOutReason::NO_INITIAL_SCRIPTS));
+  Start("http://a.example.com/path");
+  EXPECT_EQ(AutofillAssistantState::STOPPED, controller_->GetState());
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/metrics.h b/components/autofill_assistant/browser/metrics.h
index ff8a84c2..ec2cb71 100644
--- a/components/autofill_assistant/browser/metrics.h
+++ b/components/autofill_assistant/browser/metrics.h
@@ -48,8 +48,9 @@
     BACK_BUTTON_CLICKED = 22,
     ONBOARDING_BACK_BUTTON_CLICKED = 23,
     NAVIGATION_WHILE_RUNNING = 24,
+    UI_CLOSED_UNEXPECTEDLY = 25,  // This is a "should never happen" entry.
 
-    kMaxValue = NAVIGATION_WHILE_RUNNING
+    kMaxValue = UI_CLOSED_UNEXPECTEDLY
   };
 
   // The different ways that autofill assistant can stop.
@@ -235,6 +236,9 @@
       case DropOutReason::NAVIGATION_WHILE_RUNNING:
         out << "NAVIGATION_WHILE_RUNNING";
         break;
+      case DropOutReason::UI_CLOSED_UNEXPECTEDLY:
+        out << "UI_CLOSED_UNEXPECTEDLY";
+        break;
         // Do not add default case to force compilation error for new values.
     }
     return out;
diff --git a/components/autofill_assistant/browser/mock_client.h b/components/autofill_assistant/browser/mock_client.h
index a83fe7a..31b2153 100644
--- a/components/autofill_assistant/browser/mock_client.h
+++ b/components/autofill_assistant/browser/mock_client.h
@@ -35,8 +35,10 @@
                      password_manager::PasswordManagerClient*());
   MOCK_METHOD0(GetAccessTokenFetcher, AccessTokenFetcher*());
   MOCK_METHOD1(Shutdown, void(Metrics::DropOutReason reason));
+  MOCK_METHOD1(RecordDropOut, void(Metrics::DropOutReason reason));
   MOCK_METHOD0(AttachUI, void());
   MOCK_METHOD0(DestroyUI, void());
+  MOCK_CONST_METHOD0(HasHadUI, bool());
 
  private:
   std::unique_ptr<MockPersonalDataManager> mock_personal_data_manager_;
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index c0185c2..0a895bb 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -238,6 +238,9 @@
 
   virtual bool ShouldShowOverlay() const = 0;
 
+  // Notifies the UI deleagate that it should shut down.
+  virtual void ShutdownIfNecessary() = 0;
+
  protected:
   UiDelegate() = default;
 };
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 57e076e..a9f4045 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -267,7 +267,7 @@
               account_password_store_
                   ? account_password_store_->CreateSyncControllerDelegate()
                   : nullptr,
-              sync_client_->GetPrefService(),
+              account_password_store_, sync_client_->GetPrefService(),
               sync_client_->GetIdentityManager(), sync_service,
               sync_client_->GetPasswordStateChangedCallback()));
     }
diff --git a/components/feature_engagement/BUILD.gn b/components/feature_engagement/BUILD.gn
index 991252e..0e37ea5 100644
--- a/components/feature_engagement/BUILD.gn
+++ b/components/feature_engagement/BUILD.gn
@@ -3,23 +3,14 @@
 # found in the LICENSE file.
 
 import("//build/buildflag_header.gni")
-import("//components/feature_engagement/features.gni")
 
 if (is_android) {
   import("//build/config/android/config.gni")
   import("//build/config/android/rules.gni")
 }
 
-buildflag_header("buildflags") {
-  header = "buildflags.h"
-  flags = [ "ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP=$enable_legacy_desktop_in_product_help" ]
-}
-
 group("feature_engagement") {
-  public_deps = [
-    ":buildflags",
-    "//components/feature_engagement/public",
-  ]
+  public_deps = [ "//components/feature_engagement/public" ]
 
   deps = [ "//components/feature_engagement/internal" ]
 }
diff --git a/components/feature_engagement/features.gni b/components/feature_engagement/features.gni
deleted file mode 100644
index 67b93f7..0000000
--- a/components/feature_engagement/features.gni
+++ /dev/null
@@ -1,7 +0,0 @@
-# Copyright 2017 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.
-
-declare_args() {
-  enable_legacy_desktop_in_product_help = false
-}
diff --git a/components/feature_engagement/public/BUILD.gn b/components/feature_engagement/public/BUILD.gn
index 6346d5c..9cb0bf0b 100644
--- a/components/feature_engagement/public/BUILD.gn
+++ b/components/feature_engagement/public/BUILD.gn
@@ -27,7 +27,6 @@
 
   deps = [
     "//base",
-    "//components/feature_engagement:buildflags",
     "//components/flags_ui",
     "//components/keyed_service/core",
   ]
diff --git a/components/feature_engagement/public/event_constants.cc b/components/feature_engagement/public/event_constants.cc
index 3f97ea2e..4d17bddd 100644
--- a/components/feature_engagement/public/event_constants.cc
+++ b/components/feature_engagement/public/event_constants.cc
@@ -5,7 +5,6 @@
 #include "components/feature_engagement/public/event_constants.h"
 
 #include "build/build_config.h"
-#include "components/feature_engagement/buildflags.h"
 
 namespace feature_engagement {
 
@@ -33,18 +32,6 @@
 
 const char kWebUITabStripClosed[] = "webui_tab_strip_closed";
 const char kWebUITabStripOpened[] = "webui_tab_strip_opened";
-
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-const char kBookmarkAdded[] = "bookmark_added";
-const char kBookmarkSessionTimeMet[] = "bookmark_session_time_met";
-
-const char kOmniboxInteraction[] = "omnibox_used";
-const char kNewTabSessionTimeMet[] = "new_tab_session_time_met";
-
-const char kIncognitoWindowOpened[] = "incognito_window_opened";
-const char kIncognitoWindowSessionTimeMet[] =
-    "incognito_window_session_time_met";
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 
diff --git a/components/feature_engagement/public/event_constants.h b/components/feature_engagement/public/event_constants.h
index c7d2187..2a0311a 100644
--- a/components/feature_engagement/public/event_constants.h
+++ b/components/feature_engagement/public/event_constants.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_FEATURE_ENGAGEMENT_PUBLIC_EVENT_CONSTANTS_H_
 
 #include "build/build_config.h"
-#include "components/feature_engagement/buildflags.h"
 
 namespace feature_engagement {
 
@@ -55,36 +54,6 @@
 // The WebUI tab strip was opened by the user.
 extern const char kWebUITabStripOpened[];
 
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-// All the events declared below are the string names of deferred onboarding
-// events for the Bookmark feature.
-
-// The user has added a Bookmark (one-off event).
-extern const char kBookmarkAdded[];
-// The user has satisfied the session time requirement to show the BookmarkPromo
-// by accumulating 5 hours of active session time (one-off event).
-extern const char kBookmarkSessionTimeMet[];
-
-// All the events declared below are the string names of deferred onboarding
-// events for the New Tab.
-
-// The user has interacted with the omnibox.
-extern const char kOmniboxInteraction[];
-// The user has satisfied the session time requirement to show the NewTabPromo
-// by accumulating 2 hours of active session time (one-off event).
-extern const char kNewTabSessionTimeMet[];
-
-// All the events declared below are the string names of deferred onboarding
-// events for the Incognito Window.
-
-// The user has opened an incognito window.
-extern const char kIncognitoWindowOpened[];
-// The user has satisfied the session time requirement to show the
-// IncognitoWindowPromo by accumulating 2 hours of active session time (one-off
-// event).
-extern const char kIncognitoWindowSessionTimeMet[];
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index bd164fba..bbd6ac2 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -4,8 +4,6 @@
 
 #include "components/feature_engagement/public/feature_constants.h"
 
-#include "components/feature_engagement/buildflags.h"
-
 namespace feature_engagement {
 
 const base::Feature kIPHDemoMode{"IPH_DemoMode",
@@ -28,15 +26,6 @@
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kIPHWebUITabStripFeature{"IPH_WebUITabStrip",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
-
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-const base::Feature kIPHBookmarkFeature{"IPH_Bookmark",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kIPHIncognitoWindowFeature{
-    "IPH_IncognitoWindow", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kIPHNewTabFeature{"IPH_NewTab",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index 9ce8f9f..ebaa5d3 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -7,7 +7,6 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
-#include "components/feature_engagement/buildflags.h"
 
 namespace feature_engagement {
 
@@ -25,12 +24,6 @@
 extern const base::Feature kIPHPasswordsAccountStorageFeature;
 extern const base::Feature kIPHReopenTabFeature;
 extern const base::Feature kIPHWebUITabStripFeature;
-
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-extern const base::Feature kIPHBookmarkFeature;
-extern const base::Feature kIPHIncognitoWindowFeature;
-extern const base::Feature kIPHNewTabFeature;
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IPH)
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index d038503..f4d77e25f 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -5,7 +5,6 @@
 #include "components/feature_engagement/public/feature_list.h"
 
 #include "base/stl_util.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/feature_engagement/public/feature_constants.h"
 
 namespace feature_engagement {
@@ -69,11 +68,6 @@
     &kIPHPasswordsAccountStorageFeature,
     &kIPHReopenTabFeature,
     &kIPHWebUITabStripFeature,
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-    &kIPHBookmarkFeature,
-    &kIPHIncognitoWindowFeature,
-    &kIPHNewTabFeature,
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 };
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 0326476..44fed855 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -10,7 +10,6 @@
 #include "base/feature_list.h"
 #include "base/stl_util.h"
 #include "build/build_config.h"
-#include "components/feature_engagement/buildflags.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/flags_ui/feature_entry.h"
 
@@ -125,11 +124,6 @@
                        "IPH_PasswordsAccountStorage");
 DEFINE_VARIATION_PARAM(kIPHReopenTabFeature, "IPH_ReopenTab");
 DEFINE_VARIATION_PARAM(kIPHWebUITabStripFeature, "IPH_WebUITabStrip");
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-DEFINE_VARIATION_PARAM(kIPHBookmarkFeature, "IPH_Bookmark");
-DEFINE_VARIATION_PARAM(kIPHIncognitoWindowFeature, "IPH_IncognitoWindow");
-DEFINE_VARIATION_PARAM(kIPHNewTabFeature, "IPH_NewTab");
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 
@@ -190,11 +184,6 @@
         VARIATION_ENTRY(kIPHGlobalMediaControls),
         VARIATION_ENTRY(kIPHReopenTabFeature),
         VARIATION_ENTRY(kIPHWebUITabStripFeature),
-#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
-        VARIATION_ENTRY(kIPHBookmarkFeature),
-        VARIATION_ENTRY(kIPHIncognitoWindowFeature),
-        VARIATION_ENTRY(kIPHNewTabFeature),
-#endif  // BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 };
diff --git a/components/open_from_clipboard/clipboard_recent_content.h b/components/open_from_clipboard/clipboard_recent_content.h
index 5d6ed3c..53c3808 100644
--- a/components/open_from_clipboard/clipboard_recent_content.h
+++ b/components/open_from_clipboard/clipboard_recent_content.h
@@ -48,6 +48,20 @@
   // Return if system's clipboard contains an image.
   virtual bool HasRecentImageFromClipboard() = 0;
 
+  /*
+   On iOS, iOS 14 introduces new clipboard APIs that are async. The asynchronous
+   forms of clipboard access below should be preferred.
+   */
+  using HasDataCallback = base::OnceCallback<void(bool)>;
+  using GetRecentURLCallback = base::OnceCallback<void(base::Optional<GURL>)>;
+
+  // Returns whether the clipboard contains a URL to |HasDataCallback| if it
+  // is recent enough and has not been suppressed.
+  virtual void HasRecentURLFromClipboard(HasDataCallback callback) = 0;
+  // Returns clipboard content as URL to |GetRecentURLCallback|, if it has a
+  // compatible type, is recent enough and has not been suppressed.
+  virtual void GetRecentURLFromClipboard(GetRecentURLCallback callback) = 0;
+
   // Returns how old the content of the clipboard is.
   virtual base::TimeDelta GetClipboardContentAge() const = 0;
 
diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.cc b/components/open_from_clipboard/clipboard_recent_content_generic.cc
index b4841e0..1764f49 100644
--- a/components/open_from_clipboard/clipboard_recent_content_generic.cc
+++ b/components/open_from_clipboard/clipboard_recent_content_generic.cc
@@ -118,6 +118,16 @@
       /* data_dst = */ nullptr);
 }
 
+void ClipboardRecentContentGeneric::HasRecentURLFromClipboard(
+    HasDataCallback callback) {
+  std::move(callback).Run(GetRecentURLFromClipboard().has_value());
+}
+
+void ClipboardRecentContentGeneric::GetRecentURLFromClipboard(
+    GetRecentURLCallback callback) {
+  std::move(callback).Run(GetRecentURLFromClipboard());
+}
+
 base::TimeDelta ClipboardRecentContentGeneric::GetClipboardContentAge() const {
   const base::Time last_modified_time =
       ui::Clipboard::GetForCurrentThread()->GetLastModifiedTime();
diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.h b/components/open_from_clipboard/clipboard_recent_content_generic.h
index 218a411a0..9728781 100644
--- a/components/open_from_clipboard/clipboard_recent_content_generic.h
+++ b/components/open_from_clipboard/clipboard_recent_content_generic.h
@@ -28,6 +28,8 @@
   base::Optional<base::string16> GetRecentTextFromClipboard() override;
   void GetRecentImageFromClipboard(GetRecentImageCallback callback) override;
   bool HasRecentImageFromClipboard() override;
+  void HasRecentURLFromClipboard(HasDataCallback callback) override;
+  void GetRecentURLFromClipboard(GetRecentURLCallback callback) override;
   base::TimeDelta GetClipboardContentAge() const override;
   void SuppressClipboardContent() override;
   void ClearClipboardContent() override;
diff --git a/components/open_from_clipboard/clipboard_recent_content_impl_ios.h b/components/open_from_clipboard/clipboard_recent_content_impl_ios.h
index 19bbf84..b55e13f 100644
--- a/components/open_from_clipboard/clipboard_recent_content_impl_ios.h
+++ b/components/open_from_clipboard/clipboard_recent_content_impl_ios.h
@@ -44,6 +44,15 @@
 // not been suppressed. Otherwise, returns nil.
 - (UIImage*)recentImageFromClipboard;
 
+// Uses the new iOS 14 pasteboard detection pattern API to asynchronously detect
+// if the clipboard contains a URL (that has not been suppressed) without
+// actually getting the contents.
+- (void)hasRecentURLFromClipboard:(void (^)(BOOL))callback;
+// Uses the new iOS 14 pasteboard detection pattern API to asynchronously get a
+// copied URL from the clipboard if it has not been suppressed. Passes nil to
+// the callback otherwise.
+- (void)recentURLFromClipboardAsync:(void (^)(NSURL*))callback;
+
 // Returns how old the content of the clipboard is.
 - (NSTimeInterval)clipboardContentAge;
 
diff --git a/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm b/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
index d7aea3f..5a33f64 100644
--- a/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
+++ b/components/open_from_clipboard/clipboard_recent_content_impl_ios.mm
@@ -158,6 +158,81 @@
   return self.cachedImage;
 }
 
+- (void)hasRecentURLFromClipboard:(void (^)(BOOL))callback {
+  DCHECK(callback);
+  if (@available(iOS 14, *)) {
+    [self updateIfNeeded];
+    if (![self shouldReturnValueOfClipboard]) {
+      callback(NO);
+      return;
+    }
+
+    // Use cached value if it exists
+    if (self.cachedURL) {
+      callback(YES);
+      return;
+    }
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+    NSSet<UIPasteboardDetectionPattern>* urlPattern =
+        [NSSet setWithObject:UIPasteboardDetectionPatternProbableWebURL];
+    [UIPasteboard.generalPasteboard
+        detectPatternsForPatterns:urlPattern
+                completionHandler:^(
+                    NSSet<UIPasteboardDetectionPattern>* patterns,
+                    NSError* error) {
+                  callback([patterns
+                      containsObject:
+                          UIPasteboardDetectionPatternProbableWebURL]);
+                }];
+#else
+    // To prevent clipboard notification from appearing on iOS 14 with iOS 13
+    // SDK, use the -hasURLs property to check for URL existence. This will
+    // cause crbug.com/1033935 to reappear in code using this method (also see
+    // the comments in -URLFromPasteboard in this file), but that is preferable
+    // to the notificatio appearing when it shouldn't.
+    callback(UIPasteboard.generalPasteboard.hasURLs);
+#endif
+  } else {
+    callback([self recentURLFromClipboard] != nil);
+  }
+}
+
+- (void)recentURLFromClipboardAsync:(void (^)(NSURL*))callback {
+  DCHECK(callback);
+  if (@available(iOS 14, *)) {
+    [self updateIfNeeded];
+    if (![self shouldReturnValueOfClipboard]) {
+      callback(nil);
+      return;
+    }
+
+    // Use cached value if it exists.
+    if (self.cachedURL) {
+      callback(self.cachedURL);
+      return;
+    }
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+    NSSet<UIPasteboardDetectionPattern>* urlPattern =
+        [NSSet setWithObject:UIPasteboardDetectionPatternProbableWebURL];
+    [UIPasteboard.generalPasteboard
+        detectValuesForPatterns:urlPattern
+              completionHandler:^(
+                  NSDictionary<UIPasteboardDetectionPattern, id>* values,
+                  NSError* error) {
+                self.cachedURL =
+                    values[UIPasteboardDetectionPatternProbableWebURL];
+                callback(self.cachedURL);
+              }];
+#else
+    callback([self recentURLFromClipboard]);
+#endif
+  } else {
+    callback([self recentURLFromClipboard]);
+  }
+}
+
 - (NSTimeInterval)clipboardContentAge {
   return -[self.lastPasteboardChangeDate timeIntervalSinceNow];
 }
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.h b/components/open_from_clipboard/clipboard_recent_content_ios.h
index 63b3c40..4ef8fcc 100644
--- a/components/open_from_clipboard/clipboard_recent_content_ios.h
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.h
@@ -44,6 +44,8 @@
   base::Optional<base::string16> GetRecentTextFromClipboard() override;
   void GetRecentImageFromClipboard(GetRecentImageCallback callback) override;
   bool HasRecentImageFromClipboard() override;
+  void HasRecentURLFromClipboard(HasDataCallback callback) override;
+  void GetRecentURLFromClipboard(GetRecentURLCallback callback) override;
   base::TimeDelta GetClipboardContentAge() const override;
   void SuppressClipboardContent() override;
   void ClearClipboardContent() override;
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.mm b/components/open_from_clipboard/clipboard_recent_content_ios.mm
index 5287c1c..2fc51b9 100644
--- a/components/open_from_clipboard/clipboard_recent_content_ios.mm
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.mm
@@ -102,6 +102,27 @@
   return GetRecentImageFromClipboardInternal().has_value();
 }
 
+void ClipboardRecentContentIOS::HasRecentURLFromClipboard(
+    HasDataCallback callback) {
+  __block HasDataCallback callback_for_block = std::move(callback);
+  [implementation_ hasRecentURLFromClipboard:^(BOOL exists) {
+    std::move(callback_for_block).Run(exists);
+  }];
+}
+
+void ClipboardRecentContentIOS::GetRecentURLFromClipboard(
+    GetRecentURLCallback callback) {
+  __block GetRecentURLCallback callback_for_block = std::move(callback);
+  [implementation_ recentURLFromClipboardAsync:^(NSURL* url) {
+    GURL converted_url = net::GURLWithNSURL(url);
+    if (!converted_url.is_valid()) {
+      std::move(callback_for_block).Run(base::nullopt);
+      return;
+    }
+    std::move(callback_for_block).Run(converted_url);
+  }];
+}
+
 ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {}
 
 base::TimeDelta ClipboardRecentContentIOS::GetClipboardContentAge() const {
diff --git a/components/open_from_clipboard/fake_clipboard_recent_content.cc b/components/open_from_clipboard/fake_clipboard_recent_content.cc
index 2ee1252..a57a4f8 100644
--- a/components/open_from_clipboard/fake_clipboard_recent_content.cc
+++ b/components/open_from_clipboard/fake_clipboard_recent_content.cc
@@ -40,6 +40,16 @@
   return clipboard_image_content_.has_value();
 }
 
+void FakeClipboardRecentContent::HasRecentURLFromClipboard(
+    HasDataCallback callback) {
+  std::move(callback).Run(GetRecentURLFromClipboard().has_value());
+}
+
+void FakeClipboardRecentContent::GetRecentURLFromClipboard(
+    GetRecentURLCallback callback) {
+  std::move(callback).Run(GetRecentURLFromClipboard());
+}
+
 base::TimeDelta FakeClipboardRecentContent::GetClipboardContentAge() const {
   return content_age_;
 }
diff --git a/components/open_from_clipboard/fake_clipboard_recent_content.h b/components/open_from_clipboard/fake_clipboard_recent_content.h
index 8906925b..6e23405 100644
--- a/components/open_from_clipboard/fake_clipboard_recent_content.h
+++ b/components/open_from_clipboard/fake_clipboard_recent_content.h
@@ -23,6 +23,8 @@
   base::Optional<base::string16> GetRecentTextFromClipboard() override;
   void GetRecentImageFromClipboard(GetRecentImageCallback callback) override;
   bool HasRecentImageFromClipboard() override;
+  void HasRecentURLFromClipboard(HasDataCallback callback) override;
+  void GetRecentURLFromClipboard(GetRecentURLCallback callback) override;
   base::TimeDelta GetClipboardContentAge() const override;
   void SuppressClipboardContent() override;
   void ClearClipboardContent() override;
diff --git a/components/optimization_guide/optimization_guide_decider.h b/components/optimization_guide/optimization_guide_decider.h
index 6e506a1..eddda11c 100644
--- a/components/optimization_guide/optimization_guide_decider.h
+++ b/components/optimization_guide/optimization_guide_decider.h
@@ -54,12 +54,6 @@
   virtual void RegisterOptimizationTargets(
       const std::vector<proto::OptimizationTarget>& optimization_targets) = 0;
 
-  // Returns whether the current conditions match |optimization_target|. This
-  // should only be called for main frame navigations.
-  virtual OptimizationGuideDecision ShouldTargetNavigation(
-      content::NavigationHandle* navigation_handle,
-      proto::OptimizationTarget optimization_target) = 0;
-
   // Invokes |callback| with the decision for whether the current browser
   // conditions, as expressed by |client_model_feature_values| and the
   // |navigation_handle|, match |optimization_target|.
diff --git a/components/optimization_guide/test_optimization_guide_decider.cc b/components/optimization_guide/test_optimization_guide_decider.cc
index fab4f9c..f2c0617c 100644
--- a/components/optimization_guide/test_optimization_guide_decider.cc
+++ b/components/optimization_guide/test_optimization_guide_decider.cc
@@ -14,12 +14,6 @@
 void TestOptimizationGuideDecider::RegisterOptimizationTargets(
     const std::vector<proto::OptimizationTarget>& optimization_targets) {}
 
-OptimizationGuideDecision TestOptimizationGuideDecider::ShouldTargetNavigation(
-    content::NavigationHandle* navigation_handle,
-    proto::OptimizationTarget optimization_target) {
-  return OptimizationGuideDecision::kFalse;
-}
-
 void TestOptimizationGuideDecider::ShouldTargetNavigationAsync(
     content::NavigationHandle* navigation_handle,
     proto::OptimizationTarget optimization_target,
diff --git a/components/optimization_guide/test_optimization_guide_decider.h b/components/optimization_guide/test_optimization_guide_decider.h
index ad50f01..de8755c7 100644
--- a/components/optimization_guide/test_optimization_guide_decider.h
+++ b/components/optimization_guide/test_optimization_guide_decider.h
@@ -23,9 +23,6 @@
   // OptimizationGuideDecider implementation:
   void RegisterOptimizationTargets(const std::vector<proto::OptimizationTarget>&
                                        optimization_targets) override;
-  OptimizationGuideDecision ShouldTargetNavigation(
-      content::NavigationHandle* navigation_handle,
-      proto::OptimizationTarget optimization_target) override;
   void ShouldTargetNavigationAsync(
       content::NavigationHandle* navigation_handle,
       proto::OptimizationTarget optimization_target,
diff --git a/components/paint_preview/player/android/BUILD.gn b/components/paint_preview/player/android/BUILD.gn
index 87d66b85..1d06493 100644
--- a/components/paint_preview/player/android/BUILD.gn
+++ b/components/paint_preview/player/android/BUILD.gn
@@ -61,6 +61,7 @@
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameBitmapStateController.java",
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java",
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java",
+    "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetectorDelegate.java",
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java",
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorDelegate.java",
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java",
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
index 829bb28..39a83b0 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.util.Size;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.OverScroller;
@@ -37,12 +38,24 @@
         PropertyModel model = new PropertyModel.Builder(PlayerFrameProperties.ALL_KEYS).build();
         OverScroller scroller = new OverScroller(context);
         scroller.setFriction(ViewConfiguration.getScrollFriction() / 2);
-        mMediator = new PlayerFrameMediator(model, compositorDelegate, new PlayerFrameViewport(),
-                scroller, gestureHandler, frameGuid, contentWidth, contentHeight, initialScrollX,
-                initialScrollY);
-        mView = new PlayerFrameView(context, canDetectZoom, mMediator);
+
+        mMediator = new PlayerFrameMediator(model, compositorDelegate, gestureHandler, frameGuid,
+                new Size(contentWidth, contentHeight), initialScrollX, initialScrollY);
+
+        PlayerFrameScaleController scaleController = null;
+        if (canDetectZoom) {
+            scaleController =
+                    new PlayerFrameScaleController(model.get(PlayerFrameProperties.SCALE_MATRIX),
+                            mMediator, gestureHandler::onScale);
+        }
+        PlayerFrameScrollController scrollController = new PlayerFrameScrollController(
+                scroller, mMediator, gestureHandler::onScroll, gestureHandler::onFling);
+        PlayerFrameGestureDetectorDelegate gestureDelegate = new PlayerFrameGestureDetectorDelegate(
+                scaleController, scrollController, mMediator);
+
+        mView = new PlayerFrameView(context, canDetectZoom, mMediator, gestureDelegate);
         if (overscrollHandler != null) {
-            mMediator.setOverscrollHandler(overscrollHandler);
+            scrollController.setOverscrollHandler(overscrollHandler);
         }
         PropertyModelChangeProcessor.create(model, mView, PlayerFrameViewBinder::bind);
     }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
index a04801f..d9726f5 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
@@ -11,14 +11,14 @@
 
 /**
  * Detects scroll, fling, and scale gestures on calls to {@link #onTouchEvent} and reports back to
- * the provided {@link PlayerFrameViewDelegate}.
+ * the provided {@link PlayerFrameGestureDetectorDelegate}.
  */
 class PlayerFrameGestureDetector
         implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener {
     private GestureDetector mGestureDetector;
     private ScaleGestureDetector mScaleGestureDetector;
     private boolean mCanDetectZoom;
-    private PlayerFrameViewDelegate mPlayerFrameViewDelegate;
+    private PlayerFrameGestureDetectorDelegate mDelegate;
     private PlayerFrameGestureDetector mParentGestureDetector;
     /**
      * Last horizontal scroll distance that was detected by this {@link PlayerFrameGestureDetector}
@@ -36,14 +36,14 @@
      * {@link ScaleGestureDetector}.
      * @param canDetectZoom Whether this {@link PlayerFrameGestureDetector} should detect scale
      * gestures.
-     * @param playerFrameViewDelegate The delegate used when desired gestured are detected.
+     * @param delegate The delegate used when desired gestured are detected.
      */
-    PlayerFrameGestureDetector(Context context, boolean canDetectZoom,
-            PlayerFrameViewDelegate playerFrameViewDelegate) {
+    PlayerFrameGestureDetector(
+            Context context, boolean canDetectZoom, PlayerFrameGestureDetectorDelegate delegate) {
         mGestureDetector = new GestureDetector(context, this);
         mScaleGestureDetector = new ScaleGestureDetector(context, this);
         mCanDetectZoom = canDetectZoom;
-        mPlayerFrameViewDelegate = playerFrameViewDelegate;
+        mDelegate = delegate;
     }
 
     /**
@@ -65,7 +65,7 @@
         }
 
         if (event.getAction() == MotionEvent.ACTION_UP) {
-            mPlayerFrameViewDelegate.onRelease();
+            mDelegate.onRelease();
             // Propagate the release to the parent, this won't trigger any unexpected behavior as
             // this is only an UP event.
             if (mParentGestureDetector != null) {
@@ -85,13 +85,13 @@
 
     @Override
     public boolean onSingleTapUp(MotionEvent e) {
-        mPlayerFrameViewDelegate.onTap((int) e.getX(), (int) e.getY());
+        mDelegate.onTap((int) e.getX(), (int) e.getY());
         return true;
     }
 
     @Override
     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-        if (mPlayerFrameViewDelegate.scrollBy(distanceX, distanceY)) {
+        if (mDelegate.scrollBy(distanceX, distanceY)) {
             mLastParentScrollX = 0f;
             mLastParentScrollY = 0f;
             return true;
@@ -116,12 +116,12 @@
 
     @Override
     public void onLongPress(MotionEvent e) {
-        mPlayerFrameViewDelegate.onLongPress((int) e.getX(), (int) e.getY());
+        mDelegate.onLongPress((int) e.getX(), (int) e.getY());
     }
 
     @Override
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        if (mPlayerFrameViewDelegate.onFling(velocityX, velocityY)) return true;
+        if (mDelegate.onFling(velocityX, velocityY)) return true;
 
         if (mParentGestureDetector != null) {
             return mParentGestureDetector.onFling(e1, e2, velocityX, velocityY);
@@ -132,7 +132,7 @@
     @Override
     public boolean onScale(ScaleGestureDetector detector) {
         assert mCanDetectZoom;
-        return mPlayerFrameViewDelegate.scaleBy(
+        return mDelegate.scaleBy(
                 detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY());
     }
 
@@ -145,7 +145,7 @@
     @Override
     public void onScaleEnd(ScaleGestureDetector detector) {
         assert mCanDetectZoom;
-        mPlayerFrameViewDelegate.scaleFinished(
+        mDelegate.scaleFinished(
                 detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY());
     }
 }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetectorDelegate.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetectorDelegate.java
new file mode 100644
index 0000000..56d326c
--- /dev/null
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetectorDelegate.java
@@ -0,0 +1,82 @@
+// 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.components.paintpreview.player.frame;
+
+/**
+ * Dispatches gesture events to the correct controllers.
+ */
+public class PlayerFrameGestureDetectorDelegate {
+    private final PlayerFrameScaleController mScaleController;
+    private final PlayerFrameScrollController mScrollController;
+    private final PlayerFrameViewDelegate mViewDelegate;
+
+    PlayerFrameGestureDetectorDelegate(PlayerFrameScaleController scaleController,
+            PlayerFrameScrollController scrollController, PlayerFrameViewDelegate viewDelegate) {
+        mScaleController = scaleController;
+        mScrollController = scrollController;
+        mViewDelegate = viewDelegate;
+    }
+
+    /**
+     * Called when a scroll gesture is performed.
+     * @param distanceX Horizontal scroll values in pixels.
+     * @param distanceY Vertical scroll values in pixels.
+     * @return Whether this scroll event was consumed.
+     */
+    boolean scrollBy(float distanceX, float distanceY) {
+        return mScrollController.scrollBy(distanceX, distanceY);
+    }
+
+    /**
+     * Called when a fling gesture is performed.
+     * @param velocityX Horizontal velocity value in pixels.
+     * @param velocityY Vertical velocity value in pixels.
+     * @return Whether this fling was consumed.
+     */
+    boolean onFling(float velocityX, float velocityY) {
+        return mScrollController.onFling(velocityX, velocityY);
+    }
+
+    /**
+     * Called when a gesture is released.
+     */
+    void onRelease() {
+        mScrollController.onRelease();
+    }
+
+    /**
+     * Called when a scale gesture is performed.
+     * @return Whether this scale event was consumed.
+     */
+    boolean scaleBy(float scaleFactor, float focalPointX, float focalPointY) {
+        return mScaleController.scaleBy(scaleFactor, focalPointX, focalPointY);
+    }
+
+    /**
+     * Called when a scale gesture is finished.
+     * @return Whether this scale event was consumed.
+     */
+    boolean scaleFinished(float scaleFactor, float focalPointX, float focalPointY) {
+        return mScaleController.scaleFinished(scaleFactor, focalPointX, focalPointY);
+    }
+
+    /**
+     * Called when a single tap gesture is performed.
+     * @param x X coordinate of the point clicked.
+     * @param y Y coordinate of the point clicked.
+     */
+    void onTap(int x, int y) {
+        mViewDelegate.onTap(x, y);
+    }
+
+    /**
+     * Called when a long press gesture is performed.
+     * @param x X coordinate of the point clicked.
+     * @param y Y coordinate of the point clicked.
+     */
+    void onLongPress(int x, int y) {
+        mViewDelegate.onLongPress(x, y);
+    }
+}
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
index 3b20e0f..fe18175 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
@@ -9,12 +9,10 @@
 import android.graphics.Rect;
 import android.util.Size;
 import android.view.View;
-import android.widget.OverScroller;
 
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.UnguessableToken;
-import org.chromium.components.paintpreview.player.OverscrollHandler;
 import org.chromium.components.paintpreview.player.PlayerCompositorDelegate;
 import org.chromium.components.paintpreview.player.PlayerGestureListener;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -64,36 +62,29 @@
     /** The viewport of this frame. */
     private final PlayerFrameViewport mViewport;
 
+    private float mInitialScaleFactor;
     /** Handles scaling of bitmaps. */
-    private final Matrix mBitmapScaleMatrix = new Matrix();
-
-    /** Handles scrolling. */
-    private final PlayerFrameScrollController mScrollController;
-    /** Handles scaling. */
-    private final PlayerFrameScaleController mScaleController;
+    private final Matrix mBitmapScaleMatrix;
 
     private final PlayerFrameBitmapStateController mBitmapStateController;
 
     private PlayerGestureListener mGestureListener;
 
     PlayerFrameMediator(PropertyModel model, PlayerCompositorDelegate compositorDelegate,
-            PlayerFrameViewport viewport, OverScroller scroller,
-            PlayerGestureListener gestureListener, UnguessableToken frameGuid, int contentWidth,
-            int contentHeight, int initialScrollX, int initialScrollY) {
+            PlayerGestureListener gestureListener, UnguessableToken frameGuid, Size contentSize,
+            int initialScrollX, int initialScrollY) {
+        mBitmapScaleMatrix = new Matrix();
         mModel = model;
         mModel.set(PlayerFrameProperties.SCALE_MATRIX, mBitmapScaleMatrix);
 
         mCompositorDelegate = compositorDelegate;
-        mViewport = viewport;
+        mGestureListener = gestureListener;
+        mViewport = new PlayerFrameViewport();
+        mInitialScaleFactor = 0f;
         mGuid = frameGuid;
-        mContentSize = new Size(contentWidth, contentHeight);
+        mContentSize = contentSize;
         mBitmapStateController = new PlayerFrameBitmapStateController(
                 mGuid, mViewport, mContentSize, mCompositorDelegate, this);
-        mScrollController = new PlayerFrameScrollController(scroller, mViewport, mContentSize, this,
-                gestureListener::onScroll, gestureListener::onFling);
-        mScaleController = new PlayerFrameScaleController(
-                mViewport, mContentSize, mBitmapScaleMatrix, this, gestureListener::onScale);
-        mGestureListener = gestureListener;
         mViewport.offset(initialScrollX, initialScrollY);
         mViewport.setScale(0f);
     }
@@ -143,15 +134,6 @@
         setBitmapScaleMatrix(matrix, scaleFactor);
     }
 
-    /**
-     * Sets the overscroll-to-refresh handler on the {@link mScrollController}. This cannot be
-     * created at construction of this object as it needs to be created on top of the view
-     * hierarchy to show the animation.
-     */
-    void setOverscrollHandler(OverscrollHandler overscrollHandler) {
-        mScrollController.setOverscrollHandler(overscrollHandler);
-    }
-
     // PlayerFrameViewDelegate
 
     @Override
@@ -163,35 +145,10 @@
         }
 
         // Set initial scale so that content width fits within the layout dimensions.
-        mScaleController.calculateInitialScaleFactor(width);
+        adjustInitialScaleFactor(width);
         final float scaleFactor = mViewport.getScale();
-        updateViewportSize(width, height,
-                (scaleFactor == 0f) ? mScaleController.getInitialScaleFactor() : scaleFactor);
-    }
-
-    @Override
-    public boolean scrollBy(float distanceX, float distanceY) {
-        return mScrollController.scrollBy(distanceX, distanceY);
-    }
-
-    @Override
-    public boolean onFling(float velocityX, float velocityY) {
-        return mScrollController.onFling(velocityX, velocityY);
-    }
-
-    @Override
-    public void onRelease() {
-        mScrollController.onRelease();
-    }
-
-    @Override
-    public boolean scaleBy(float scaleFactor, float focalPointX, float focalPointY) {
-        return mScaleController.scaleBy(scaleFactor, focalPointX, focalPointY);
-    }
-
-    @Override
-    public boolean scaleFinished(float scaleFactor, float focalPointX, float focalPointY) {
-        return mScaleController.scaleFinished(scaleFactor, focalPointX, focalPointY);
+        updateViewportSize(
+                width, height, (scaleFactor == 0f) ? getInitialScaleFactor() : scaleFactor);
     }
 
     @Override
@@ -213,6 +170,21 @@
     // PlayerFrameMediatorDelegate
 
     @Override
+    public PlayerFrameViewport getViewport() {
+        return mViewport;
+    }
+
+    @Override
+    public Size getContentSize() {
+        return mContentSize;
+    }
+
+    @Override
+    public float getInitialScaleFactor() {
+        return mInitialScaleFactor;
+    }
+
+    @Override
     public void onStartScaling() {
         mBitmapStateController.invalidateLoadingBitmaps();
     }
@@ -342,10 +314,9 @@
 
     @VisibleForTesting
     void forceRedraw() {
-        mScaleController.calculateInitialScaleFactor(mViewport.getWidth());
+        adjustInitialScaleFactor(mViewport.getWidth());
         final float scaleFactor = mViewport.getScale();
-        mViewport.setScale(
-                (scaleFactor == 0f) ? mScaleController.getInitialScaleFactor() : scaleFactor);
+        mViewport.setScale((scaleFactor == 0f) ? getInitialScaleFactor() : scaleFactor);
         updateVisuals(true);
         for (int i = 0; i < mSubFrameViews.size(); i++) {
             if (mSubFrameViews.get(i).getVisibility() != View.VISIBLE) continue;
@@ -360,4 +331,12 @@
                 (int) (((float) inRect.right) * scaleFactor),
                 (int) (((float) inRect.bottom) * scaleFactor));
     }
+
+    /**
+     * Calculates the initial scale factor for a given viewport width.
+     * @param width The viewport width.
+     */
+    private void adjustInitialScaleFactor(float width) {
+        mInitialScaleFactor = width / ((float) mContentSize.getWidth());
+    }
 }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorDelegate.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorDelegate.java
index a62ce89f..9b12b1d 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorDelegate.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorDelegate.java
@@ -7,12 +7,28 @@
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.util.Size;
 
 /**
  * API of the PlayerFrameMediator to helper classes.
  */
 public interface PlayerFrameMediatorDelegate {
     /**
+     * Gets the visual viewport of the player.
+     */
+    public PlayerFrameViewport getViewport();
+
+    /**
+     * Gets the size of the content shown in the mediator.
+     */
+    public Size getContentSize();
+
+    /**
+     * Gets the initial scale factor at the last computed viewport width.
+     */
+    public float getInitialScaleFactor();
+
+    /**
      * Triggers an update of the visual contents of the PlayerFrameView. This fetches updates the
      * model and fetches any new bitmaps asynchronously.
      * @param scaleChanged Indicates that the scale changed so all current bitmaps need to be
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleController.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleController.java
index 97f069f7..c148756 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleController.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleController.java
@@ -18,7 +18,6 @@
 public class PlayerFrameScaleController {
     private static final float MAX_SCALE_FACTOR = 5f;
 
-    private float mInitialScaleFactor;
     private float mUncommittedScaleFactor;
 
     /** References to shared state. */
@@ -29,32 +28,18 @@
     private final PlayerFrameMediatorDelegate mMediatorDelegate;
     private final Callback<Boolean> mOnScaleListener;
 
-    PlayerFrameScaleController(PlayerFrameViewport viewport, Size contentSize,
-            Matrix bitmapScaleMatrix, PlayerFrameMediatorDelegate mediatorDelegate,
+    PlayerFrameScaleController(Matrix bitmapScaleMatrix,
+            PlayerFrameMediatorDelegate mediatorDelegate,
             @Nullable Callback<Boolean> onScaleListener) {
-        mViewport = viewport;
-        mContentSize = contentSize;
+        mUncommittedScaleFactor = 0f;
+        mViewport = mediatorDelegate.getViewport();
+        mContentSize = mediatorDelegate.getContentSize();
         mBitmapScaleMatrix = bitmapScaleMatrix;
         mMediatorDelegate = mediatorDelegate;
         mOnScaleListener = onScaleListener;
     }
 
     /**
-     * Calculates the initial scale factor for a given viewport width.
-     * @param width The viewport width.
-     */
-    void calculateInitialScaleFactor(float width) {
-        mInitialScaleFactor = width / ((float) mContentSize.getWidth());
-    }
-
-    /**
-     * Gets the initial scale factor at the last computed viewport width.
-     */
-    float getInitialScaleFactor() {
-        return mInitialScaleFactor;
-    }
-
-    /**
      * How scale for the paint preview player works.
      *
      * There are two reference frames:
@@ -92,22 +77,23 @@
         }
         // Don't scale outside of the acceptable range. The value is still accumulated such that the
         // continuous gesture feels smooth.
+        final float initialScaleFactor = mMediatorDelegate.getInitialScaleFactor();
         final float lastUncommittedScaleFactor = mUncommittedScaleFactor;
         mUncommittedScaleFactor *= scaleFactor;
         // Compute a corrected and bounded scale factor when close to the max/min scale.
-        if (mUncommittedScaleFactor < mInitialScaleFactor
-                && lastUncommittedScaleFactor > mInitialScaleFactor) {
-            scaleFactor = mInitialScaleFactor / lastUncommittedScaleFactor;
+        if (mUncommittedScaleFactor < initialScaleFactor
+                && lastUncommittedScaleFactor > initialScaleFactor) {
+            scaleFactor = initialScaleFactor / lastUncommittedScaleFactor;
         } else if (mUncommittedScaleFactor > MAX_SCALE_FACTOR
                 && lastUncommittedScaleFactor < MAX_SCALE_FACTOR) {
             scaleFactor = MAX_SCALE_FACTOR / lastUncommittedScaleFactor;
-        } else if (mUncommittedScaleFactor > mInitialScaleFactor
-                && lastUncommittedScaleFactor < mInitialScaleFactor) {
-            scaleFactor = mUncommittedScaleFactor / mInitialScaleFactor;
+        } else if (mUncommittedScaleFactor > initialScaleFactor
+                && lastUncommittedScaleFactor < initialScaleFactor) {
+            scaleFactor = mUncommittedScaleFactor / initialScaleFactor;
         } else if (mUncommittedScaleFactor < MAX_SCALE_FACTOR
                 && lastUncommittedScaleFactor > MAX_SCALE_FACTOR) {
             scaleFactor = mUncommittedScaleFactor / MAX_SCALE_FACTOR;
-        } else if (mUncommittedScaleFactor < mInitialScaleFactor
+        } else if (mUncommittedScaleFactor < initialScaleFactor
                 || lastUncommittedScaleFactor > MAX_SCALE_FACTOR) {
             return true;
         }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollController.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollController.java
index ceb3d4ab..9e08fdc9 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollController.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollController.java
@@ -32,12 +32,11 @@
     private final Runnable mOnScrollListener;
     private final Runnable mOnFlingListener;
 
-    PlayerFrameScrollController(OverScroller scroller, PlayerFrameViewport viewport,
-            Size contentSize, PlayerFrameMediatorDelegate mediatorDelegate,
+    PlayerFrameScrollController(OverScroller scroller, PlayerFrameMediatorDelegate mediatorDelegate,
             @Nullable Runnable onScrollListener, @Nullable Runnable onFlingListener) {
         mScroller = scroller;
-        mViewport = viewport;
-        mContentSize = contentSize;
+        mViewport = mediatorDelegate.getViewport();
+        mContentSize = mediatorDelegate.getContentSize();
         mMediatorDelegate = mediatorDelegate;
         mOnScrollListener = onScrollListener;
         mOnFlingListener = onFlingListener;
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
index d3c18d9..a8edfbb1 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
@@ -38,13 +38,14 @@
      * @param playerFrameViewDelegate The interface used for forwarding events.
      */
     PlayerFrameView(@NonNull Context context, boolean canDetectZoom,
-            PlayerFrameViewDelegate playerFrameViewDelegate) {
+            PlayerFrameViewDelegate playerFrameViewDelegate,
+            PlayerFrameGestureDetectorDelegate gestureDetectorDelegate) {
         super(context);
         setWillNotDraw(false);
         mDelegate = playerFrameViewDelegate;
         mBitmapPainter = new PlayerFrameBitmapPainter(this::invalidate);
         mGestureDetector =
-                new PlayerFrameGestureDetector(context, canDetectZoom, playerFrameViewDelegate);
+                new PlayerFrameGestureDetector(context, canDetectZoom, gestureDetectorDelegate);
     }
 
     PlayerFrameGestureDetector getGestureDetector() {
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java
index 2140f33..6e6cda5c 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewDelegate.java
@@ -14,26 +14,6 @@
     void setLayoutDimensions(int width, int height);
 
     /**
-     * Called when a scroll gesture is performed.
-     * @param distanceX Horizontal scroll values in pixels.
-     * @param distanceY Vertical scroll values in pixels.
-     * @return Whether this scroll event was consumed.
-     */
-    boolean scrollBy(float distanceX, float distanceY);
-
-    /**
-     * Called when a scale gesture is performed.
-     * @return Whether this scale event was consumed.
-     */
-    boolean scaleBy(float scaleFactor, float focalPointX, float focalPointY);
-
-    /**
-     * Called when a scale gesture is finished.
-     * @return Whether this scale event was consumed.
-     */
-    boolean scaleFinished(float scaleFactor, float focalPointX, float focalPointY);
-
-    /**
      * Called when a single tap gesture is performed.
      * @param x X coordinate of the point clicked.
      * @param y Y coordinate of the point clicked.
@@ -46,17 +26,4 @@
      * @param y Y coordinate of the point clicked.
      */
     void onLongPress(int x, int y);
-
-    /**
-     * Called when a fling gesture is performed.
-     * @param velocityX Horizontal velocity value in pixels.
-     * @param velocityY Vertical velocity value in pixels.
-     * @return Whether this fling was consumed.
-     */
-    boolean onFling(float velocityX, float velocityY);
-
-    /**
-     * Called when a gesture is released.
-     */
-    void onRelease();
 }
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
index a2f593a..7c4512f 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
@@ -15,6 +15,7 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.util.Pair;
+import android.util.Size;
 import android.view.View;
 import android.widget.OverScroller;
 
@@ -62,9 +63,10 @@
     private OverScroller mScroller;
     private boolean mHasUserInteraction;
     private PlayerGestureListener mGestureListener;
-    private PlayerFrameViewport mViewport;
     private PlayerFrameMediator mMediator;
     private PlayerFrameBitmapStateController mBitmapStateController;
+    private PlayerFrameScrollController mScrollController;
+    private PlayerFrameScaleController mScaleController;
 
     /**
      * Generate an UnguessableToken with a static value.
@@ -207,9 +209,14 @@
         mCompositorDelegate = new TestPlayerCompositorDelegate();
         mScroller = new OverScroller(ContextUtils.getApplicationContext());
         mGestureListener = new PlayerGestureListener(null, () -> mHasUserInteraction = true);
-        mViewport = new PlayerFrameViewport();
-        mMediator = new PlayerFrameMediator(mModel, mCompositorDelegate, mViewport, mScroller,
-                mGestureListener, mFrameGuid, CONTENT_WIDTH, CONTENT_HEIGHT, 0, 0);
+        Size contentSize = new Size(CONTENT_WIDTH, CONTENT_HEIGHT);
+        mMediator = new PlayerFrameMediator(
+                mModel, mCompositorDelegate, mGestureListener, mFrameGuid, contentSize, 0, 0);
+        mScaleController =
+                new PlayerFrameScaleController(mModel.get(PlayerFrameProperties.SCALE_MATRIX),
+                        mMediator, mGestureListener::onScale);
+        mScrollController = new PlayerFrameScrollController(
+                mScroller, mMediator, mGestureListener::onScroll, mGestureListener::onFling);
         mBitmapStateController = mMediator.getBitmapStateControllerForTest();
     }
 
@@ -305,7 +312,7 @@
                 new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f));
         Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
 
-        mMediator.scrollBy(10, 20);
+        mScrollController.scrollBy(10, 20);
         // The view port was moved with the #updateViewport call. It should've been updated in the
         // model.
         Rect expectedViewPort = new Rect(10, 20, 110, 220);
@@ -343,7 +350,7 @@
         // Move the view port slightly. It is still covered by the same 4 tiles. Since there were
         // already bitmap requests out for those tiles and their adjacent tiles, we shouldn't have
         // made new requests.
-        mMediator.scrollBy(10, 20);
+        mScrollController.scrollBy(10, 20);
         Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
 
         // Move the view port to the bottom right so it covers portions of the 4 bottom right bitmap
@@ -362,7 +369,7 @@
         // |   |   |   | 6 | 1 | 3 |
         // -------------------------
         // |   |   |   | 7 | 2 | 4 |
-        mMediator.scrollBy(430, 900);
+        mScrollController.scrollBy(430, 900);
         expectedViewPort.set(450, 940, 550, 1140);
         Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT));
 
@@ -418,7 +425,7 @@
         Assert.assertTrue(Arrays.deepEquals(
                 expectedRequiredBitmaps, getVisibleBitmapState().getRequiredBitmapsForTest()));
 
-        mMediator.scrollBy(10, 15);
+        mScrollController.scrollBy(10, 15);
         // The current viewport covers portions of the 4 top left bitmap tiles.
         // -------------------------
         // | x | x | x |   |   |   |
@@ -440,7 +447,7 @@
         Assert.assertTrue(Arrays.deepEquals(
                 expectedRequiredBitmaps, getVisibleBitmapState().getRequiredBitmapsForTest()));
 
-        mMediator.scrollBy(200, 400);
+        mScrollController.scrollBy(200, 400);
         // The current view port contains portions of the middle 4 tiles.
         // Tiles marked with x are required for the current view port.
         // -------------------------
@@ -474,7 +481,7 @@
         Assert.assertTrue(Arrays.deepEquals(
                 expectedRequiredBitmaps, getVisibleBitmapState().getRequiredBitmapsForTest()));
 
-        mMediator.scrollBy(200, 400);
+        mScrollController.scrollBy(200, 400);
         // The current view port contains portions of the 4 bottom right tiles.
         // Tiles marked with x are required for the current view port.
         // -------------------------
@@ -543,7 +550,7 @@
                 expectedBitmapMatrix, mModel.get(PlayerFrameProperties.BITMAP_MATRIX)));
 
         // Move the viewport to an area that is covered by 4 top left tiles.
-        mMediator.scrollBy(10, 10);
+        mScrollController.scrollBy(10, 10);
 
         // Scroll should've triggered bitmap requests for an the 4th new tile as well as adjacent
         // tiles. See comments on {@link #testBitmapRequest} for details on which tiles will be
@@ -568,7 +575,7 @@
         // Move the view port while staying within the 4 bitmap tiles in order to trigger the
         // request logic again. Make sure only one new request is added, for the tile with a
         // compositing failure.
-        mMediator.scrollBy(10, 10);
+        mScrollController.scrollBy(10, 10);
         Assert.assertEquals(9, mCompositorDelegate.mRequestedBitmap.size());
         Assert.assertEquals(new RequestedBitmap(mFrameGuid, getRectForTile(150, 200, 2, 0), 1f),
                 mCompositorDelegate.mRequestedBitmap.get(
@@ -587,7 +594,7 @@
 
         // Scroll right and down by a within bounds amount. Both scroll directions should be
         // effective.
-        Assert.assertTrue(mMediator.scrollBy(250f, 80f));
+        Assert.assertTrue(mScrollController.scrollBy(250f, 80f));
         expectedViewPort.offset(250, 80);
         Assert.assertEquals(expectedViewPort, mModel.get(PlayerFrameProperties.VIEWPORT));
     }
@@ -633,7 +640,7 @@
         Assert.assertEquals(expectedVisibility,
                 getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)));
 
-        mMediator.scrollBy(100, 0);
+        mScrollController.scrollBy(100, 0);
         expectedRects.set(0, new Rect(0, 0, 0, 0));
         expectedRects.set(1, new Rect(0, 0, 0, 0));
         expectedRects.set(2, new Rect(20, 35, 50, 65));
@@ -646,7 +653,7 @@
         Assert.assertEquals(expectedVisibility,
                 getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)));
 
-        mMediator.scrollBy(-50, 0);
+        mScrollController.scrollBy(-50, 0);
         expectedRects.clear();
         expectedRects.add(new Rect(-40, 20, 10, 120));
         expectedRects.add(new Rect(-20, 130, 20, 160));
@@ -660,7 +667,7 @@
         Assert.assertEquals(expectedVisibility,
                 getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)));
 
-        mMediator.scrollBy(0, 200);
+        mScrollController.scrollBy(0, 200);
         expectedRects.clear();
         expectedRects.add(new Rect(0, 0, 0, 0));
         expectedRects.add(new Rect(0, 0, 0, 0));
@@ -685,7 +692,7 @@
         mMediator.updateViewportSize(100, 200, 1f);
         Rect expectedViewPort = new Rect(0, 0, 100, 200);
 
-        mMediator.onFling(100, 0);
+        mScrollController.onFling(100, 0);
         expectedViewPort.offsetTo(mScroller.getFinalX(), mScroller.getFinalY());
         ShadowLooper.runUiThreadTasks();
         Assert.assertTrue(mScroller.isFinished());
@@ -709,12 +716,12 @@
 
         // Scroll, and then click. The call to {@link PlayerFrameMediator} must account for the
         // scroll offset.
-        mMediator.scrollBy(90, 100);
+        mScrollController.scrollBy(90, 100);
         mMediator.onTap(70, 50);
         expectedClickedPoints.add(new ClickedPoint(mFrameGuid, 160, 150));
         Assert.assertEquals(expectedClickedPoints, mCompositorDelegate.mClickedPoints);
 
-        mMediator.scrollBy(-40, -60);
+        mScrollController.scrollBy(-40, -60);
         mMediator.onTap(30, 80);
         expectedClickedPoints.add(new ClickedPoint(mFrameGuid, 80, 120));
         Assert.assertEquals(expectedClickedPoints, mCompositorDelegate.mClickedPoints);
@@ -756,8 +763,8 @@
 
         // Now a scale factor of 2 will be applied. This will happen at a focal point of 0, 0.
         // The same bitmaps will be required but the grid will be double the size.
-        Assert.assertTrue(mMediator.scaleBy(2f, 0, 0));
-        Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleBy(2f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleFinished(1f, 0, 0));
         mBitmapStateController.swapForTest();
 
         expectedRequiredBitmaps = new boolean[12][12];
@@ -768,8 +775,8 @@
                 expectedRequiredBitmaps, getVisibleBitmapState().getRequiredBitmapsForTest()));
 
         // Reduce the scale factor by 0.5 returning to a scale of 1.
-        Assert.assertTrue(mMediator.scaleBy(0.5f, 0, 0));
-        Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleBy(0.5f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleFinished(1f, 0, 0));
         mBitmapStateController.swapForTest();
 
         expectedRequiredBitmaps = new boolean[6][6];
@@ -818,7 +825,7 @@
                 new RequestedBitmap(mFrameGuid, getRectForTile(100, 200, 0, 1), 1f));
 
         // Both matricies should be identity to start.
-        assertViewportStateIs(1f, 0f, 0f, mViewport);
+        assertViewportStateIs(1f, 0f, 0f, mMediator.getViewport());
         Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
         // Ensure the correct bitmaps are required and requested.
         Assert.assertTrue(Arrays.deepEquals(
@@ -826,7 +833,7 @@
         Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
 
         // STEP 2: Scroll slightly.
-        mMediator.scrollBy(10, 15);
+        mScrollController.scrollBy(10, 15);
         // The current viewport covers portions of the 4 top left bitmap tiles.
         // -------------------------
         // | x | x | x |   |   |   |
@@ -868,18 +875,18 @@
         expectedViewportMatrixValues[Matrix.MTRANS_Y] = 15;
         expectedViewportMatrix.setValues(expectedViewportMatrixValues);
 
-        assertViewportStateIs(expectedViewportMatrix, mViewport);
+        assertViewportStateIs(expectedViewportMatrix, mMediator.getViewport());
         Assert.assertTrue(mModel.get(PlayerFrameProperties.SCALE_MATRIX).isIdentity());
 
         // STEP 3: Now a scale factor of 2 will be applied. This will happen at a focal point of 50,
         // 100.
-        Assert.assertTrue(mMediator.scaleBy(2f, 50f, 100f));
+        Assert.assertTrue(mScaleController.scaleBy(2f, 50f, 100f));
 
         // Before the scaling commits both matricies should update.
         expectedViewportMatrix.postScale(2f, 2f, -50f, -100f);
         Matrix expectedBitmapMatrix = new Matrix();
         expectedBitmapMatrix.postScale(2f, 2f, 50f, 100f);
-        assertViewportStateIs(expectedViewportMatrix, mViewport);
+        assertViewportStateIs(expectedViewportMatrix, mMediator.getViewport());
         Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
 
         // Bitmaps should be the same as before scaling until scaling is finished.
@@ -887,7 +894,7 @@
         mCompositorDelegate.mRequestedBitmap.clear();
         expectedRequestedBitmaps.clear();
 
-        Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleFinished(1f, 0, 0));
         mBitmapStateController.swapForTest();
 
         expectedRequiredBitmaps = new boolean[12][12];
@@ -926,12 +933,12 @@
 
         // STEP4: Now a scale factor of 0.5 will be applied. This will happen at a focal point of
         // 50, 100.
-        Assert.assertTrue(mMediator.scaleBy(0.5f, 50f, 100f));
+        Assert.assertTrue(mScaleController.scaleBy(0.5f, 50f, 100f));
 
         // Ensure the matricies are correct mid-scale.
         expectedViewportMatrix.postScale(0.5f, 0.5f, -50f, -100f);
         expectedBitmapMatrix.postScale(0.5f, 0.5f, 50f, 100f);
-        assertViewportStateIs(expectedViewportMatrix, mViewport);
+        assertViewportStateIs(expectedViewportMatrix, mMediator.getViewport());
         Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
 
         // Bitmaps should be the same as before scaling until scaling is finished.
@@ -939,7 +946,7 @@
         mCompositorDelegate.mRequestedBitmap.clear();
         expectedRequestedBitmaps.clear();
 
-        Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleFinished(1f, 0, 0));
         mBitmapStateController.swapForTest();
 
         expectedRequiredBitmaps = new boolean[6][6];
@@ -989,18 +996,18 @@
         // |   |   |   |   |   |   |
         // -------------------------
         // |   |   |   |   |   |   |
-        Assert.assertTrue(mMediator.scaleBy(2f, 100f, 200f));
+        Assert.assertTrue(mScaleController.scaleBy(2f, 100f, 200f));
 
         expectedViewportMatrix.postScale(2f, 2f, -100f, -200f);
         expectedBitmapMatrix.postScale(2f, 2f, 100f, 200f);
-        assertViewportStateIs(expectedViewportMatrix, mViewport);
+        assertViewportStateIs(expectedViewportMatrix, mMediator.getViewport());
         Assert.assertEquals(expectedBitmapMatrix, mModel.get(PlayerFrameProperties.SCALE_MATRIX));
 
         Assert.assertEquals(expectedRequestedBitmaps, mCompositorDelegate.mRequestedBitmap);
         mCompositorDelegate.mRequestedBitmap.clear();
         expectedRequestedBitmaps.clear();
 
-        Assert.assertTrue(mMediator.scaleFinished(1f, 0, 0));
+        Assert.assertTrue(mScaleController.scaleFinished(1f, 0, 0));
         mBitmapStateController.swapForTest();
 
         expectedRequiredBitmaps = new boolean[12][12];
@@ -1090,7 +1097,7 @@
         expectedVisibility.set(1, false);
 
         // During scaling the second subframe should disappear from the viewport.
-        Assert.assertTrue(mMediator.scaleBy(2f, 0f, 0f));
+        Assert.assertTrue(mScaleController.scaleBy(2f, 0f, 0f));
         Assert.assertEquals(expectedViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
         Assert.assertEquals(expectedRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
         Assert.assertEquals(expectedVisibility,
@@ -1100,7 +1107,7 @@
         inOrderMediator1.verify(subFrame1Mediator)
                 .setBitmapScaleMatrixOfSubframe(argThat(new MatrixMatcher(expectedMatrix)), eq(2f));
 
-        Assert.assertTrue(mMediator.scaleFinished(1f, 0f, 0f));
+        Assert.assertTrue(mScaleController.scaleFinished(1f, 0f, 0f));
         mBitmapStateController.swapForTest();
         inOrderMediator1.verify(subFrame1Mediator).resetScaleFactor();
         inOrderMediator1.verify(subFrame1Mediator).forceRedraw();
@@ -1113,7 +1120,7 @@
                 .setBitmapScaleMatrixOfSubframe(argThat(new MatrixMatcher(expectedMatrix)), eq(1f));
 
         // Scroll so the second subframe is back in the viewport..
-        mMediator.scrollBy(20, 40);
+        mScrollController.scrollBy(20, 40);
         expectedRects.clear();
         expectedRects.add(new Rect(0, 0, 100, 40));
         expectedRects.add(new Rect(40, 60, 120, 280));
@@ -1126,7 +1133,7 @@
                 getVisibilities(mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS)));
 
         // Scale out keeping the subframes in the viewport..
-        Assert.assertTrue(mMediator.scaleBy(0.75f, 25f, 50f));
+        Assert.assertTrue(mScaleController.scaleBy(0.75f, 25f, 50f));
         expectedRects.clear();
         expectedRects.add(new Rect(6, 12, 81, 42));
         expectedRects.add(new Rect(36, 57, 96, 222));
@@ -1210,15 +1217,32 @@
 
         Assert.assertFalse(
                 "User interaction callback shouldn't have been called", mHasUserInteraction);
-        mMediator.scrollBy(0, 10);
+        mScrollController.scrollBy(0, 10);
         Assert.assertTrue("User interaction callback should have been called", mHasUserInteraction);
 
         mHasUserInteraction = false;
-        mMediator.onFling(0, 10);
+        mScrollController.onFling(0, 10);
         Assert.assertTrue("User interaction callback should have been called", mHasUserInteraction);
 
         mHasUserInteraction = false;
-        mMediator.scaleBy(1.5f, 20, 30);
+        mScaleController.scaleBy(1.5f, 20, 30);
         Assert.assertTrue("User interaction callback should have been called", mHasUserInteraction);
     }
+
+    /**
+     * Tests that bitmap matrix is offset correctly.
+     */
+    @Test
+    public void testOffsetBitmapScaleMatrix() {
+        Matrix bitmapScaleMatrix = mModel.get(PlayerFrameProperties.SCALE_MATRIX);
+        Matrix expectedBitmapScaleMatrix = new Matrix();
+        mMediator.offsetBitmapScaleMatrix(5f, 10f); // Should no-op.
+        Assert.assertEquals(expectedBitmapScaleMatrix, bitmapScaleMatrix);
+
+        bitmapScaleMatrix.postScale(2f, 2f);
+        mMediator.offsetBitmapScaleMatrix(5f, 10f);
+        expectedBitmapScaleMatrix.postScale(2f, 2f);
+        expectedBitmapScaleMatrix.postTranslate(-5f, -10f);
+        Assert.assertEquals(expectedBitmapScaleMatrix, bitmapScaleMatrix);
+    }
 }
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleControllerTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleControllerTest.java
index a47e217..0b18b48 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleControllerTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScaleControllerTest.java
@@ -7,6 +7,7 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.when;
 
 import android.graphics.Matrix;
 import android.util.Size;
@@ -59,30 +60,17 @@
         Callback<Boolean> mScaleListener = (Boolean didFinish) -> mDidScale = true;
         mViewport = new PlayerFrameViewport();
         mBitmapScaleMatrix = new Matrix();
-        mScaleController =
-                new PlayerFrameScaleController(mViewport, new Size(CONTENT_WIDTH, CONTENT_HEIGHT),
-                        mBitmapScaleMatrix, mMediatorDelegateMock, mScaleListener);
-        mScaleController.calculateInitialScaleFactor(CONTENT_WIDTH);
-        mViewport.setScale(mScaleController.getInitialScaleFactor());
+        Size contentSize = new Size(CONTENT_WIDTH, CONTENT_HEIGHT);
+        when(mMediatorDelegateMock.getViewport()).thenReturn(mViewport);
+        when(mMediatorDelegateMock.getContentSize()).thenReturn(contentSize);
+        when(mMediatorDelegateMock.getInitialScaleFactor()).thenReturn(1f);
+        mScaleController = new PlayerFrameScaleController(
+                mBitmapScaleMatrix, mMediatorDelegateMock, mScaleListener);
+        mViewport.setScale(1f);
         mViewport.setSize(100, 100);
     }
 
     /**
-     * Tests calculating and getting the initial scale factor.
-     */
-    @Test
-    public void testInitialScaleFactor() {
-        mScaleController.calculateInitialScaleFactor(CONTENT_WIDTH);
-        Assert.assertEquals(1f, mScaleController.getInitialScaleFactor(), TOLERANCE);
-
-        mScaleController.calculateInitialScaleFactor(250);
-        Assert.assertEquals(0.5f, mScaleController.getInitialScaleFactor(), TOLERANCE);
-
-        mScaleController.calculateInitialScaleFactor(1000);
-        Assert.assertEquals(2f, mScaleController.getInitialScaleFactor(), TOLERANCE);
-    }
-
-    /**
      * Tests the limits of scaling.
      */
     @Test
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollControllerTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollControllerTest.java
index 5e076f8..7f547d3 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollControllerTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameScrollControllerTest.java
@@ -56,9 +56,11 @@
         Runnable mOnScrollListener = () -> mDidScroll = true;
         Runnable mOnFlingListener = () -> mDidFling = true;
         mViewport = new PlayerFrameViewport();
-        mScrollController = new PlayerFrameScrollController(mScroller, mViewport,
-                new Size(CONTENT_WIDTH, CONTENT_HEIGHT), mMediatorDelegateMock, mOnScrollListener,
-                mOnFlingListener);
+        when(mMediatorDelegateMock.getViewport()).thenReturn(mViewport);
+        when(mMediatorDelegateMock.getContentSize())
+                .thenReturn(new Size(CONTENT_WIDTH, CONTENT_HEIGHT));
+        mScrollController = new PlayerFrameScrollController(
+                mScroller, mMediatorDelegateMock, mOnScrollListener, mOnFlingListener);
     }
 
     /**
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index 9f7b5ff..26ebe0a4 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -98,6 +98,7 @@
   MOCK_METHOD1(AddFieldInfoImpl, void(const FieldInfo&));
   MOCK_METHOD0(GetAllFieldInfoImpl, std::vector<FieldInfo>());
   MOCK_METHOD2(RemoveFieldInfoByTimeImpl, void(base::Time, base::Time));
+  MOCK_METHOD0(IsEmpty, bool());
 
   MOCK_CONST_METHOD0(IsAbleToSavePasswords, bool());
 
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 3eed71e..8b08c01 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -465,6 +465,12 @@
                               std::move(completion)));
 }
 
+void PasswordStore::ClearStore(base::OnceCallback<void(bool)> completion) {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  ScheduleTask(base::BindOnce(&PasswordStore::ClearStoreInternal, this,
+                              std::move(completion)));
+}
+
 void PasswordStore::AddObserver(Observer* observer) {
   observers_->AddObserver(observer);
 }
@@ -1093,6 +1099,18 @@
     main_task_runner_->PostTask(FROM_HERE, std::move(completion));
 }
 
+void PasswordStore::ClearStoreInternal(
+    base::OnceCallback<void(bool)> completion) {
+  DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
+  bool should_clear = !IsEmpty();
+  if (should_clear)
+    DeleteAndRecreateDatabaseFile();
+  if (completion) {
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(completion), should_clear));
+  }
+}
+
 std::vector<std::unique_ptr<autofill::PasswordForm>>
 PasswordStore::GetLoginsImpl(const FormDigest& form) {
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index 16e1398..30b15f266 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -323,6 +323,12 @@
                              base::Time remove_end,
                              base::OnceClosure completion);
 
+  // Deletes and re-creates the whole PasswordStore, unless it is already empty
+  // anyway. If |completion| is not null, it will be posted to the
+  // |main_task_runner_| once the process is complete. The bool parameter
+  // indicates whether any data was actually cleared.
+  void ClearStore(base::OnceCallback<void(bool)> completion);
+
   // Adds an observer to be notified when the password store data changes.
   void AddObserver(Observer* observer);
 
@@ -568,6 +574,10 @@
   virtual void RemoveFieldInfoByTimeImpl(base::Time remove_begin,
                                          base::Time remove_end) = 0;
 
+  // Synchronous implementation provided by subclasses to check whether the
+  // store is empty.
+  virtual bool IsEmpty() = 0;
+
   // PasswordStoreSync:
   PasswordStoreChangeList AddLoginSync(const autofill::PasswordForm& form,
                                        AddLoginError* error) override;
@@ -735,6 +745,8 @@
                                      base::Time remove_end,
                                      base::OnceClosure completion);
 
+  void ClearStoreInternal(base::OnceCallback<void(bool)> completion);
+
   // Finds all PasswordForms with a signon_realm that is equal to, or is a
   // PSL-match to that of |form|, and takes care of notifying the consumer with
   // the results when done.
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index bd79718..132a72d 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -296,6 +296,12 @@
     login_db_->field_info_table().RemoveRowsByTime(remove_begin, remove_end);
 }
 
+bool PasswordStoreDefault::IsEmpty() {
+  if (!login_db_)
+    return true;
+  return login_db_->IsEmpty();
+}
+
 bool PasswordStoreDefault::BeginTransaction() {
   if (login_db_)
     return login_db_->BeginTransaction();
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index b77d42e..ee727a69 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -98,6 +98,8 @@
   void RemoveFieldInfoByTimeImpl(base::Time remove_begin,
                                  base::Time remove_end) override;
 
+  bool IsEmpty() override;
+
   // Implements PasswordStoreSync interface.
   bool BeginTransaction() override;
   void RollbackTransaction() override;
diff --git a/components/password_manager/core/browser/sync/password_model_type_controller.cc b/components/password_manager/core/browser/sync/password_model_type_controller.cc
index de95ab75..ba1f9c74 100644
--- a/components/password_manager/core/browser/sync/password_model_type_controller.cc
+++ b/components/password_manager/core/browser/sync/password_model_type_controller.cc
@@ -6,7 +6,11 @@
 
 #include <utility>
 
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/password_manager/core/browser/password_manager_features_util.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
 #include "components/sync/base/model_type.h"
@@ -16,11 +20,36 @@
 
 namespace password_manager {
 
+namespace {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ClearedOnStartup {
+  kOptedInSoNoNeedToClear = 0,
+  kNotOptedInAndWasAlreadyEmpty = 1,
+  kNotOptedInAndHadToClear = 2,
+  kMaxValue = kNotOptedInAndHadToClear
+};
+
+void RecordClearedOnStartup(ClearedOnStartup state) {
+  base::UmaHistogramEnumeration(
+      "PasswordManager.AccountStorage.ClearedOnStartup", state);
+}
+
+void PasswordStoreClearDone(bool cleared) {
+  RecordClearedOnStartup(cleared
+                             ? ClearedOnStartup::kNotOptedInAndHadToClear
+                             : ClearedOnStartup::kNotOptedInAndWasAlreadyEmpty);
+}
+
+}  // namespace
+
 PasswordModelTypeController::PasswordModelTypeController(
     std::unique_ptr<syncer::ModelTypeControllerDelegate>
         delegate_for_full_sync_mode,
     std::unique_ptr<syncer::ModelTypeControllerDelegate>
         delegate_for_transport_mode,
+    scoped_refptr<PasswordStore> account_password_store_for_cleanup,
     PrefService* pref_service,
     signin::IdentityManager* identity_manager,
     syncer::SyncService* sync_service,
@@ -39,6 +68,22 @@
               &PasswordModelTypeController::OnOptInStateMaybeChanged,
               base::Unretained(this))) {
   identity_manager_->AddObserver(this);
+
+  if (!base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage)) {
+    DCHECK(!account_password_store_for_cleanup);
+    // TODO(crbug.com/1079297): Delete the database file (if it exists); see
+    // password_store_factory_util.h.
+  } else {
+    DCHECK(account_password_store_for_cleanup);
+    // Note: Right now, we're still in the middle of SyncService initialization,
+    // so we can't check IsOptedInForAccountStorage() yet (SyncService might not
+    // have determined the syncing account yet). Post a task do to it after the
+    // initialization is complete.
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&PasswordModelTypeController::MaybeClearStore,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  account_password_store_for_cleanup));
+  }
 }
 
 PasswordModelTypeController::~PasswordModelTypeController() {
@@ -142,4 +187,15 @@
   sync_service_->DataTypePreconditionChanged(syncer::PASSWORDS);
 }
 
+void PasswordModelTypeController::MaybeClearStore(
+    scoped_refptr<PasswordStore> account_password_store_for_cleanup) {
+  DCHECK(account_password_store_for_cleanup);
+  if (features_util::IsOptedInForAccountStorage(pref_service_, sync_service_)) {
+    RecordClearedOnStartup(ClearedOnStartup::kOptedInSoNoNeedToClear);
+  } else {
+    account_password_store_for_cleanup->ClearStore(
+        base::BindOnce(&PasswordStoreClearDone));
+  }
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/sync/password_model_type_controller.h b/components/password_manager/core/browser/sync/password_model_type_controller.h
index e3bfb46..f535438 100644
--- a/components/password_manager/core/browser/sync/password_model_type_controller.h
+++ b/components/password_manager/core/browser/sync/password_model_type_controller.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "components/password_manager/core/browser/password_account_storage_settings_watcher.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/sync/driver/model_type_controller.h"
@@ -23,6 +24,8 @@
 
 namespace password_manager {
 
+class PasswordStore;
+
 // A class that manages the startup and shutdown of password sync.
 class PasswordModelTypeController : public syncer::ModelTypeController,
                                     public syncer::SyncServiceObserver,
@@ -33,6 +36,7 @@
           delegate_for_full_sync_mode,
       std::unique_ptr<syncer::ModelTypeControllerDelegate>
           delegate_for_transport_mode,
+      scoped_refptr<PasswordStore> account_password_store_for_cleanup,
       PrefService* pref_service,
       signin::IdentityManager* identity_manager,
       syncer::SyncService* sync_service,
@@ -60,6 +64,9 @@
  private:
   void OnOptInStateMaybeChanged();
 
+  void MaybeClearStore(
+      scoped_refptr<PasswordStore> account_password_store_for_cleanup);
+
   PrefService* const pref_service_;
   signin::IdentityManager* const identity_manager_;
   syncer::SyncService* const sync_service_;
@@ -70,6 +77,8 @@
   // Passed in to LoadModels(), and cached here for later use in Stop().
   syncer::SyncMode sync_mode_ = syncer::SyncMode::kFull;
 
+  base::WeakPtrFactory<PasswordModelTypeController> weak_ptr_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(PasswordModelTypeController);
 };
 
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index f467fd5..dd32383 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -125,7 +125,7 @@
   stored_passwords_.clear();
 }
 
-bool TestPasswordStore::IsEmpty() const {
+bool TestPasswordStore::IsEmpty() {
   // The store is empty, if the sum of all stored passwords across all entries
   // in |stored_passwords_| is 0.
   size_t number_of_passwords = 0u;
diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h
index 672f01e..199fbbd1 100644
--- a/components/password_manager/core/browser/test_password_store.h
+++ b/components/password_manager/core/browser/test_password_store.h
@@ -64,7 +64,7 @@
   // Returns true if no passwords are stored in the store. Note that this is not
   // as simple as asking whether stored_passwords().empty(), because the map can
   // have entries of size 0.
-  bool IsEmpty() const;
+  bool IsEmpty() override;
 
   int fill_matching_logins_calls() const { return fill_matching_logins_calls_; }
 
diff --git a/components/password_manager/core/browser/ui/compromised_credentials_manager.cc b/components/password_manager/core/browser/ui/compromised_credentials_manager.cc
index e2d9205..b51497ff 100644
--- a/components/password_manager/core/browser/ui/compromised_credentials_manager.cc
+++ b/components/password_manager/core/browser/ui/compromised_credentials_manager.cc
@@ -120,6 +120,29 @@
 
 }  // namespace
 
+CredentialView::CredentialView(std::string signon_realm,
+                               GURL url,
+                               base::string16 username,
+                               base::string16 password)
+    : signon_realm(std::move(signon_realm)),
+      url(std::move(url)),
+      username(std::move(username)),
+      password(std::move(password)) {}
+
+CredentialView::CredentialView(const autofill::PasswordForm& form)
+    : signon_realm(form.signon_realm),
+      url(form.url),
+      username(form.username_value),
+      password(form.password_value) {}
+
+CredentialView::CredentialView(const CredentialView& credential) = default;
+CredentialView::CredentialView(CredentialView&& credential) = default;
+CredentialView& CredentialView::operator=(const CredentialView& credential) =
+    default;
+CredentialView& CredentialView::operator=(CredentialView&& credential) =
+    default;
+CredentialView::~CredentialView() = default;
+
 CredentialWithPassword::CredentialWithPassword(const CredentialView& credential)
     : CredentialView(std::move(credential)) {}
 CredentialWithPassword::~CredentialWithPassword() = default;
@@ -129,12 +152,13 @@
 CredentialWithPassword::CredentialWithPassword(CredentialWithPassword&& other) =
     default;
 CredentialWithPassword::CredentialWithPassword(
-    const CompromisedCredentials& credential) {
-  username = credential.username;
-  signon_realm = credential.signon_realm;
-  create_time = credential.create_time;
-  compromise_type = ConvertCompromiseType(credential.compromise_type);
-}
+    const CompromisedCredentials& credential)
+    : CredentialView(credential.signon_realm,
+                     GURL(credential.signon_realm),
+                     credential.username,
+                     /*password=*/{}),
+      create_time(credential.create_time),
+      compromise_type(ConvertCompromiseType(credential.compromise_type)) {}
 
 CredentialWithPassword& CredentialWithPassword::operator=(
     const CredentialWithPassword& other) = default;
diff --git a/components/password_manager/core/browser/ui/compromised_credentials_manager.h b/components/password_manager/core/browser/ui/compromised_credentials_manager.h
index a5578e7..b3ffb9f 100644
--- a/components/password_manager/core/browser/ui/compromised_credentials_manager.h
+++ b/components/password_manager/core/browser/ui/compromised_credentials_manager.h
@@ -43,14 +43,20 @@
 // Simple struct that augments key values of CompromisedCredentials and a
 // password.
 struct CredentialView {
-  CredentialView() = default;
-  // Enable explicit construction from the autofill::PasswordForm
-  explicit CredentialView(const autofill::PasswordForm& form)
-      : signon_realm(form.signon_realm),
-        username(form.username_value),
-        password(form.password_value) {}
+  CredentialView(std::string signon_realm,
+                 GURL url,
+                 base::string16 username,
+                 base::string16 password);
+  // Enable explicit construction from autofill::PasswordForm for convenience.
+  explicit CredentialView(const autofill::PasswordForm& form);
+  CredentialView(const CredentialView& credential);
+  CredentialView(CredentialView&& credential);
+  CredentialView& operator=(const CredentialView& credential);
+  CredentialView& operator=(CredentialView&& credential);
+  ~CredentialView();
 
   std::string signon_realm;
+  GURL url;
   base::string16 username;
   base::string16 password;
 };
diff --git a/components/permissions/permission_context_base.cc b/components/permissions/permission_context_base.cc
index 12f8a94..5af28eae 100644
--- a/components/permissions/permission_context_base.cc
+++ b/components/permissions/permission_context_base.cc
@@ -385,7 +385,17 @@
           .insert(std::make_pair(id.ToString(), std::move(request_ptr)))
           .second;
   DCHECK(inserted) << "Duplicate id " << id.ToString();
-  permission_request_manager->AddRequest(request);
+
+  content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
+      id.render_process_id(), id.render_frame_id());
+
+  if (!rfh) {
+    request->Cancelled();
+    request->RequestFinished();
+    return;
+  }
+
+  permission_request_manager->AddRequest(rfh, request);
 }
 
 void PermissionContextBase::PermissionDecided(
diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc
index 60234f3..1303df1 100644
--- a/components/permissions/permission_request_manager.cc
+++ b/components/permissions/permission_request_manager.cc
@@ -99,7 +99,13 @@
   DCHECK(queued_requests_.empty());
 }
 
-void PermissionRequestManager::AddRequest(PermissionRequest* request) {
+void PermissionRequestManager::AddRequest(
+    content::RenderFrameHost* source_frame,
+    PermissionRequest* request) {
+  DCHECK(source_frame);
+  DCHECK_EQ(content::WebContents::FromRenderFrameHost(source_frame),
+            web_contents());
+
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDenyPermissionPrompts)) {
     request->PermissionDenied();
diff --git a/components/permissions/permission_request_manager.h b/components/permissions/permission_request_manager.h
index 158214c..53bfc17 100644
--- a/components/permissions/permission_request_manager.h
+++ b/components/permissions/permission_request_manager.h
@@ -18,6 +18,10 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
+namespace content {
+class RenderFrameHost;
+}
+
 namespace test {
 class PermissionRequestManagerTestApi;
 }
@@ -84,7 +88,8 @@
   // bubble closes. A request with message text identical to an outstanding
   // request will be merged with the outstanding request, and will have the same
   // callbacks called as the outstanding request.
-  void AddRequest(PermissionRequest* request);
+  void AddRequest(content::RenderFrameHost* source_frame,
+                  PermissionRequest* request);
 
   // Will reposition the bubble (may change parent if necessary).
   void UpdateAnchorPosition();
diff --git a/components/permissions/permission_request_manager_unittest.cc b/components/permissions/permission_request_manager_unittest.cc
index 2c92c224..9c2e28c 100644
--- a/components/permissions/permission_request_manager_unittest.cc
+++ b/components/permissions/permission_request_manager_unittest.cc
@@ -135,7 +135,7 @@
 };
 
 TEST_F(PermissionRequestManagerTest, SingleRequest) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -146,7 +146,7 @@
 }
 
 TEST_F(PermissionRequestManagerTest, SingleRequestViewFirst) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -158,8 +158,8 @@
 
 // Most requests should never be grouped.
 TEST_F(PermissionRequestManagerTest, TwoRequestsUngrouped) {
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
 
   WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -176,8 +176,8 @@
 
 // Only mic/camera requests from the same origin should be grouped.
 TEST_F(PermissionRequestManagerTest, MicCameraGrouped) {
-  manager_->AddRequest(&request_mic_);
-  manager_->AddRequest(&request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -188,8 +188,9 @@
   EXPECT_TRUE(request_camera_.granted());
 
   // If the requests come from different origins, they should not be grouped.
-  manager_->AddRequest(&iframe_request_mic_other_domain_);
-  manager_->AddRequest(&request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_mic_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -198,8 +199,8 @@
 
 // Only camera/ptz requests from the same origin should be grouped.
 TEST_F(PermissionRequestManagerTest, CameraPtzGrouped) {
-  manager_->AddRequest(&request_camera_);
-  manager_->AddRequest(&request_ptz_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -210,8 +211,9 @@
   EXPECT_TRUE(request_ptz_.granted());
 
   // If the requests come from different origins, they should not be grouped.
-  manager_->AddRequest(&iframe_request_camera_other_domain_);
-  manager_->AddRequest(&request_ptz_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_camera_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -220,9 +222,9 @@
 
 // Only mic/camera/ptz requests from the same origin should be grouped.
 TEST_F(PermissionRequestManagerTest, MicCameraPtzGrouped) {
-  manager_->AddRequest(&request_mic_);
-  manager_->AddRequest(&request_camera_);
-  manager_->AddRequest(&request_ptz_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -234,9 +236,10 @@
   EXPECT_TRUE(request_ptz_.granted());
 
   // If the requests come from different origins, they should not be grouped.
-  manager_->AddRequest(&iframe_request_mic_other_domain_);
-  manager_->AddRequest(&request_camera_);
-  manager_->AddRequest(&request_ptz_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_mic_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -244,8 +247,8 @@
 }
 
 TEST_F(PermissionRequestManagerTest, TwoRequestsTabSwitch) {
-  manager_->AddRequest(&request_mic_);
-  manager_->AddRequest(&request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -269,9 +272,9 @@
 }
 
 TEST_F(PermissionRequestManagerTest, ThreeRequestsTabSwitch) {
-  manager_->AddRequest(&request_mic_);
-  manager_->AddRequest(&request_camera_);
-  manager_->AddRequest(&request_ptz_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_mic_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_camera_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request_ptz_);
   WaitForBubbleToBeShown();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -302,7 +305,7 @@
 
 TEST_F(PermissionRequestManagerTest, PermissionRequestWhileTabSwitchedAway) {
   MockTabSwitchAway();
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(prompt_factory_->is_visible());
 
@@ -312,18 +315,18 @@
 }
 
 TEST_F(PermissionRequestManagerTest, TwoRequestsDoNotCoalesce) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
 }
 
 TEST_F(PermissionRequestManagerTest, TwoRequestsShownInTwoBubbles) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
@@ -337,8 +340,8 @@
 }
 
 TEST_F(PermissionRequestManagerTest, TestAddDuplicateRequest) {
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
 
   WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -346,7 +349,7 @@
 }
 
 TEST_F(PermissionRequestManagerTest, SequentialRequests) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
 
@@ -355,7 +358,7 @@
 
   EXPECT_FALSE(prompt_factory_->is_visible());
 
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
   Accept();
@@ -364,8 +367,8 @@
 }
 
 TEST_F(PermissionRequestManagerTest, SameRequestRejected) {
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   EXPECT_FALSE(request1_.finished());
 
   WaitForBubbleToBeShown();
@@ -374,17 +377,17 @@
 }
 
 TEST_F(PermissionRequestManagerTest, DuplicateQueuedRequest) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
 
   MockPermissionRequest dupe_request("test1");
-  manager_->AddRequest(&dupe_request);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &dupe_request);
   EXPECT_FALSE(dupe_request.finished());
   EXPECT_FALSE(request1_.finished());
 
   MockPermissionRequest dupe_request2("test2");
-  manager_->AddRequest(&dupe_request2);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &dupe_request2);
   EXPECT_FALSE(dupe_request2.finished());
   EXPECT_FALSE(request2_.finished());
 
@@ -399,10 +402,11 @@
 }
 
 TEST_F(PermissionRequestManagerTest, ForgetRequestsOnPageNavigation) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
-  manager_->AddRequest(&request2_);
-  manager_->AddRequest(&iframe_request_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_other_domain_);
 
   EXPECT_TRUE(prompt_factory_->is_visible());
   ASSERT_EQ(prompt_factory_->request_count(), 1);
@@ -417,7 +421,8 @@
 }
 
 TEST_F(PermissionRequestManagerTest, MainFrameNoRequestIFrameRequest) {
-  manager_->AddRequest(&iframe_request_same_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_same_domain_);
   WaitForBubbleToBeShown();
   WaitForFrameLoad();
 
@@ -427,8 +432,9 @@
 }
 
 TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestSameDomain) {
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&iframe_request_same_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_same_domain_);
   WaitForFrameLoad();
   WaitForBubbleToBeShown();
 
@@ -446,8 +452,9 @@
 }
 
 TEST_F(PermissionRequestManagerTest, MainFrameAndIFrameRequestOtherDomain) {
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&iframe_request_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_other_domain_);
   WaitForFrameLoad();
   WaitForBubbleToBeShown();
 
@@ -461,11 +468,12 @@
 }
 
 TEST_F(PermissionRequestManagerTest, IFrameRequestWhenMainRequestVisible) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
 
-  manager_->AddRequest(&iframe_request_same_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_same_domain_);
   WaitForFrameLoad();
   ASSERT_EQ(prompt_factory_->request_count(), 1);
   Closing();
@@ -479,11 +487,12 @@
 
 TEST_F(PermissionRequestManagerTest,
        IFrameRequestOtherDomainWhenMainRequestVisible) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(prompt_factory_->is_visible());
 
-  manager_->AddRequest(&iframe_request_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_other_domain_);
   WaitForFrameLoad();
   Closing();
   EXPECT_TRUE(request1_.finished());
@@ -496,9 +505,10 @@
 TEST_F(PermissionRequestManagerTest, RequestsDontNeedUserGesture) {
   WaitForFrameLoad();
   WaitForBubbleToBeShown();
-  manager_->AddRequest(&request1_);
-  manager_->AddRequest(&iframe_request_other_domain_);
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(),
+                       &iframe_request_other_domain_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(prompt_factory_->is_visible());
@@ -510,7 +520,7 @@
 TEST_F(PermissionRequestManagerTest, UMAForSimpleDeniedBubbleAlternatePath) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   // No need to test UMA for showing prompts again, they were tested in
   // UMAForSimpleAcceptedBubble.
@@ -525,7 +535,7 @@
 TEST_F(PermissionRequestManagerTest, UMAForTabSwitching) {
   base::HistogramTester histograms;
 
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   histograms.ExpectUniqueSample(
       PermissionUmaUtil::kPermissionsPromptShown,
@@ -584,7 +594,7 @@
         NotificationPermissionUiSelector::QuietUiReason::kEnabledInPrefs,
         false /* async */);
 
-    manager_->AddRequest(request);
+    manager_->AddRequest(web_contents()->GetMainFrame(), request);
     WaitForBubbleToBeShown();
 
     ASSERT_TRUE(prompt_factory_->is_visible());
@@ -617,7 +627,7 @@
         "foo", PermissionRequestType::PERMISSION_NOTIFICATIONS,
         PermissionRequestGestureType::GESTURE);
 
-    manager_->AddRequest(&request);
+    manager_->AddRequest(web_contents()->GetMainFrame(), &request);
     WaitForBubbleToBeShown();
 
     EXPECT_TRUE(prompt_factory_->is_visible());
@@ -639,7 +649,7 @@
   MockPermissionRequest request1(
       "request1", PermissionRequestType::PERMISSION_NOTIFICATIONS,
       PermissionRequestGestureType::GESTURE);
-  manager_->AddRequest(&request1);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1);
   WaitForBubbleToBeShown();
   EXPECT_TRUE(manager_->ShouldCurrentRequestUseQuietUI());
   Accept();
@@ -650,21 +660,21 @@
   MockNotificationPermissionUiSelector::CreateForManager(
       manager_, NotificationPermissionUiSelector::Decision::UseNormalUi(),
       true);
-  manager_->AddRequest(&request2);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2);
   WaitForBubbleToBeShown();
   EXPECT_FALSE(manager_->ShouldCurrentRequestUseQuietUI());
   Accept();
 }
 
 TEST_F(PermissionRequestManagerTest, RequestsNotSupported) {
-  manager_->AddRequest(&request1_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request1_);
   WaitForBubbleToBeShown();
   Accept();
   EXPECT_TRUE(request1_.granted());
 
   manager_->set_web_contents_supports_permission_requests(false);
 
-  manager_->AddRequest(&request2_);
+  manager_->AddRequest(web_contents()->GetMainFrame(), &request2_);
   EXPECT_TRUE(request2_.cancelled());
 }
 }  // namespace permissions
diff --git a/components/permissions/quota_permission_context_impl.cc b/components/permissions/quota_permission_context_impl.cc
index a9778e0..2447b79 100644
--- a/components/permissions/quota_permission_context_impl.cc
+++ b/components/permissions/quota_permission_context_impl.cc
@@ -38,16 +38,6 @@
 // different message to the user.
 const int64_t kRequestLargeQuotaThreshold = 5 * 1024 * 1024;
 
-// TODO(sky): move this to content and remove the one in tab_util.
-content::WebContents* GetWebContentsByFrameID(int render_process_id,
-                                              int render_frame_id) {
-  content::RenderFrameHost* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
-  if (!render_frame_host)
-    return nullptr;
-  return content::WebContents::FromRenderFrameHost(render_frame_host);
-}
-
 // QuotaPermissionRequest ---------------------------------------------
 
 class QuotaPermissionRequest : public PermissionRequest {
@@ -177,9 +167,10 @@
     return;
   }
 
-  content::WebContents* web_contents =
-      GetWebContentsByFrameID(render_process_id, params.render_frame_id);
-  if (!web_contents) {
+  content::RenderFrameHost* render_frame_host =
+      content::RenderFrameHost::FromID(render_process_id,
+                                       params.render_frame_id);
+  if (!render_frame_host) {
     // The tab may have gone away or the request may not be from a tab.
     LOG(WARNING) << "Attempt to request quota tabless renderer: "
                  << render_process_id << "," << params.render_frame_id;
@@ -189,12 +180,15 @@
   }
 
   PermissionRequestManager* permission_request_manager =
-      PermissionRequestManager::FromWebContents(web_contents);
+      PermissionRequestManager::FromWebContents(
+          content::WebContents::FromRenderFrameHost(render_frame_host));
   if (permission_request_manager) {
     bool is_large_quota_request =
         params.requested_size > kRequestLargeQuotaThreshold;
-    permission_request_manager->AddRequest(new QuotaPermissionRequest(
-        this, params.origin_url, is_large_quota_request, std::move(callback)));
+    permission_request_manager->AddRequest(
+        render_frame_host, new QuotaPermissionRequest(this, params.origin_url,
+                                                      is_large_quota_request,
+                                                      std::move(callback)));
     return;
   }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index be7f3eb..075c531 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -3568,23 +3568,21 @@
       'id': 371,
       'caption': '''Allow download restrictions''',
       'tags': ['local-data-access'],
-      'desc': '''Configures the type of downloads that <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will completely block, without letting users override the security decision.
+      'desc': '''Setting the policy means users can't bypass download security decisions.
 
-      If you set this policy, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will prevent certain types of downloads, and won't let user bypass the security warnings.
+       Setting the policy to:
 
-      When the 'Block dangerous downloads' option is chosen, all downloads are allowed, except for those that carry Safe Browsing warnings.
+      * Block dangerous downloads means all downloads are allowed, except for those that carry Safe Browsing warnings.
 
-      When the 'Block potentially dangerous downloads' option is chosen, all downloads allowed, except for those that carry Safe Browsing warnings of potentially dangerous downloads.
+      * Block potentially dangerous downloads means all downloads allowed, except for those that carry Safe Browsing warnings of potentially dangerous downloads.
 
-      When the 'Block all downloads' option is chosen, all downloads are blocked.
+      * Block all downloads means all downloads are blocked.
 
-      When the 'Block malicious downloads' option is chosen, all downloads are allowed, except for those that Safe Browsing assesses to be malware with high confidence. Unlike with dangerous downloads, this does not take into account file type, but does take into account the host.
+      * Block malicious downloads means all downloads are allowed, except for those that Safe Browsing assesses to be malware with high confidence. Unlike with dangerous downloads, this does not take into account file type, but does take into account the host.
 
-      When this policy is not set, (or the 'No special restrictions' option is chosen), the downloads will go through the usual security restrictions based on Safe Browsing analysis results.
+      * No special restrictions means the downloads go through the usual security restrictions based on Safe Browsing analysis results.
 
-      Note that these restrictions apply to downloads triggered from web page content, as well as the 'download link...' context menu option. These restrictions do not apply to the save / download of the currently displayed page, nor does it apply to saving as PDF from the printing options.
-
-      See https://developers.google.com/safe-browsing for more info on Safe Browsing.''',
+      Note: These restrictions apply to downloads triggered from webpage content, as well as the Download link... menu option. They don't apply to the download of the currently displayed page or to saving as PDF from the printing options. Read more about Safe Browsing ( https://developers.google.com/safe-browsing ).''',
       'label': '''Download restrictions''',
     },
     {
@@ -3602,13 +3600,11 @@
       'id': 64,
       'caption': '''Set download directory''',
       'tags': ['local-data-access'],
-      'desc': '''Configures the directory that <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use for downloading files.
+      'desc': '''Setting the policy sets up the directory Chrome uses for downloading files. It uses the provided directory, whether or not users specify one or turned on the flag to be prompted for download location every time.
 
-      If you set this policy, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use the provided directory regardless whether the user has specified one or enabled the flag to be prompted for download location every time.
+      Leaving the policy unset means Chrome uses the default download directory, and users can change it.
 
-      See https://www.chromium.org/administrators/policy-list-3/user-data-directory-variables for a list of variables that can be used.
-
-      If this policy is left not set the default download directory will be used and the user will be able to change it.''',
+      Note: See a list of variables you can use ( https://www.chromium.org/administrators/policy-list-3/user-data-directory-variables ).''',
       'label': '''Set download directory''',
       'arc_support': 'This policy has no effect on Android apps. Android apps always use the default downloads directory and cannot access any files downloaded by <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> into a non-default downloads directory.',
     },
@@ -7077,11 +7073,9 @@
       'id': 97,
       'caption': '''Import bookmarks from default browser on first run''',
       'tags': ['local-data-access'],
-      'desc': '''This policy forces bookmarks to be imported from the current default browser if enabled. If enabled, this policy also affects the import dialog.
+      'desc': '''Setting the policy to Enabled imports bookmarks from the previous default browser on first run. Setting the policy to Disabled or leaving it unset means no bookmarks are imported on first run.
 
-      If disabled, no bookmarks are imported.
-
-      If it is not set, the user may be asked whether to import, or importing may happen automatically.''',
+      Users can trigger an import dialog and the bookmarks checkbox will be checked or unchecked to match this policy's value.''',
       'label': '''Import bookmarks from default browser on first run''',
     },
     {
@@ -7099,11 +7093,9 @@
       'id': 98,
       'caption': '''Import browsing history from default browser on first run''',
       'tags': ['local-data-access'],
-      'desc': '''This policy forces the browsing history to be imported from the current default browser if enabled. If enabled, this policy also affects the import dialog.
+      'desc': '''Setting the policy to Enabled imports browsing history from the previous default browser on first run. Setting the policy to Disabled or leaving it unset means no browsing history is imported on first run.
 
-      If disabled, no browsing history is imported.
-
-      If it is not set, the user may be asked whether to import, or importing may happen automatically.''',
+      Users can trigger an import dialog and the browsing history checkbox will be checked or unchecked to match this policy's value.''',
       'label': '''Import browsing history from default browser on first run''',
     },
     {
@@ -7120,11 +7112,9 @@
       'id': 99,
       'caption': '''Import of homepage from default browser on first run''',
       'tags': ['local-data-access'],
-      'desc': '''This policy forces the home page to be imported from the current default browser if enabled.
+      'desc': '''Setting the policy to Enabled imports the homepage from the previous default browser on first run. Setting the policy to Disabled or leaving it unset means the homepage isn't imported on first run.
 
-      If disabled, the home page is not imported.
-
-      If it is not set, the user may be asked whether to import, or importing may happen automatically.''',
+      Users can trigger an import dialog and the the homepage checkbox will be checked or unchecked to match this policy's value.''',
       'label': '''Import of homepage from default browser on first run''',
     },
     {
@@ -7142,11 +7132,9 @@
       'id': 100,
       'caption': '''Import search engines from default browser on first run''',
       'tags': ['local-data-access'],
-      'desc': '''This policy forces search engines to be imported from the current default browser if enabled. If enabled, this policy also affects the import dialog.
+      'desc': '''Setting the policy to Enabled imports the default search engine from the previous default browser on first run. Setting the policy to Disabled or leaving it unset means the default search engine isn't imported on first run.
 
-      If disabled, the default search engine is not imported.
-
-      If it is not set, the user may be asked whether to import, or importing may happen automatically.''',
+      Users can trigger an import dialog and the default search engine checkbox will be checked or unchecked to match this policy's value.''',
       'label': '''Import search engines from default browser on first run''',
     },
     {
@@ -7164,11 +7152,9 @@
       'id': 101,
       'caption': '''Import saved passwords from default browser on first run''',
       'tags': ['local-data-access'],
-      'desc': '''This policy forces the saved passwords to be imported from the previous default browser if enabled. If enabled, this policy also affects the import dialog.
+      'desc': '''Setting the policy to Enabled imports saved passwords from the previous default browser on first run. Setting the policy to Disabled or leaving it unset means no saved passwords are imported on first run.
 
-      If disabled, the saved passwords are not imported.
-
-      If it is not set, the user may be asked whether to import, or importing may happen automatically.''',
+      Users can trigger an import dialog and the saved passwords checkbox will be checked or unchecked to match this policy's value.''',
       'label': '''Import saved passwords from default browser on first run''',
     },
     {
@@ -7186,11 +7172,9 @@
       'id': 277,
       'caption': '''Import autofill form data from default browser on first run''',
       'tags': ['local-data-access'],
-      'desc': '''This policy forces the autofill form data to be imported from the previous default browser if enabled. If enabled, this policy also affects the import dialog.
+      'desc': '''Setting the policy to Enabled imports autofill form data from the previous default browser on first run. Setting the policy to Disabled or leaving it unset means no autofill form data is imported on first run.
 
-      If disabled, the autofill form data is not imported.
-
-      If it is not set, the user may be asked whether to import, or importing may happen automatically.''',
+      Users can trigger an import dialog and the autofill form data checkbox will be checked or unchecked to match this policy's value.''',
       'label': '''Import autofill form data from default browser on first run''',
     },
     {
@@ -9492,9 +9476,9 @@
       'id': 139,
       'caption': '''Disable Drive in the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> Files app''',
       'tags': [],
-      'desc': '''Disables Google Drive syncing in the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> Files app when set to True. In that case, no data is uploaded to Google Drive.
+      'desc': '''Setting the policy to Enabled turns off <ph name="GOOGLE_DRIVE_NAME">Google Drive</ph> syncing in the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> Files app. No data is uploaded to Drive.
 
-          If not set or set to False, then users will be able to transfer files to Google Drive.''',
+      Setting the policy to Disabled or leaving it unset lets users transfer files to Drive.''',
       'arc_support': 'This policy does not prevent the user from using the Android Google Drive app. If you want to prevent access to Google Drive, you should disallow installation of the Android Google Drive app as well.',
     },
     {
@@ -9511,9 +9495,9 @@
       'id': 140,
       'caption': '''Disable Google Drive over cellular connections in the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> Files app''',
       'tags': [],
-      'desc': '''Disables Google Drive syncing in the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> Files app when using a cellular connection when set to True. In that case, data is only synced to Google Drive when connected via WiFi or Ethernet.
+      'desc': '''Setting the policy to Enabled turns off <ph name="GOOGLE_DRIVE_NAME">Google Drive</ph> syncing in the <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> Files app when on a cellular connection. Data is only synced to Drive when connected through Wi-Fi or Ethernet.
 
-          If not set or set to False, then users will be able to transfer files to Google Drive via cellular connections.''',
+      Setting the policy to Disabled or leaving it unset lets users transfer files to Drive on cellular connections.''',
       'arc_support': 'This policy has no effect on the Android Google Drive app. If you want to prevent use of Google Drive over cellular connections, you should disallow installation of the Android Google Drive app.',
     },
     {
@@ -14195,9 +14179,9 @@
       'id': 333,
       'caption': '''Enable <ph name="PRODUCT_NAME">Google Cast</ph>''',
       'tags': [],
-      'desc': '''If this policy is set to true or is not set, <ph name="PRODUCT_NAME">Google Cast</ph> will be enabled, and users will be able to launch it from the app menu, page context menus, media controls on Cast-enabled websites, and (if shown) the Cast toolbar icon.
+      'desc': '''Setting the policy to Enabled or leaving it unset turns on <ph name="PRODUCT_NAME">Google Cast</ph>, which users can launch from the app menu, page context menus, media controls on Cast-enabled websites, and (if shown) the Cast toolbar icon.
 
-          If this policy set to false, <ph name="PRODUCT_NAME">Google Cast</ph> will be disabled.'''
+      Setting the policy to Disabled turns off <ph name="PRODUCT_NAME">Google Cast</ph>.'''
     },
     {
       'name': 'ShowCastIconInToolbar',
@@ -14213,11 +14197,11 @@
       'id': 362,
       'caption': '''Show the <ph name="PRODUCT_NAME">Google Cast</ph> toolbar icon''',
       'tags': [],
-      'desc': '''If this policy is set to true, the Cast toolbar icon will always be shown on the toolbar or the overflow menu, and users will not be able to remove it.
+      'desc': '''Setting the policy to Enabled displays the Cast toolbar icon on the toolbar or the overflow menu, and users can't remove it.
 
-          If this policy is set to false or is not set, users will be able to pin or remove the icon via its contextual menu.
+      Setting the policy to Disabled or leaving it unset lets users pin or remove the icon through its contextual menu.
 
-          If the policy "EnableMediaRouter" is set to false, then this policy's value would have no effect, and the toolbar icon would not be shown.'''
+      If the policy <ph name="ENABLE_MEDIA_ROUTER_POLICY_NAME">EnableMediaRouter</ph> is set to Disabled, then this policy's value has no effect, and the toolbar icon doesn't appear.'''
     },
     {
       'name': 'MediaRouterCastAllowAllIPs',
@@ -14233,13 +14217,11 @@
       'id': 437,
       'caption': '''Allow <ph name="PRODUCT_NAME">Google Cast</ph> to connect to Cast devices on all IP addresses.''',
       'tags': [],
-      'desc': '''If this policy is set to true, <ph name="PRODUCT_NAME">Google Cast</ph> will connect to Cast devices on all IP addresses, not just RFC1918/RFC4193 private addresses.
+      'desc': '''Unless <ph name="ENABLE_MEDIA_ROUTER_POLICY_NAME">EnableMediaRouter</ph> is set to Disabled, setting <ph name="MEDIA_ROUTER_CAST_ALLOW_ALL_IPS_POLICY_NAME">MediaRouterCastAllowAllIPs</ph> to Enabled connects <ph name="PRODUCT_NAME">Google Cast</ph> to Cast devices on all IP addresses, not just RFC1918/RFC4193 private addresses.
 
-          If this policy is set to false, <ph name="PRODUCT_NAME">Google Cast</ph> will connect to Cast devices on RFC1918/RFC4193 private addresses only.
+      Setting the policy to Disabled connects <ph name="PRODUCT_NAME">Google Cast</ph> to Cast devices only on RFC1918/RFC4193.
 
-          If this policy is not set, <ph name="PRODUCT_NAME">Google Cast</ph> will connect to Cast devices on RFC1918/RFC4193 private addresses only, unless the CastAllowAllIPs feature is enabled.
-
-          If the policy "EnableMediaRouter" is set to false, then this policy's value would have no effect.'''
+      Leaving the policy unset connects <ph name="PRODUCT_NAME">Google Cast</ph> to Cast devices only on RFC1918/RFC4193, unless the CastAllowAllIPs feature is turned on.'''
     },
     {
       'name': 'ArcBackupRestoreEnabled',
@@ -15706,11 +15688,9 @@
       'id': 395,
       'caption': '''Ask where to save each file before downloading''',
       'tags': [],
-      'desc': '''
-      If the policy is enabled, the user will be asked where to save each file before downloading.
-      If the policy is disabled, downloads will start immediately, and the user will not be asked where to save the file.
-      If the policy is not configured, the user will be able to change this setting.
-      '''
+      'desc': '''Setting the policy to Enabled means users are asked where to save each file before downloading. Setting the policy to Disabled has downloads start immediately, and users aren't asked where to save the file.
+
+      Leaving the policy unset lets users change this setting.'''
     },
     {
       'name': 'UnaffiliatedArcAllowed',
@@ -15970,13 +15950,11 @@
       'id': 401,
       'caption': '''Set default download directory''',
       'tags': ['local-data-access'],
-      'desc': '''Configures the default directory that <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use for downloading files.
+      'desc': '''Setting the policy changes the default directory that Chrome downloads files to, but users can change the directory.
 
-      If you set this policy, it will change the default directory that <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> downloads files to. This policy is not mandatory, so the user will be able to change the directory.
+      Leaving the policy unset means Chrome uses its platform-specific default directory.
 
-      If you do not set this policy, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will use its usual default directory (platform-specific).
-
-      See https://www.chromium.org/administrators/policy-list-3/user-data-directory-variables for a list of variables that can be used.''',
+      Note: See a list of variables you can use ( https://www.chromium.org/administrators/policy-list-3/user-data-directory-variables ).''',
     },
     {
       'name': 'DeviceHostnameTemplate',
@@ -18773,10 +18751,9 @@
       'example_value': True,
       'id': 527,
       'caption': '''"Allow Google Assistant to access screen context"''',
-      'desc': '''This policy gives Google Assistant permission to access screen context and send the info to server.
-      If the policy is enabled, Google Assistant will be allowed to access screen context.
-      If the policy is disabled, Google Assistant will not be allowed to access screen context.
-      If not set, users can decide whether to allow Google Assistant to access screen context or not''',
+      'desc': '''Setting the policy to Enabled lets Google Assistant access screen context and send that data to a server. Setting the policy to Disabled keeps Google Assistant from screen context.
+
+      Leaving the policy unset lets users decide to turn this feature on or off.''',
     },
     {
       'name': 'VoiceInteractionHotwordEnabled',
@@ -18792,12 +18769,9 @@
       'example_value': True,
       'id': 529,
       'caption': '''Allow Google Assistant to listen for the voice activation phrase''',
-      'desc': '''This policy gives Google Assistant permission to listen for the voice activation phrase.
+      'desc': '''Setting the policy to Enabled lets Google Assistant listen for the voice activation phrase. Setting the policy to Disabled keeps Google Assistant from listening for the phrase.
 
-      If the policy is enabled, Google Assistant would listen for the voice activation phrase.
-      If the policy is disabled, Google Assistant would not listen for the voice activation phrase.
-      If the policy is not set, users can decide whether to allow Google Assistant to listen for the voice activation phrase.
-      ''',
+      Leaving the policy unset lets users decide to turn this feature on or off.''',
     },
     {
       'name': 'VoiceInteractionQuickAnswersEnabled',
diff --git a/components/prerender/common/BUILD.gn b/components/prerender/common/BUILD.gn
index 974b784..b5cd57e 100644
--- a/components/prerender/common/BUILD.gn
+++ b/components/prerender/common/BUILD.gn
@@ -10,12 +10,17 @@
     "prerender_final_status.h",
     "prerender_origin.cc",
     "prerender_origin.h",
+    "prerender_util.cc",
+    "prerender_util.h",
   ]
   deps = [
     ":mojo_bindings",
+    "//extensions/common:common_constants",
     "//ipc",
     "//ipc:message_support",
     "//ipc:param_traits",
+    "//ui/gfx",
+    "//url",
   ]
 }
 
diff --git a/components/prerender/common/DEPS b/components/prerender/common/DEPS
index 1c40d98..2cfa362 100644
--- a/components/prerender/common/DEPS
+++ b/components/prerender/common/DEPS
@@ -1,3 +1,10 @@
 include_rules = [
+  "+content/public/common",
+  "+extensions/common",
   "+ipc",
+  "+mojo/public/cpp/bindings",
+  "+net/base",
+  "+net/url_request",
+  "+services/network/public",
+  "+third_party/blink/public",
 ]
diff --git a/chrome/common/prerender_util.cc b/components/prerender/common/prerender_util.cc
similarity index 96%
rename from chrome/common/prerender_util.cc
rename to components/prerender/common/prerender_util.cc
index a08bb92f..cf60258c 100644
--- a/chrome/common/prerender_util.cc
+++ b/components/prerender/common/prerender_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/common/prerender_util.h"
+#include "components/prerender/common/prerender_util.h"
 
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_functions.h"
@@ -19,12 +19,15 @@
 
 // Valid HTTP methods for both prefetch and prerendering.
 const char* const kValidHttpMethods[] = {
-    "GET", "HEAD",
+    "GET",
+    "HEAD",
 };
 
 // Additional valid HTTP methods for prerendering.
 const char* const kValidHttpMethodsForPrerendering[] = {
-    "OPTIONS", "POST", "TRACE",
+    "OPTIONS",
+    "POST",
+    "TRACE",
 };
 
 // This enum is used to define the buckets for the
diff --git a/chrome/common/prerender_util.h b/components/prerender/common/prerender_util.h
similarity index 90%
rename from chrome/common/prerender_util.h
rename to components/prerender/common/prerender_util.h
index 39d2aa7..964eb125 100644
--- a/chrome/common/prerender_util.h
+++ b/components/prerender/common/prerender_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_COMMON_PRERENDER_UTIL_H_
-#define CHROME_COMMON_PRERENDER_UTIL_H_
+#ifndef COMPONENTS_PRERENDER_COMMON_PRERENDER_UTIL_H_
+#define COMPONENTS_PRERENDER_COMMON_PRERENDER_UTIL_H_
 
 #include <string>
 
@@ -43,4 +43,4 @@
 
 }  // namespace prerender
 
-#endif  // CHROME_COMMON_PRERENDER_UTIL_H_
+#endif  // COMPONENTS_PRERENDER_COMMON_PRERENDER_UTIL_H_
diff --git a/components/services/app_service/app_service_impl.cc b/components/services/app_service/app_service_impl.cc
index e1f183d..a2ff42b 100644
--- a/components/services/app_service/app_service_impl.cc
+++ b/components/services/app_service/app_service_impl.cc
@@ -419,8 +419,8 @@
 
   writing_preferred_apps_ = true;
 
-  auto preferred_apps_value =
-      apps::ConvertPreferredAppsToValue(preferred_apps.GetReference());
+  auto preferred_apps_value = apps::ConvertPreferredAppsToValue(
+      preferred_apps.GetReference(), is_share_intents_supported_);
 
   std::string json_string;
   JSONStringValueSerializer serializer(&json_string);
@@ -478,15 +478,16 @@
                << error_code << " and error message: " << error_message;
       preferred_apps_.Init();
     } else {
+      preferred_apps_upgraded = IsUpgradedForSharing(*preferred_apps_value);
       auto preferred_apps =
           apps::ParseValueToPreferredApps(*preferred_apps_value);
-      if (is_share_intents_supported_) {
-        preferred_apps_upgraded = UpgradePreferredApps(preferred_apps);
+      if (is_share_intents_supported_ && !preferred_apps_upgraded) {
+        UpgradePreferredApps(preferred_apps);
       }
       preferred_apps_.Init(preferred_apps);
     }
   }
-  if (preferred_apps_upgraded) {
+  if (is_share_intents_supported_ && !preferred_apps_upgraded) {
     LogPreferredAppUpdateAction(PreferredAppsUpdateAction::kUpgraded);
     WriteToJSON(profile_dir_, preferred_apps_);
   }
diff --git a/components/services/app_service/public/cpp/preferred_apps_converter.cc b/components/services/app_service/public/cpp/preferred_apps_converter.cc
index 9334af87..00b5b7b 100644
--- a/components/services/app_service/public/cpp/preferred_apps_converter.cc
+++ b/components/services/app_service/public/cpp/preferred_apps_converter.cc
@@ -11,6 +11,9 @@
 
 namespace {
 
+constexpr int kVersionInitial = 0;
+constexpr int kVersionSupportsSharing = 1;
+
 base::Value ConvertConditionValueToValue(
     const apps::mojom::ConditionValuePtr& condition_value) {
   base::Value condition_value_dict(base::Value::Type::DICTIONARY);
@@ -120,29 +123,46 @@
 const char kMatchTypeKey[] = "match_type";
 const char kAppIdKey[] = "app_id";
 const char kIntentFilterKey[] = "intent_filter";
+const char kPreferredAppsKey[] = "preferred_apps";
+const char kVersionKey[] = "version";
 
 base::Value ConvertPreferredAppsToValue(
-    const PreferredAppsList::PreferredApps& preferred_apps) {
-  base::Value preferred_apps_value(base::Value::Type::LIST);
+    const PreferredAppsList::PreferredApps& preferred_apps,
+    bool upgraded_for_sharing) {
+  base::Value preferred_apps_value(base::Value::Type::DICTIONARY);
+  int version =
+      upgraded_for_sharing ? kVersionSupportsSharing : kVersionInitial;
+  preferred_apps_value.SetIntKey(kVersionKey, version);
+  base::Value preferred_apps_list(base::Value::Type::LIST);
   for (auto& preferred_app : preferred_apps) {
     base::Value preferred_app_dict(base::Value::Type::DICTIONARY);
     preferred_app_dict.SetKey(
         kIntentFilterKey,
         ConvertIntentFilterToValue(preferred_app->intent_filter));
     preferred_app_dict.SetStringKey(kAppIdKey, preferred_app->app_id);
-    preferred_apps_value.Append(std::move(preferred_app_dict));
+    preferred_apps_list.Append(std::move(preferred_app_dict));
   }
+  preferred_apps_value.SetKey(kPreferredAppsKey,
+                              std::move(preferred_apps_list));
   return preferred_apps_value;
 }
 
 PreferredAppsList::PreferredApps ParseValueToPreferredApps(
     const base::Value& preferred_apps_value) {
-  if (!preferred_apps_value.is_list()) {
-    DVLOG(0) << "Fail to parse preferred apps. Cannot the preferred app list.";
+  const base::Value* preferred_apps_list = nullptr;
+  if (preferred_apps_value.is_list()) {
+    preferred_apps_list = &preferred_apps_value;
+  } else if (preferred_apps_value.is_dict()) {
+    preferred_apps_list = preferred_apps_value.FindListKey(kPreferredAppsKey);
+  }
+  if (!preferred_apps_list || !preferred_apps_list->is_list()) {
+    DVLOG(0)
+        << "Fail to parse preferred apps. Cannot find the preferred app list.";
     return PreferredAppsList::PreferredApps();
   }
+
   PreferredAppsList::PreferredApps preferred_apps;
-  for (auto& entry : preferred_apps_value.GetList()) {
+  for (auto& entry : preferred_apps_list->GetList()) {
     auto* app_id = entry.FindStringKey(kAppIdKey);
     if (!app_id) {
       DVLOG(0) << "Fail to parse condition value. Cannot find \""
@@ -159,18 +179,24 @@
         std::move(parsed_intent_filter), *app_id);
     preferred_apps.push_back(std::move(new_preferred_app));
   }
+
   return preferred_apps;
 }
 
-bool UpgradePreferredApps(PreferredAppsList::PreferredApps& preferred_apps) {
-  bool preferred_apps_upgraded = false;
+void UpgradePreferredApps(PreferredAppsList::PreferredApps& preferred_apps) {
   for (auto& preferred_app : preferred_apps) {
     if (apps_util::FilterNeedsUpgrade(preferred_app->intent_filter)) {
       apps_util::UpgradeFilter(preferred_app->intent_filter);
-      preferred_apps_upgraded = true;
     }
   }
-  return preferred_apps_upgraded;
+}
+
+bool IsUpgradedForSharing(const base::Value& preferred_apps_value) {
+  if (preferred_apps_value.is_list()) {
+    return false;
+  }
+  auto version = preferred_apps_value.FindIntKey(kVersionKey);
+  return version.value_or(kVersionInitial) >= kVersionSupportsSharing;
 }
 
 }  // namespace apps
diff --git a/components/services/app_service/public/cpp/preferred_apps_converter.h b/components/services/app_service/public/cpp/preferred_apps_converter.h
index b31e8a4..71b52bf 100644
--- a/components/services/app_service/public/cpp/preferred_apps_converter.h
+++ b/components/services/app_service/public/cpp/preferred_apps_converter.h
@@ -16,12 +16,14 @@
 extern const char kMatchTypeKey[];
 extern const char kAppIdKey[];
 extern const char kIntentFilterKey[];
+extern const char kPreferredAppsKey[];
+extern const char kVersionKey[];
 
 // Convert the PreferredAppsList struct to base::Value to write to JSON file.
 // e.g. for preferred app with |app_id| "abcdefg", and |intent_filter| for url
 // https://www.google.com/abc.
 // The converted base::Value format will be:
-//[ {"app_id": "abcdefg",
+//{"preferred_apps": [ {"app_id": "abcdefg",
 //    "intent_filter": [ {
 //       "condition_type": 0,
 //       "condition_values": [ {
@@ -41,17 +43,21 @@
 //          "value": "/abc"
 //       } ]
 //    } ]
-// } ]
+// } ],
+// "version": 0}
 base::Value ConvertPreferredAppsToValue(
-    const PreferredAppsList::PreferredApps& preferred_apps);
+    const PreferredAppsList::PreferredApps& preferred_apps,
+    bool upgraded_for_sharing = false);
 
 // Parse the base::Value read from JSON file back to preferred apps struct.
 PreferredAppsList::PreferredApps ParseValueToPreferredApps(
     const base::Value& preferred_apps_value);
 
-// Upgrade the preferred apps struct to contain action in the filters. Return
-// true if |preferred_apps| is upgraded.
-bool UpgradePreferredApps(PreferredAppsList::PreferredApps& preferred_apps);
+// Upgrade the preferred apps struct to contain action in the filters.
+void UpgradePreferredApps(PreferredAppsList::PreferredApps& preferred_apps);
+
+// Check if the preferred apps file already upgraded for supporting sharing.
+bool IsUpgradedForSharing(const base::Value& preferred_apps_value);
 
 }  // namespace apps
 
diff --git a/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc b/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
index c62d00b..bd4a303 100644
--- a/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
+++ b/components/services/app_service/public/cpp/preferred_apps_converter_unittest.cc
@@ -30,9 +30,11 @@
   auto converted_value =
       apps::ConvertPreferredAppsToValue(preferred_apps.GetReference());
 
+  auto* converted_preferred_apps =
+      converted_value.FindKey(apps::kPreferredAppsKey);
   // Check that each entry is correct.
-  ASSERT_EQ(1u, converted_value.GetList().size());
-  auto& entry = converted_value.GetList()[0];
+  ASSERT_EQ(1u, converted_preferred_apps->GetList().size());
+  auto& entry = converted_preferred_apps->GetList()[0];
   EXPECT_EQ(kAppId1, *entry.FindStringKey(apps::kAppIdKey));
 
   auto* converted_intent_filter = entry.FindKey(apps::kIntentFilterKey);
@@ -77,7 +79,7 @@
       apps::ConvertPreferredAppsToValue(preferred_apps.GetReference());
 
   const char expected_output_string[] =
-      "[ {\"app_id\": \"abcdefg\","
+      "{\"preferred_apps\": [ {\"app_id\": \"abcdefg\","
       "   \"intent_filter\": [ {"
       "      \"condition_type\": 0,"
       "      \"condition_values\": [ {"
@@ -97,14 +99,62 @@
       "         \"value\": \"/abc\""
       "      } ]"
       "   } ]"
-      "} ]";
+      "} ],"
+      "\"version\": 0}";
   base::Optional<base::Value> expected_output =
       base::JSONReader::Read(expected_output_string);
   ASSERT_TRUE(expected_output);
   EXPECT_EQ(expected_output.value(), converted_value);
 }
 
-// Test parse simple entry from json string.
+// Test one upgraded simple entry with json string.
+TEST_F(PreferredAppsConverterTest, ConvertUpgradedSimpleEntryJson) {
+  GURL filter_url = GURL("https://www.google.com/abc");
+  auto intent_filter = apps_util::CreateIntentFilterForUrlScope(
+      filter_url, /*with_action_view=*/true);
+
+  apps::PreferredAppsList preferred_apps;
+  preferred_apps.Init();
+  preferred_apps.AddPreferredApp(kAppId1, intent_filter);
+  auto converted_value = apps::ConvertPreferredAppsToValue(
+      preferred_apps.GetReference(), /*upgraded_for_sharing=*/true);
+
+  const char expected_output_string[] =
+      "{\"preferred_apps\": [ {\"app_id\": \"abcdefg\","
+      "   \"intent_filter\": [ {"
+      "      \"condition_type\": 3,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 0,"
+      "         \"value\": \"view\""
+      "      } ]"
+      "   }, {"
+      "      \"condition_type\": 0,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 0,"
+      "         \"value\": \"https\""
+      "      } ]"
+      "   }, {"
+      "      \"condition_type\": 1,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 0,"
+      "         \"value\": \"www.google.com\""
+      "      } ]"
+      "   }, {"
+      "      \"condition_type\": 2,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 2,"
+      "         \"value\": \"/abc\""
+      "      } ]"
+      "   } ]"
+      "} ],"
+      "\"version\": 1}";
+  base::Optional<base::Value> expected_output =
+      base::JSONReader::Read(expected_output_string);
+  ASSERT_TRUE(expected_output);
+  EXPECT_EQ(expected_output.value(), converted_value);
+}
+
+// Test parse simple entry from json string (old format).
 TEST_F(PreferredAppsConverterTest, ParseSimpleEntryJson) {
   const char test_string[] =
       "[ {\"app_id\": \"abcdefg\","
@@ -131,6 +181,7 @@
   base::Optional<base::Value> test_value = base::JSONReader::Read(test_string);
   ASSERT_TRUE(test_value);
   auto parsed_entry = apps::ParseValueToPreferredApps(test_value.value());
+  EXPECT_FALSE(apps::IsUpgradedForSharing(test_value.value()));
 
   GURL filter_url = GURL("https://www.google.com/abc");
   auto intent_filter = apps_util::CreateIntentFilterForUrlScope(filter_url);
@@ -143,6 +194,54 @@
   EXPECT_EQ(expected_entry, parsed_entry);
 }
 
+// Test parse simple entry from json string (upgraded for sharing).
+TEST_F(PreferredAppsConverterTest, ParseUpgradedSimpleEntryJson) {
+  const char test_string[] =
+      "{\"preferred_apps\": [ {\"app_id\": \"abcdefg\","
+      "   \"intent_filter\": [ {"
+      "      \"condition_type\": 3,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 0,"
+      "         \"value\": \"view\""
+      "      } ]"
+      "   }, {"
+      "      \"condition_type\": 0,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 0,"
+      "         \"value\": \"https\""
+      "      } ]"
+      "   }, {"
+      "      \"condition_type\": 1,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 0,"
+      "         \"value\": \"www.google.com\""
+      "      } ]"
+      "   }, {"
+      "      \"condition_type\": 2,"
+      "      \"condition_values\": [ {"
+      "         \"match_type\": 2,"
+      "         \"value\": \"/abc\""
+      "      } ]"
+      "   } ]"
+      "} ],"
+      "\"version\": 1}";
+  base::Optional<base::Value> test_value = base::JSONReader::Read(test_string);
+  ASSERT_TRUE(test_value);
+  auto parsed_entry = apps::ParseValueToPreferredApps(test_value.value());
+  EXPECT_TRUE(apps::IsUpgradedForSharing(test_value.value()));
+
+  GURL filter_url = GURL("https://www.google.com/abc");
+  auto intent_filter = apps_util::CreateIntentFilterForUrlScope(
+      filter_url, /*with_action_view=*/true);
+
+  apps::PreferredAppsList preferred_apps;
+  preferred_apps.Init();
+  preferred_apps.AddPreferredApp(kAppId1, intent_filter);
+  auto& expected_entry = preferred_apps.GetReference();
+
+  EXPECT_EQ(expected_entry, parsed_entry);
+}
+
 TEST_F(PreferredAppsConverterTest, ParseJsonWithInvalidAppId) {
   // Invalid key.
   const char test_key[] =
@@ -487,6 +586,6 @@
   new_preferred_apps.AddPreferredApp(kAppId1, new_intent_filter);
 
   auto old_preferred_apps_value = old_preferred_apps.GetValue();
-  EXPECT_TRUE(apps::UpgradePreferredApps(old_preferred_apps_value));
+  apps::UpgradePreferredApps(old_preferred_apps_value);
   EXPECT_EQ(old_preferred_apps_value, new_preferred_apps.GetReference());
 }
diff --git a/components/signin/ios/browser/account_consistency_service.mm b/components/signin/ios/browser/account_consistency_service.mm
index 798a67e..53195c1 100644
--- a/components/signin/ios/browser/account_consistency_service.mm
+++ b/components/signin/ios/browser/account_consistency_service.mm
@@ -414,7 +414,7 @@
         break;
       case REMOVE_CHROME_CONNECTED_COOKIE:
         // Remove request.domain from prefs.
-        update->RemoveWithoutPathExpansion(request.domain, nullptr);
+        update->RemoveKey(request.domain);
         break;
     }
   }
diff --git a/components/sync/test/fake_server/fake_server_nigori_helper.cc b/components/sync/test/fake_server/fake_server_nigori_helper.cc
index 559252d..50d5a68 100644
--- a/components/sync/test/fake_server/fake_server_nigori_helper.cc
+++ b/components/sync/test/fake_server/fake_server_nigori_helper.cc
@@ -28,8 +28,12 @@
 
 void SetNigoriInFakeServer(const sync_pb::NigoriSpecifics& nigori,
                            FakeServer* fake_server) {
-  std::string nigori_entity_id =
-      fake_server->GetTopLevelPermanentItemId(syncer::NIGORI);
+  std::vector<sync_pb::SyncEntity> nigoris =
+      fake_server->GetPermanentSyncEntitiesByModelType(syncer::NIGORI);
+  ASSERT_EQ(nigoris.size(), 1u);
+  // Note: GetTopLevelPermanentItemId() is not safe to use here, see
+  // crbug.com/1104225.
+  std::string nigori_entity_id = nigoris[0].id_string();
   ASSERT_NE(nigori_entity_id, "");
   sync_pb::EntitySpecifics nigori_entity_specifics;
   *nigori_entity_specifics.mutable_nigori() = nigori;
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc
index ffa81b8d..23d6d22 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -5,7 +5,6 @@
 #include "components/sync_bookmarks/synced_bookmark_tracker.h"
 
 #include <algorithm>
-#include <set>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -445,6 +444,7 @@
   sync_pb::BookmarkModelMetadata model_metadata;
   model_metadata.set_bookmarks_full_title_reuploaded(
       bookmarks_full_title_reuploaded_);
+  model_metadata.set_last_sync_time(syncer::TimeToProtoTime(last_sync_time_));
   for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair :
        sync_id_to_entities_map_) {
     DCHECK(pair.second) << " for ID " << pair.first;
@@ -707,7 +707,7 @@
   //    node. What's left in |nodes| are the roots of the forest.
   // 3. Start at each root in |nodes|, emit the update and recurse over its
   //    children.
-  std::set<const bookmarks::BookmarkNode*> nodes;
+  std::unordered_set<const bookmarks::BookmarkNode*> nodes;
   // Collect nodes with updates
   for (const SyncedBookmarkTracker::Entity* entity : entities) {
     DCHECK(entity->IsUnsynced());
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.h b/components/sync_bookmarks/synced_bookmark_tracker.h
index efd933a..d0d209c 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.h
+++ b/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_SYNC_BOOKMARKS_SYNCED_BOOKMARK_TRACKER_H_
 #define COMPONENTS_SYNC_BOOKMARKS_SYNCED_BOOKMARK_TRACKER_H_
 
-#include <map>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -351,7 +350,8 @@
 
   // A map of sync server ids to sync entities. This should contain entries and
   // metadata for almost everything.
-  std::map<std::string, std::unique_ptr<Entity>> sync_id_to_entities_map_;
+  std::unordered_map<std::string, std::unique_ptr<Entity>>
+      sync_id_to_entities_map_;
 
   // A map of bookmark nodes to sync entities. It's keyed by the bookmark node
   // pointers which get assigned when loading the bookmark model. This map is
diff --git a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
index 7fb34920..ed8ce4d 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
@@ -186,6 +186,7 @@
   EXPECT_THAT(
       bookmark_model_metadata.bookmarks_metadata(0).metadata().server_id(),
       Eq(kSyncId));
+  EXPECT_TRUE(bookmark_model_metadata.has_last_sync_time());
 }
 
 TEST(SyncedBookmarkTrackerTest,
diff --git a/components/viz/service/display/scoped_gpu_memory_buffer_texture.h b/components/viz/service/display/scoped_gpu_memory_buffer_texture.h
index a0a87ae..bd252eb 100644
--- a/components/viz/service/display/scoped_gpu_memory_buffer_texture.h
+++ b/components/viz/service/display/scoped_gpu_memory_buffer_texture.h
@@ -37,9 +37,9 @@
 
   // The ContextProvider used to free the texture when this object is destroyed,
   // so it must outlive this object.
-  ContextProvider* context_provider_;
-  uint32_t gl_id_;
-  uint32_t target_;
+  ContextProvider* context_provider_ = nullptr;
+  uint32_t gl_id_ = 0;
+  uint32_t target_ = 0;
   gfx::Size size_;
   gfx::ColorSpace color_space_;
 };
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index c69fee7..3c7464c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -49,6 +49,7 @@
     "//base:base_static",
     "//base:clang_profiling_buildflags",
     "//base/third_party/dynamic_annotations",
+    "//base/util/ranges:ranges",
     "//build:branding_buildflags",
     "//build:lacros_buildflags",
     "//cc",
@@ -1792,6 +1793,8 @@
     "sms/sms_queue.h",
     "sms/sms_service.cc",
     "sms/sms_service.h",
+    "sms/user_consent_handler.cc",
+    "sms/user_consent_handler.h",
     "speech/speech_recognition_dispatcher_host.cc",
     "speech/speech_recognition_dispatcher_host.h",
     "speech/speech_recognition_manager_impl.cc",
diff --git a/content/browser/cross_origin_opener_policy_browsertest.cc b/content/browser/cross_origin_opener_policy_browsertest.cc
index 31deeb09..4c9ab63 100644
--- a/content/browser/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/cross_origin_opener_policy_browsertest.cc
@@ -99,6 +99,8 @@
   net::EmbeddedTestServer https_server_;
 };
 
+using VirtualBrowsingContextGroupTest = CrossOriginOpenerPolicyBrowserTest;
+
 int VirtualBrowsingContextGroup(WebContents* wc) {
   return static_cast<WebContentsImpl*>(wc)
       ->GetMainFrame()
@@ -971,8 +973,7 @@
 
 // Navigate in between two documents. Check the virtual browsing context group
 // is properly updated.
-IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
-                       VirtualBrowsingContextGroup_Navigation) {
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest, Navigation) {
   const struct {
     GURL url_a;
     GURL url_b;
@@ -1183,7 +1184,6 @@
       },
       // TODO(https://crbug.com/1101339). Test with COEP-RO.
       // TODO(https://crbug.com/1101339). Test with COOP-RO+COOP.
-      // TODO(https://crbug.com/1101339). Test with COOP-same-origin-allow-popup
   };
 
   for (const auto& test_case : kTestCases) {
@@ -1203,7 +1203,6 @@
     // Note: Navigating from A to B and navigating from B to A must lead to the
     // same decision. We check both to avoid adding all the symmetric test
     // cases.
-    //
     if (test_case.expect_different_virtual_browsing_context_group) {
       EXPECT_NE(group_1, group_2);  // url_a -> url_b.
       EXPECT_NE(group_2, group_3);  // url_a <- url_b.
@@ -1216,8 +1215,7 @@
 
 // Use window.open(url). Check the virtual browsing context group of the two
 // window.
-IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
-                       VirtualBrowsingContextGroup_WindowOpen) {
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest, WindowOpen) {
   const struct {
     GURL url_opener;
     GURL url_openee;
@@ -1329,7 +1327,6 @@
 
       // TODO(https://crbug.com/1101339). Test with COEP-RO.
       // TODO(https://crbug.com/1101339). Test with COOP-RO+COOP
-      // TODO(https://crbug.com/1101339). Test with COOP-same-origin-allow-popup
   };
 
   for (const auto& test_case : kTestCases) {
@@ -1359,12 +1356,392 @@
   }
 }
 
+namespace {
+// Use two URLs, |url_a| and |url_b|. One of them at least uses
+// COOP:same-origin-allow-popups, or COOP-Report-Only:same-origin-allow-popups,
+// or both.
+//
+// Test two scenario:
+// 1. From |url_a|, opens |url_b|
+// 2. From |url_a|, navigates to |url_b|.
+//
+// In both cases, check whether a new virtual browsing context group has been
+// used or not.
+struct VirtualBcgAllowPopupTestCase {
+  GURL url_a;
+  GURL url_b;
+  bool expect_different_group_window_open;
+  bool expect_different_group_navigation;
+};
+
+void RunTest(const VirtualBcgAllowPopupTestCase& test_case, Shell* shell) {
+  SCOPED_TRACE(testing::Message()
+               << std::endl
+               << "url_a = " << test_case.url_a << std::endl
+               << "url_b = " << test_case.url_b << std::endl);
+  ASSERT_TRUE(NavigateToURL(shell, test_case.url_a));
+  int group_initial = VirtualBrowsingContextGroup(shell->web_contents());
+
+  ShellAddedObserver shell_observer;
+  EXPECT_TRUE(ExecJs(shell->web_contents()->GetMainFrame(),
+                     JsReplace("window.open($1)", test_case.url_b)));
+  WebContents* popup = shell_observer.GetShell()->web_contents();
+  WaitForLoadStop(popup);
+  int group_openee = VirtualBrowsingContextGroup(popup);
+
+  ASSERT_TRUE(NavigateToURL(shell, test_case.url_b));
+  int group_navigate = VirtualBrowsingContextGroup(shell->web_contents());
+
+  if (test_case.expect_different_group_window_open)
+    EXPECT_NE(group_initial, group_navigate);
+  else
+    EXPECT_EQ(group_initial, group_navigate);
+
+  if (test_case.expect_different_group_navigation)
+    EXPECT_NE(group_initial, group_openee);
+  else
+    EXPECT_EQ(group_initial, group_openee);
+
+  popup->Close();
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       NonCoopToCoopAllowPopup) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      {
+          // same-origin.
+          https_server()->GetURL("a.com", "/title1.html"),
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL("a.a.com", "/title1.html"),
+          https_server()->GetURL(
+              "b.a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL("a.com", "/title1.html"),
+          https_server()->GetURL(
+              "b.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+  };
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
+// coop:same-origin-allow-popup -> coop:none.
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       CoopAllowPopup_NonCoop) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      {
+          // same-origin.
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com", "/title1.html"), false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL(
+              "b.a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.a.com", "/title1.html"), false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL(
+              "b.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com", "/title1.html"), false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+  };
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
+// coop:none -> coop:same-origin-allow-popup.
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       CoopRoAllowPopup_NonCoop) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      {
+          // same-origin.
+          https_server()->GetURL("a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com", "/title1.html"), false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL("b.a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.a.com", "/title1.html"), false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL("b.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com", "/title1.html"), false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+  };
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
+// coop:same-origin-allow-popup -> coop:same-origin-allow-popup.
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       CoopAllowPopup_CoopAllowPopup) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      {
+          // same-origin.
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,
+          false,
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL(
+              "a.a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL(
+              "b.a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL(
+              "b.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+
+  };
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
+// coop:same-origin-allow-popup -> coop-ro:same-origin-allow-popup.
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       CoopAllowPopup_CoopRoAllowPopup) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      {
+          // same-origin.
+          https_server()->GetURL("a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          false,
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL("a.a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("b.a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL("a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("b.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+  };
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
+// coop-ro:same-origin-allow-popup -> coop:same-origin-allow-popup.
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       CoopRoAllowPopup_CoopAllowPopup) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      {
+          // same-origin.
+          https_server()->GetURL("a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL("a.a.com",
+                                 "/set-header?"
+                                 "Cross-Origin-Opener-Policy-Report-Only: "
+                                 "same-origin-allow-popups&"
+                                 "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL(
+              "b.a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL(
+              "b.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+  };
+
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
+// coop:same-origin-allow-popup + coop-ro:same-origin-allow-popup -> coop:none.
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       CoopPopupRoSameOrigin_NonCoop) {
+  const VirtualBcgAllowPopupTestCase kTestCases[] = {
+      // coop:allow-popup, coop-ro:same-origin-> no-coop.
+      {
+          // same-origin.
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Opener-Policy-Report-Only: same-origin&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("a.com", "/title1.html"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-origin.
+          https_server()->GetURL(
+              "a.a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Opener-Policy-Report-Only: same-origin&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("b.a.com", "/title1.html"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+      {
+          // cross-site.
+          https_server()->GetURL(
+              "a.com",
+              "/set-header?"
+              "Cross-Origin-Opener-Policy: same-origin-allow-popups&"
+              "Cross-Origin-Opener-Policy-Report-Only: same-origin&"
+              "Cross-Origin-Embedder-Policy: require-corp"),
+          https_server()->GetURL("b.com", "/title1.html"),
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+          false,  // TODO(https://crbug.com/1101339). Replace with true.
+      },
+  };
+
+  for (const auto& test : kTestCases)
+    RunTest(test, shell());
+}
+
 // Navigates in between two pages from a different browsing context group. Then
 // use the history API to navigate back and forth. Check their virtual browsing
 // context group isn't restored.
 // The goal is to spot differences when the BackForwardCache is enabled.
-IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
-                       VirtualBrowsingContextGroup_HistoryNavigation) {
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest, HistoryNavigation) {
   GURL url_a = https_server()->GetURL(
       "a.com",
       "/set-header?"
@@ -1404,8 +1781,8 @@
 //    context group).
 //
 // A1 and B4 must not be in the same browsing context group.
-IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest,
-                       VirtualBrowsingContextGroup_HistoryNavigationWithPopup) {
+IN_PROC_BROWSER_TEST_P(VirtualBrowsingContextGroupTest,
+                       HistoryNavigationWithPopup) {
   GURL url_a = https_server()->GetURL("a.com", "/title1.html");
   GURL url_b = https_server()->GetURL("b.com", "/title1.html");
   GURL url_c = https_server()->GetURL(
@@ -1445,4 +1822,7 @@
 INSTANTIATE_TEST_SUITE_P(All,
                          CrossOriginOpenerPolicyBrowserTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
+INSTANTIATE_TEST_SUITE_P(All,
+                         VirtualBrowsingContextGroupTest,
+                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
 }  // namespace content
diff --git a/content/browser/frame_host/ancestor_throttle.cc b/content/browser/frame_host/ancestor_throttle.cc
index c0a3470..9b8c5e9 100644
--- a/content/browser/frame_host/ancestor_throttle.cc
+++ b/content/browser/frame_host/ancestor_throttle.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/util/ranges/algorithm.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_request.h"
@@ -79,27 +80,15 @@
       XFrameOptionsHistogram::XFRAMEOPTIONS_HISTOGRAM_MAX);
 }
 
-bool HeadersContainFrameAncestorsCSP(const net::HttpResponseHeaders* headers,
-                                     bool include_report_only) {
-  std::vector<std::string> header_names = {"content-security-policy"};
-  if (include_report_only)
-    header_names.push_back("content-security-policy-report-only");
-  for (const auto& header : header_names) {
-    size_t iter = 0;
-    std::string value;
-    while (headers->EnumerateHeader(&iter, header, &value)) {
-      // A content-security-policy is a semicolon-separated list of directives.
-      for (const auto& directive : base::SplitStringPiece(
-               value, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
-        // The trailing " " is intentional; we'd otherwise match
-        // "frame-ancestors-is-not-this-directive".
-        if (base::StartsWith(directive, "frame-ancestors ",
-                             base::CompareCase::INSENSITIVE_ASCII))
-          return true;
-      }
-    }
-  }
-  return false;
+bool HeadersContainFrameAncestorsCSP(
+    const network::mojom::ParsedHeadersPtr& headers) {
+  return util::ranges::any_of(
+      headers->content_security_policy, [](const auto& csp) {
+        return csp->header->type ==
+                   network::mojom::ContentSecurityPolicyType::kEnforce &&
+               csp->directives.count(
+                   network::mojom::CSPDirectiveName::FrameAncestors);
+      });
 }
 
 class FrameAncestorCSPContext : public network::CSPContext {
@@ -285,6 +274,16 @@
   HeaderDisposition disposition =
       ParseXFrameOptionsHeader(request->GetResponseHeaders(), &header_value);
 
+  // If 'X-Frame-Options' would potentially block the response, check whether
+  // the 'frame-ancestors' CSP directive should take effect instead. See
+  // https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
+  if (disposition != HeaderDisposition::NONE &&
+      disposition != HeaderDisposition::ALLOWALL &&
+      HeadersContainFrameAncestorsCSP(request->response()->parsed_headers)) {
+    RecordXFrameOptionsUsage(XFrameOptionsHistogram::BYPASS);
+    return CheckResult::PROCEED;
+  }
+
   switch (disposition) {
     case HeaderDisposition::CONFLICT:
       if (logging == LoggingDisposition::LOG_TO_CONSOLE)
@@ -339,9 +338,6 @@
     case HeaderDisposition::NONE:
       RecordXFrameOptionsUsage(XFrameOptionsHistogram::NONE);
       return CheckResult::PROCEED;
-    case HeaderDisposition::BYPASS:
-      RecordXFrameOptionsUsage(XFrameOptionsHistogram::BYPASS);
-      return CheckResult::PROCEED;
     case HeaderDisposition::ALLOWALL:
       RecordXFrameOptionsUsage(XFrameOptionsHistogram::ALLOWALL);
       return CheckResult::PROCEED;
@@ -524,18 +520,6 @@
       result = HeaderDisposition::CONFLICT;
   }
 
-  // If 'X-Frame-Options' would potentially block the response, check whether
-  // the 'frame-ancestors' CSP directive should take effect instead. See
-  // https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
-  if (result != HeaderDisposition::NONE &&
-      result != HeaderDisposition::ALLOWALL &&
-      // TODO(antoniosartori): Use the already parsed CSP header instead of the
-      // raw headers here as soon as we remove
-      // network::features::kOutOfBlinkFrameAncestors
-      HeadersContainFrameAncestorsCSP(headers, false)) {
-    return HeaderDisposition::BYPASS;
-  }
-
   return result;
 }
 
diff --git a/content/browser/frame_host/ancestor_throttle.h b/content/browser/frame_host/ancestor_throttle.h
index 8c107f9b..5122ece 100644
--- a/content/browser/frame_host/ancestor_throttle.h
+++ b/content/browser/frame_host/ancestor_throttle.h
@@ -37,8 +37,7 @@
     SAMEORIGIN,
     ALLOWALL,
     INVALID,
-    CONFLICT,
-    BYPASS
+    CONFLICT
   };
 
   static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index f07d96e..3c94c561 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1014,9 +1014,6 @@
   // the dtor has run.  (It may also be null in tests.)
   unload_event_monitor_timeout_.reset();
 
-  for (auto& iter : visual_state_callbacks_)
-    std::move(iter.second).Run(false);
-
   // Delete this before destroying the widget, to guard against reentrancy
   // by in-process screen readers such as JAWS.
   browser_accessibility_manager_.reset();
@@ -1616,7 +1613,6 @@
     IPC_MESSAGE_HANDLER(FrameHostMsg_Detach, OnDetach)
     IPC_MESSAGE_HANDLER(FrameHostMsg_Unload_ACK, OnUnloadACK)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_VisualStateResponse, OnVisualStateResponse)
     IPC_MESSAGE_HANDLER(FrameHostMsg_DidStopLoading, OnDidStopLoading)
     IPC_MESSAGE_HANDLER(FrameHostMsg_SelectionChanged, OnSelectionChanged)
   IPC_END_MESSAGE_MAP()
@@ -1826,8 +1822,6 @@
   smart_clip_callbacks_.Clear();
 #endif  // defined(OS_ANDROID)
 
-  visual_state_callbacks_.clear();
-
   // Ensure that future remote interface requests are associated with the new
   // process's channel.
   remote_associated_interfaces_.reset();
@@ -3085,16 +3079,6 @@
 }
 #endif  // defined(OS_ANDROID)
 
-void RenderFrameHostImpl::OnVisualStateResponse(uint64_t id) {
-  auto it = visual_state_callbacks_.find(id);
-  if (it != visual_state_callbacks_.end()) {
-    std::move(it->second).Run(true);
-    visual_state_callbacks_.erase(it);
-  } else {
-    NOTREACHED() << "Received script response for unknown request";
-  }
-}
-
 void RenderFrameHostImpl::RunModalAlertDialog(
     const base::string16& alert_message,
     RunModalAlertDialogCallback response_callback) {
@@ -6617,10 +6601,7 @@
 
 void RenderFrameHostImpl::InsertVisualStateCallback(
     VisualStateCallback callback) {
-  static uint64_t next_id = 1;
-  uint64_t key = next_id++;
-  Send(new FrameMsg_VisualStateRequest(routing_id_, key));
-  visual_state_callbacks_.emplace(key, std::move(callback));
+  GetRenderWidgetHost()->InsertVisualStateCallback(std::move(callback));
 }
 
 bool RenderFrameHostImpl::IsRenderFrameCreated() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index f339978..57cf39c7 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1863,8 +1863,6 @@
   void OnDetach();
   void OnUnloadACK();
   void OnContextMenu(const UntrustworthyContextMenuParams& params);
-  void OnVisualStateResponse(uint64_t id);
-
   void OnForwardResourceTimingToParent(
       const ResourceTimingInfo& resource_timing);
   void OnDidStopLoading();
@@ -2475,8 +2473,6 @@
   // The http status code of the last committed navigation.
   int last_http_status_code_ = 0;
 
-  std::map<uint64_t, VisualStateCallback> visual_state_callbacks_;
-
   // Local root subframes directly own their RenderWidgetHost.
   // Please see comments about the GetLocalRenderWidgetHost() function.
   // TODO(kenrb): Later this will also be used on the top-level frame, when
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index 12f5110..c7a2b11 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -233,12 +233,13 @@
   return net::NetLogCaptureMode::kDefault;
 }
 
+static NetworkServiceClient* g_client = nullptr;
+
 }  // namespace
 
 network::mojom::NetworkService* GetNetworkService() {
   if (!g_network_service_remote)
     g_network_service_remote = new mojo::Remote<network::mojom::NetworkService>;
-  static NetworkServiceClient* g_client;
   if (!g_network_service_remote->is_bound() ||
       !g_network_service_remote->is_connected()) {
     bool service_was_bound = g_network_service_remote->is_bound();
@@ -445,6 +446,8 @@
 void ShutDownNetworkService() {
   delete g_network_service_remote;
   g_network_service_remote = nullptr;
+  delete g_client;
+  g_client = nullptr;
   if (g_in_process_instance) {
     GetNetworkTaskRunner()->DeleteSoon(FROM_HERE, g_in_process_instance);
     g_in_process_instance = nullptr;
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index 7de8b92..01f5cdc8 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -18,6 +18,7 @@
 #include "base/optional.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
+#include "base/time/time.h"
 #include "content/browser/permissions/permission_controller_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
@@ -167,14 +168,16 @@
   // Public GetSubscription methods on UI thread -------------------------------
 
   // Callback called on UI thread.
-  void GetSubscriptionDidGetInfoOnUI(GetSubscriptionCallback callback,
-                                     const GURL& origin,
-                                     int64_t service_worker_registration_id,
-                                     const std::string& application_server_key,
-                                     bool is_valid,
-                                     const GURL& endpoint,
-                                     const std::vector<uint8_t>& p256dh,
-                                     const std::vector<uint8_t>& auth);
+  void GetSubscriptionDidGetInfoOnUI(
+      GetSubscriptionCallback callback,
+      const GURL& origin,
+      int64_t service_worker_registration_id,
+      const std::string& application_server_key,
+      bool is_valid,
+      const GURL& endpoint,
+      const base::Optional<base::Time>& expiration_time,
+      const std::vector<uint8_t>& p256dh,
+      const std::vector<uint8_t>& auth);
 
   // Callback called on UI thread.
   void GetSubscriptionDidUnsubscribe(
@@ -216,6 +219,7 @@
   void DidRegister(RegisterData data,
                    const std::string& push_subscription_id,
                    const GURL& endpoint,
+                   const base::Optional<base::Time>& expiration_time,
                    const std::vector<uint8_t>& p256dh,
                    const std::vector<uint8_t>& auth,
                    blink::mojom::PushRegistrationStatus status);
@@ -505,10 +509,14 @@
           blink::mojom::PushRegistrationStatus::INCOGNITO_PERMISSION_DENIED));
 }
 
+// TODO(crbug.com/1104215): Handle expiration_time that is passed from push
+// service check if |expiration_time| is valid before saving it in |data| and
+// passing it back in SendSubscriptionSuccess
 void PushMessagingManager::Core::DidRegister(
     RegisterData data,
     const std::string& push_subscription_id,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth,
     blink::mojom::PushRegistrationStatus status) {
@@ -534,6 +542,8 @@
                            : blink::mojom::PushRegistrationStatus::
                                  SUCCESS_FROM_PUSH_SERVICE));
   } else {
+    // TODO(crbug.com/646721): for invalid |expiration_time| send a subscription
+    // error with a new PushRegistrationStatus
     RunOrPostTaskOnThread(
         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
         base::BindOnce(&PushMessagingManager::SendSubscriptionError, sw_parent_,
@@ -844,6 +854,7 @@
     const std::string& application_server_key,
     bool is_valid,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -861,14 +872,12 @@
     blink::mojom::PushGetRegistrationStatus status =
         blink::mojom::PushGetRegistrationStatus::SUCCESS;
 
-    // TODO(crbug.com/1104215): Get expiration_time from push_service in
-    // Core::GetSubscriptionInfoOnUI
     RunOrPostTaskOnThread(
         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
-        base::BindOnce(std::move(callback), status,
-                       blink::mojom::PushSubscription::New(
-                           endpoint, base::nullopt /* expiration_time */,
-                           std::move(options), p256dh, auth)));
+        base::BindOnce(
+            std::move(callback), status,
+            blink::mojom::PushSubscription::New(
+                endpoint, expiration_time, std::move(options), p256dh, auth)));
 
     RecordGetRegistrationStatus(status);
   } else {
@@ -930,6 +939,7 @@
   if (!push_service) {
     std::move(callback).Run(
         false /* is_valid */, GURL::EmptyGURL() /* endpoint */,
+        base::nullopt /* expiration_time */,
         std::vector<uint8_t>() /* p256dh */, std::vector<uint8_t>() /* auth */);
     return;
   }
diff --git a/content/browser/renderer_host/pepper/pepper_socket_utils.cc b/content/browser/renderer_host/pepper/pepper_socket_utils.cc
index 1926bb1..b6245b9c 100644
--- a/content/browser/renderer_host/pepper/pepper_socket_utils.cc
+++ b/content/browser/renderer_host/pepper/pepper_socket_utils.cc
@@ -221,8 +221,8 @@
             }
           }
           chrome_policy {
-            ExtensionInstallBlacklist {
-              ExtensionInstallBlacklist: {
+            ExtensionInstallBlocklist {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
@@ -265,8 +265,8 @@
             }
           }
           chrome_policy {
-            ExtensionInstallBlacklist {
-              ExtensionInstallBlacklist: {
+            ExtensionInstallBlocklist {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 94a78100..f92d55b 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -95,6 +95,7 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/ipc/common/gpu_messages.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "net/base/filename_util.h"
@@ -630,6 +631,7 @@
   blink_frame_widget_host_receiver_.reset();
   blink_frame_widget_.reset();
   frame_widget_input_handler_.reset();
+  widget_compositor_.reset();
   return std::make_pair(
       blink_frame_widget_host_receiver_.BindNewEndpointAndPassRemote(),
       blink_frame_widget_.BindNewEndpointAndPassReceiver());
@@ -644,6 +646,7 @@
   blink_frame_widget_host_receiver_.reset();
   blink_frame_widget_.reset();
   frame_widget_input_handler_.reset();
+  widget_compositor_.reset();
   blink_frame_widget_host_receiver_.Bind(std::move(frame_widget_host));
   blink_frame_widget_.Bind(std::move(frame_widget));
 }
@@ -2887,6 +2890,23 @@
   return false;
 }
 
+void RenderWidgetHostImpl::InsertVisualStateCallback(
+    VisualStateCallback callback) {
+  if (!blink_frame_widget_) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  if (!widget_compositor_) {
+    blink_frame_widget_->BindWidgetCompositor(
+        widget_compositor_.BindNewPipeAndPassReceiver());
+  }
+
+  widget_compositor_->VisualStateRequest(base::BindOnce(
+      [](VisualStateCallback callback) { std::move(callback).Run(true); },
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false)));
+}
+
 const mojo::AssociatedRemote<blink::mojom::FrameWidget>&
 RenderWidgetHostImpl::GetAssociatedFrameWidget() {
   return blink_frame_widget_;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 0d5bc90..92d86a7b 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/queue.h"
 #include "base/gtest_prod_util.h"
@@ -785,6 +786,14 @@
   // Add/ClearPendingUserActivation() for details.
   bool RemovePendingUserActivationIfAvailable();
 
+  // Roundtrips through the renderer and compositor pipeline to ensure that any
+  // changes to the contents resulting from operations executed prior to this
+  // call are visible on screen. The call completes asynchronously by running
+  // the supplied |callback| with a value of true upon successful completion and
+  // false otherwise when the widget is destroyed.
+  using VisualStateCallback = base::OnceCallback<void(bool)>;
+  void InsertVisualStateCallback(VisualStateCallback callback);
+
   const mojo::AssociatedRemote<blink::mojom::FrameWidget>&
   GetAssociatedFrameWidget();
 
@@ -1325,6 +1334,8 @@
       blink_widget_host_receiver_{this};
   mojo::AssociatedRemote<blink::mojom::Widget> blink_widget_;
 
+  mojo::Remote<blink::mojom::WidgetCompositor> widget_compositor_;
+
   base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostImpl);
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc
index fcb06c7..ce32f040 100644
--- a/content/browser/sms/sms_browsertest.cc
+++ b/content/browser/sms/sms_browsertest.cc
@@ -228,7 +228,9 @@
   ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
 }
 
-IN_PROC_BROWSER_TEST_F(SmsBrowserTest, AtMostOneSmsRequestPerOriginPerTab) {
+// Disabled test: https://crbug.com/1052385
+IN_PROC_BROWSER_TEST_F(SmsBrowserTest,
+                       DISABLED_AtMostOneSmsRequestPerOriginPerTab) {
   auto provider = std::make_unique<MockSmsProvider>();
   MockSmsProvider* mock_provider_ptr = provider.get();
   BrowserMainLoop::GetInstance()->SetSmsProviderForTesting(std::move(provider));
diff --git a/content/browser/sms/sms_service.cc b/content/browser/sms/sms_service.cc
index 96580bb..83aadbba 100644
--- a/content/browser/sms/sms_service.cc
+++ b/content/browser/sms/sms_service.cc
@@ -5,6 +5,7 @@
 #include "content/browser/sms/sms_service.h"
 
 #include <iterator>
+#include <memory>
 #include <queue>
 #include <string>
 #include <utility>
@@ -16,6 +17,7 @@
 #include "base/optional.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/sms/sms_metrics.h"
+#include "content/browser/sms/user_consent_handler.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/navigation_type.h"
 #include "content/public/browser/sms_fetcher.h"
@@ -23,6 +25,7 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-shared.h"
 
 using blink::SmsReceiverDestroyedReason;
 using blink::mojom::SmsStatus;
@@ -31,11 +34,13 @@
 
 SmsService::SmsService(
     SmsFetcher* fetcher,
+    std::unique_ptr<UserConsentHandler> consent_handler,
     const url::Origin& origin,
     RenderFrameHost* host,
     mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver)
     : FrameServiceBase(host, std::move(receiver)),
       fetcher_(fetcher),
+      consent_handler_(std::move(consent_handler)),
       origin_(origin) {
   DCHECK(fetcher_);
 }
@@ -45,13 +50,24 @@
     RenderFrameHost* host,
     mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver)
     : SmsService(fetcher,
+                 nullptr,
                  host->GetLastCommittedOrigin(),
                  host,
-                 std::move(receiver)) {}
+                 std::move(receiver)) {
+  bool needs_user_prompt =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kWebOtpBackend) == switches::kWebOtpBackendSmsVerification;
+
+  if (needs_user_prompt) {
+    consent_handler_ = std::make_unique<PromptBasedUserConsentHandler>();
+  } else {
+    consent_handler_ = std::make_unique<NoopUserConsentHandler>();
+  }
+}
 
 SmsService::~SmsService() {
   if (callback_)
-    Process(SmsStatus::kTimeout, base::nullopt);
+    CompleteRequest(SmsStatus::kTimeout);
   DCHECK(!callback_);
 }
 
@@ -72,6 +88,13 @@
 
 void SmsService::Receive(ReceiveCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(majidvp): The comment below seems incorrect. This flow is used for
+  // both prompted and unprompted backends so it is not clear if we should
+  // always cancel early. Also I don't believe that we are actually silently
+  // dropping the sms but in fact the logic cancels the request once
+  // an sms comes in and there is no delegate.
+
   // This flow relies on the delegate to display an infobar for user
   // confirmation. Cancelling the call early if no delegate is available is
   // easier to debug then silently dropping SMSes later on.
@@ -82,6 +105,7 @@
     return;
   }
 
+  // Abort the last request if there is we have not yet handled it.
   if (callback_) {
     std::move(callback_).Run(SmsStatus::kCancelled, base::nullopt);
     fetcher_->Unsubscribe(origin_, this);
@@ -90,9 +114,11 @@
   start_time_ = base::TimeTicks::Now();
   callback_ = std::move(callback);
 
-  // |one_time_code_| and prompt are still present from the previous
-  // request so a new subscription is unnecessary.
-  if (prompt_open_) {
+  // |one_time_code_| and prompt are still present from the previous request so
+  // a new subscription is unnecessary. Note that it is only safe for us to use
+  // the in flight otp with the new request since both requests belong to the
+  // same origin.
+  if (consent_handler_->is_active()) {
     // TODO(crbug.com/1024598): Add UMA histogram.
     return;
   }
@@ -102,29 +128,23 @@
 
 void SmsService::OnReceive(const std::string& one_time_code) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
   DCHECK(!one_time_code_);
   DCHECK(!start_time_.is_null());
 
-  auto now = base::TimeTicks::Now();
-  RecordSmsReceiveTime(now - start_time_);
+  receive_time_ = base::TimeTicks::Now();
+  RecordSmsReceiveTime(receive_time_ - start_time_);
 
   one_time_code_ = one_time_code;
 
-  if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kWebOtpBackend) !=
-      switches::kWebOtpBackendSmsVerification) {
-    Process(SmsStatus::kSuccess, one_time_code_);
-    return;
-  }
-
-  receive_time_ = now;
-  OpenInfoBar(one_time_code);
+  consent_handler_->RequestUserConsent(
+      render_frame_host(), origin_, one_time_code,
+      base::BindOnce(&SmsService::CompleteRequest,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void SmsService::Abort() {
   DCHECK(callback_);
-  Process(SmsStatus::kAborted, base::nullopt);
+  CompleteRequest(SmsStatus::kAborted);
 }
 
 void SmsService::NavigationEntryCommitted(
@@ -145,68 +165,39 @@
   }
 }
 
-void SmsService::OpenInfoBar(const std::string& one_time_code) {
-  WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host());
-  if (!web_contents->GetDelegate()) {
-    Process(SmsStatus::kCancelled, base::nullopt);
-    return;
+void SmsService::CompleteRequest(blink::mojom::SmsStatus status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::Optional<std::string> code = base::nullopt;
+  if (status == SmsStatus::kSuccess) {
+    DCHECK(one_time_code_);
+    code = one_time_code_;
   }
 
-  prompt_open_ = true;
-  web_contents->GetDelegate()->CreateSmsPrompt(
-      render_frame_host(), origin_, one_time_code,
-      base::BindOnce(&SmsService::OnConfirm, weak_ptr_factory_.GetWeakPtr()),
-      base::BindOnce(&SmsService::OnCancel, weak_ptr_factory_.GetWeakPtr()));
-}
+  // Record ContinueOn timing values only if we are using an asynchronous
+  // consent handler (i.e. showing user prompts).
+  if (consent_handler_->is_async()) {
+    if (status == SmsStatus::kSuccess) {
+      DCHECK(!receive_time_.is_null());
+      RecordContinueOnSuccessTime(base::TimeTicks::Now() - receive_time_);
+    } else if (status == SmsStatus::kCancelled) {
+      DCHECK(!receive_time_.is_null());
+      RecordCancelOnSuccessTime(base::TimeTicks::Now() - receive_time_);
+    }
+  }
 
-void SmsService::Process(blink::mojom::SmsStatus status,
-                         base::Optional<std::string> one_time_code) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(callback_);
-  std::move(callback_).Run(status, one_time_code);
+  if (callback_) {
+    std::move(callback_).Run(status, code);
+  }
+
   CleanUp();
 }
 
-void SmsService::OnConfirm() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  DCHECK(one_time_code_);
-  DCHECK(!receive_time_.is_null());
-  RecordContinueOnSuccessTime(base::TimeTicks::Now() - receive_time_);
-
-  prompt_open_ = false;
-
-  if (!callback_) {
-    // Cleanup since request has been aborted while prompt is up.
-    CleanUp();
-    return;
-  }
-  Process(SmsStatus::kSuccess, one_time_code_);
-}
-
-void SmsService::OnCancel() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Record only when SMS has already been received.
-  DCHECK(!receive_time_.is_null());
-  RecordCancelOnSuccessTime(base::TimeTicks::Now() - receive_time_);
-
-  prompt_open_ = false;
-
-  if (!callback_) {
-    // Cleanup since request has been aborted while prompt is up.
-    CleanUp();
-    return;
-  }
-  Process(SmsStatus::kCancelled, base::nullopt);
-}
-
 void SmsService::CleanUp() {
   // Skip resetting |one_time_code_|, |sms| and |receive_time_| while prompt is
   // still open in case it needs to be returned to the next incoming request
   // upon prompt confirmation.
-  if (!prompt_open_) {
+  if (!consent_handler_->is_active()) {
     one_time_code_.reset();
     receive_time_ = base::TimeTicks();
   }
diff --git a/content/browser/sms/sms_service.h b/content/browser/sms/sms_service.h
index eaf075f..b660c05 100644
--- a/content/browser/sms/sms_service.h
+++ b/content/browser/sms/sms_service.h
@@ -13,6 +13,7 @@
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "content/browser/sms/sms_queue.h"
+#include "content/browser/sms/user_consent_handler.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/frame_service_base.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -44,6 +45,7 @@
              RenderFrameHost*,
              mojo::PendingReceiver<blink::mojom::SmsReceiver>);
   SmsService(SmsFetcher*,
+             std::unique_ptr<UserConsentHandler> consent_handler,
              const url::Origin&,
              RenderFrameHost*,
              mojo::PendingReceiver<blink::mojom::SmsReceiver>);
@@ -56,31 +58,27 @@
   // content::SmsQueue::Subscriber
   void OnReceive(const std::string& one_time_code) override;
 
+  // Completes the in-flight sms otp code request. Invokes the receive callback,
+  // if one is available, with the provided status code and the existing one
+  // time code.
+  void CompleteRequest(blink::mojom::SmsStatus);
+
  protected:
   // content::WebContentsObserver:
   void NavigationEntryCommitted(
       const content::LoadCommittedDetails& load_details) override;
 
  private:
-  void OpenInfoBar(const std::string& one_time_code);
-  void Process(blink::mojom::SmsStatus,
-               base::Optional<std::string> one_time_code);
   void CleanUp();
 
-  // Called when the user manually clicks the 'Enter code' button.
-  void OnConfirm();
-  // Called when the user manually dismisses the infobar.
-  void OnCancel();
 
   // |fetcher_| is safe because all instances of SmsFetcher are owned
   // by the browser context, which transitively (through RenderFrameHost) owns
   // and outlives this class.
   SmsFetcher* fetcher_;
+  std::unique_ptr<UserConsentHandler> consent_handler_;
 
   const url::Origin origin_;
-
-  bool prompt_open_ = false;
-
   ReceiveCallback callback_;
   base::Optional<std::string> one_time_code_;
   base::TimeTicks start_time_;
diff --git a/content/browser/sms/sms_service_unittest.cc b/content/browser/sms/sms_service_unittest.cc
index 4b24e4e..4c9a61f 100644
--- a/content/browser/sms/sms_service_unittest.cc
+++ b/content/browser/sms/sms_service_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/sms/sms_service.h"
 
+#include <memory>
 #include <string>
 #include <utility>
 
@@ -20,6 +21,8 @@
 #include "content/browser/sms/sms_fetcher_impl.h"
 #include "content/browser/sms/test/mock_sms_provider.h"
 #include "content/browser/sms/test/mock_sms_web_contents_delegate.h"
+#include "content/browser/sms/test/mock_user_consent_handler.h"
+#include "content/browser/sms/user_consent_handler.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/sms_fetcher.h"
 #include "content/public/common/content_switches.h"
@@ -32,6 +35,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/sms/sms_receiver_destroyed_reason.h"
+#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-shared.h"
 #include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h"
 
 using base::BindLambdaForTesting;
@@ -56,6 +60,8 @@
 
 const char kTestUrl[] = "https://www.google.com";
 
+class StubWebContentsDelegate : public WebContentsDelegate {};
+
 // Service encapsulates a SmsService endpoint, with all of its dependencies
 // mocked out (and the common plumbing needed to inject them), and a
 // mojo::Remote<SmsReceiver> endpoint that tests can use to make requests.
@@ -63,55 +69,32 @@
 // also exposes the low level mocks that enables tests to set expectations and
 // control the testing environment.
 class Service {
- public:
-  Service(WebContents* web_contents, const Origin& origin)
-      : fetcher_(web_contents->GetBrowserContext(), &provider_) {
-    WebContentsImpl* web_contents_impl =
-        reinterpret_cast<WebContentsImpl*>(web_contents);
-    web_contents_impl->SetDelegate(&delegate_);
+ protected:
+  Service(WebContents* web_contents,
+          const Origin& origin,
+          std::unique_ptr<UserConsentHandler> user_consent_handler)
+      : fetcher_(web_contents->GetBrowserContext(), &provider_),
+        consent_handler_(user_consent_handler.get()) {
+    // Set a stub delegate because sms service checks existence of delegate and
+    // cancels requests early if one does not exist.
+    web_contents->SetDelegate(&contents_delegate_);
+
     service_ = std::make_unique<SmsService>(
-        &fetcher_, origin, web_contents->GetMainFrame(),
+        &fetcher_, std::move(user_consent_handler), origin,
+        web_contents->GetMainFrame(),
         service_remote_.BindNewPipeAndPassReceiver());
   }
 
-  Service(WebContents* web_contents)
+ public:
+  explicit Service(WebContents* web_contents)
       : Service(web_contents,
-                web_contents->GetMainFrame()->GetLastCommittedOrigin()) {}
+                web_contents->GetMainFrame()->GetLastCommittedOrigin(),
+                /* avoid showing user prompts */
+                std::make_unique<NoopUserConsentHandler>()) {}
 
   NiceMock<MockSmsProvider>* provider() { return &provider_; }
   SmsFetcher* fetcher() { return &fetcher_; }
-
-  void CreateSmsPrompt(RenderFrameHost* rfh) {
-    EXPECT_CALL(delegate_, CreateSmsPrompt(rfh, _, _, _, _))
-        .WillOnce(Invoke([=](RenderFrameHost*, const Origin& origin,
-                             const std::string&, base::OnceClosure on_confirm,
-                             base::OnceClosure on_cancel) {
-          confirm_callback_ = std::move(on_confirm);
-          dismiss_callback_ = std::move(on_cancel);
-        }));
-  }
-
-  void ConfirmPrompt() {
-    if (!confirm_callback_.is_null()) {
-      std::move(confirm_callback_).Run();
-      dismiss_callback_.Reset();
-      return;
-    }
-    FAIL() << "SmsInfobar not available";
-  }
-
-  void DismissPrompt() {
-    if (dismiss_callback_.is_null()) {
-      FAIL() << "SmsInfobar not available";
-      return;
-    }
-    std::move(dismiss_callback_).Run();
-    confirm_callback_.Reset();
-  }
-
-  bool IsPromptOpen() const {
-    return !confirm_callback_.is_null() || !dismiss_callback_.is_null();
-  }
+  UserConsentHandler* consent_handler() { return consent_handler_; }
 
   void MakeRequest(SmsReceiver::ReceiveCallback callback) {
     service_remote_->Receive(std::move(callback));
@@ -124,13 +107,12 @@
   }
 
  private:
-  NiceMock<MockSmsWebContentsDelegate> delegate_;
+  StubWebContentsDelegate contents_delegate_;
   NiceMock<MockSmsProvider> provider_;
   SmsFetcherImpl fetcher_;
+  UserConsentHandler* consent_handler_;
   mojo::Remote<blink::mojom::SmsReceiver> service_remote_;
   std::unique_ptr<SmsService> service_;
-  base::OnceClosure confirm_callback_;
-  base::OnceClosure dismiss_callback_;
 };
 
 class SmsServiceTest : public RenderViewHostTestHarness {
@@ -138,12 +120,6 @@
   SmsServiceTest() = default;
   ~SmsServiceTest() override = default;
 
-  void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
-    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kWebOtpBackend, switches::kWebOtpBackendSmsVerification);
-  }
-
   void ExpectDestroyedReasonCount(SmsReceiverDestroyedReason bucket,
                                   int32_t count) {
     histogram_tester_.ExpectBucketCount("Blink.Sms.Receive.DestroyedReason",
@@ -169,11 +145,9 @@
 
   base::RunLoop loop;
 
-  service.CreateSmsPrompt(main_rfh());
 
   EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
     service.NotifyReceive(GURL(kTestUrl), "hi");
-    service.ConfirmPrompt();
   }));
 
   service.MakeRequest(BindLambdaForTesting(
@@ -196,11 +170,8 @@
   {
     base::RunLoop loop;
 
-    service.CreateSmsPrompt(main_rfh());
-
     EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
       service.NotifyReceive(GURL(kTestUrl), "first");
-      service.ConfirmPrompt();
     }));
 
     service.MakeRequest(BindLambdaForTesting(
@@ -216,11 +187,8 @@
   {
     base::RunLoop loop;
 
-    service.CreateSmsPrompt(main_rfh());
-
     EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
       service.NotifyReceive(GURL(kTestUrl), "second");
-      service.ConfirmPrompt();
     }));
 
     service.MakeRequest(BindLambdaForTesting(
@@ -244,14 +212,11 @@
 
   base::RunLoop sms_loop;
 
-  service.CreateSmsPrompt(main_rfh());
-
   EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
     // Delivers an SMS from an unrelated origin first and expect the
     // receiver to ignore it.
     service.NotifyReceive(GURL("http://b.com"), "wrong");
     service.NotifyReceive(GURL(kTestUrl), "right");
-    service.ConfirmPrompt();
   }));
 
   service.MakeRequest(
@@ -278,14 +243,12 @@
 
   base::RunLoop sms_loop;
 
-  service.CreateSmsPrompt(main_rfh());
 
   EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
     // Delivers two SMSes for the same origin, even if only one was being
     // expected.
     ASSERT_TRUE(service.fetcher()->HasSubscribers());
     service.NotifyReceive(GURL(kTestUrl), "first");
-    service.ConfirmPrompt();
     ASSERT_FALSE(service.fetcher()->HasSubscribers());
     service.NotifyReceive(GURL(kTestUrl), "second");
   }));
@@ -316,14 +279,10 @@
 
   base::RunLoop sms1_loop, sms2_loop;
 
-  // Expect SMS Prompt to be created once.
-  service.CreateSmsPrompt(main_rfh());
-
   EXPECT_CALL(*service.provider(), Retrieve(_))
       .WillOnce(Return())
       .WillOnce(Invoke([&service]() {
         service.NotifyReceive(GURL(kTestUrl), "second");
-        service.ConfirmPrompt();
       }));
 
   service.MakeRequest(
@@ -354,52 +313,6 @@
   EXPECT_EQ(SmsStatus::kSuccess, sms_status2);
 }
 
-TEST_F(SmsServiceTest, SecondRequestDuringPrompt) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  SmsStatus sms_status1;
-  Optional<string> response1;
-  SmsStatus sms_status2;
-  Optional<string> response2;
-
-  base::RunLoop sms_loop;
-
-  // Expect SMS Prompt to be created once.
-  service.CreateSmsPrompt(main_rfh());
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "second");
-  }));
-
-  // First request.
-  service.MakeRequest(
-      BindLambdaForTesting([&sms_status1, &response1, &service](
-                               SmsStatus status, const Optional<string>& otp) {
-        sms_status1 = status;
-        response1 = otp;
-        service.ConfirmPrompt();
-      }));
-
-  // Make second request before confirming prompt.
-  service.MakeRequest(
-      BindLambdaForTesting([&sms_status2, &response2, &sms_loop](
-                               SmsStatus status, const Optional<string>& otp) {
-        sms_status2 = status;
-        response2 = otp;
-        sms_loop.Quit();
-      }));
-
-  sms_loop.Run();
-
-  EXPECT_EQ(base::nullopt, response1);
-  EXPECT_EQ(SmsStatus::kCancelled, sms_status1);
-
-  EXPECT_EQ("second", response2.value());
-  EXPECT_EQ(SmsStatus::kSuccess, sms_status2);
-}
-
 TEST_F(SmsServiceTest, CleansUp) {
   NavigateAndCommit(GURL(kTestUrl));
 
@@ -440,58 +353,6 @@
   ASSERT_FALSE(fetcher.HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, PromptsDialog) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  base::RunLoop loop;
-
-  service.CreateSmsPrompt(main_rfh());
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "hi");
-    service.ConfirmPrompt();
-  }));
-
-  service.MakeRequest(BindLambdaForTesting(
-      [&loop](SmsStatus status, const Optional<string>& otp) {
-        EXPECT_EQ("hi", otp.value());
-        EXPECT_EQ(SmsStatus::kSuccess, status);
-        loop.Quit();
-      }));
-
-  loop.Run();
-
-  ASSERT_FALSE(service.fetcher()->HasSubscribers());
-}
-
-TEST_F(SmsServiceTest, Cancel) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  base::RunLoop loop;
-
-  service.CreateSmsPrompt(main_rfh());
-
-  service.MakeRequest(BindLambdaForTesting(
-      [&loop](SmsStatus status, const Optional<string>& otp) {
-        EXPECT_EQ(SmsStatus::kCancelled, status);
-        EXPECT_EQ(base::nullopt, otp);
-        loop.Quit();
-      }));
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "hi");
-    service.DismissPrompt();
-  }));
-
-  loop.Run();
-
-  ASSERT_FALSE(service.fetcher()->HasSubscribers());
-}
-
 TEST_F(SmsServiceTest, CancelForNoDelegate) {
   NavigateAndCommit(GURL(kTestUrl));
 
@@ -536,182 +397,6 @@
   ASSERT_FALSE(service.fetcher()->HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, AbortWhilePrompt) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  base::RunLoop loop;
-
-  service.CreateSmsPrompt(main_rfh());
-
-  service.MakeRequest(BindLambdaForTesting(
-      [&loop](SmsStatus status, const Optional<string>& otp) {
-        EXPECT_EQ(SmsStatus::kAborted, status);
-        EXPECT_EQ(base::nullopt, otp);
-        loop.Quit();
-      }));
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "ABC");
-    EXPECT_TRUE(service.IsPromptOpen());
-    service.AbortRequest();
-  }));
-
-  loop.Run();
-
-  ASSERT_FALSE(service.fetcher()->HasSubscribers());
-
-  service.ConfirmPrompt();
-}
-
-TEST_F(SmsServiceTest, RequestAfterAbortWhilePrompt) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  {
-    base::RunLoop loop;
-
-    service.CreateSmsPrompt(main_rfh());
-
-    service.MakeRequest(BindLambdaForTesting(
-        [&loop](SmsStatus status, const Optional<string>& otp) {
-          EXPECT_EQ(SmsStatus::kAborted, status);
-          EXPECT_EQ(base::nullopt, otp);
-          loop.Quit();
-        }));
-
-    EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-      service.NotifyReceive(GURL(kTestUrl), "hi");
-      EXPECT_TRUE(service.IsPromptOpen());
-      service.AbortRequest();
-    }));
-
-    loop.Run();
-  }
-
-  ASSERT_FALSE(service.fetcher()->HasSubscribers());
-
-  // Confirm to dismiss prompt for a request that has already aborted.
-  service.ConfirmPrompt();
-
-  {
-    base::RunLoop loop;
-
-    service.CreateSmsPrompt(main_rfh());
-
-    service.MakeRequest(BindLambdaForTesting(
-        [&loop](SmsStatus status, const Optional<string>& otp) {
-          // Verify that the 2nd request completes successfully after prompt
-          // confirmation.
-          EXPECT_EQ(SmsStatus::kSuccess, status);
-          EXPECT_EQ("hi2", otp.value());
-          loop.Quit();
-        }));
-
-    EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-      service.NotifyReceive(GURL(kTestUrl), "hi2");
-      service.ConfirmPrompt();
-    }));
-
-    loop.Run();
-  }
-}
-
-TEST_F(SmsServiceTest, SecondRequestWhilePrompt) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  base::RunLoop callback_loop1, callback_loop2, req_loop;
-
-  service.CreateSmsPrompt(main_rfh());
-
-  service.MakeRequest(BindLambdaForTesting(
-      [&callback_loop1](SmsStatus status, const Optional<string>& otp) {
-        EXPECT_EQ(SmsStatus::kAborted, status);
-        EXPECT_EQ(base::nullopt, otp);
-        callback_loop1.Quit();
-      }));
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "hi");
-    service.AbortRequest();
-  }));
-
-  callback_loop1.Run();
-
-  base::ThreadTaskRunnerHandle::Get()->PostTaskAndReply(
-      FROM_HERE, BindLambdaForTesting([&]() {
-        service.MakeRequest(BindLambdaForTesting(
-            [&callback_loop2](SmsStatus status, const Optional<string>& otp) {
-              EXPECT_EQ(SmsStatus::kSuccess, status);
-              EXPECT_EQ("hi", otp.value());
-              callback_loop2.Quit();
-            }));
-      }),
-      req_loop.QuitClosure());
-
-  req_loop.Run();
-
-  // Simulate pressing 'Verify' on Infobar.
-  service.ConfirmPrompt();
-
-  callback_loop2.Run();
-
-  ASSERT_FALSE(service.fetcher()->HasSubscribers());
-}
-
-TEST_F(SmsServiceTest, RecordTimeMetricsForContinueOnSuccess) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  base::RunLoop loop;
-
-  service.CreateSmsPrompt(main_rfh());
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "ABC");
-    service.ConfirmPrompt();
-  }));
-
-  service.MakeRequest(BindLambdaForTesting(
-      [&loop](SmsStatus status, const Optional<string>& otp) { loop.Quit(); }));
-
-  loop.Run();
-
-  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeContinueOnSuccess",
-                                      1);
-  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeSmsReceive", 1);
-}
-
-TEST_F(SmsServiceTest, RecordMetricsForCancelOnSuccess) {
-  NavigateAndCommit(GURL(kTestUrl));
-
-  Service service(web_contents());
-
-  // Histogram will be recorded if the SMS has already arrived.
-  base::RunLoop loop;
-
-  service.CreateSmsPrompt(main_rfh());
-
-  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
-    service.NotifyReceive(GURL(kTestUrl), "hi");
-    service.DismissPrompt();
-  }));
-
-  service.MakeRequest(BindLambdaForTesting(
-      [&loop](SmsStatus status, const Optional<string>& otp) { loop.Quit(); }));
-
-  loop.Run();
-
-  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeCancelOnSuccess",
-                                      1);
-  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeSmsReceive", 1);
-}
-
 TEST_F(SmsServiceTest, RecordMetricsForNewPage) {
   // This test depends on the page being destroyed on navigation.
   web_contents()->GetController().GetBackForwardCache().DisableForTesting(
@@ -791,6 +476,284 @@
   ExpectDestroyedReasonCount(SmsReceiverDestroyedReason::kNavigateSamePage, 1);
 }
 
+// Following tests exercise parts of sms service logic that depend on user
+// prompting. In particular how we handle incoming request while there is an
+// active in-flight prompts.
+
+class ServiceWithPrompt : public Service {
+ public:
+  explicit ServiceWithPrompt(WebContents* web_contents)
+      : Service(web_contents,
+                web_contents->GetMainFrame()->GetLastCommittedOrigin(),
+                base::WrapUnique(new NiceMock<MockUserConsentHandler>())) {
+    mock_handler_ =
+        static_cast<NiceMock<MockUserConsentHandler>*>(consent_handler());
+  }
+
+  void ExpectRequestUserConsent() {
+    EXPECT_CALL(*mock_handler_, RequestUserConsent(_, _, _, _))
+        .WillOnce(
+            Invoke([=](RenderFrameHost*, const Origin& origin,
+                       const std::string&, CompletionCallback on_complete) {
+              on_complete_callback_ = std::move(on_complete);
+            }));
+
+    EXPECT_CALL(*mock_handler_, is_async()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_handler_, is_active()).WillRepeatedly(Invoke([=]() {
+      return !on_complete_callback_.is_null();
+    }));
+  }
+
+  void ConfirmPrompt() {
+    if (on_complete_callback_.is_null()) {
+      FAIL() << "User prompt is not available";
+      return;
+    }
+    std::move(on_complete_callback_).Run(SmsStatus::kSuccess);
+    on_complete_callback_.Reset();
+  }
+
+  void DismissPrompt() {
+    if (on_complete_callback_.is_null()) {
+      FAIL() << "User prompt is not available";
+      return;
+    }
+    std::move(on_complete_callback_).Run(SmsStatus::kCancelled);
+    on_complete_callback_.Reset();
+  }
+
+  bool IsPromptOpen() const { return !on_complete_callback_.is_null(); }
+
+ private:
+  // The actual consent handler is owned by SmsService but we keep a ptr to
+  // it so it can be used to set expectations for it. It is safe since the
+  // sms service lifetime is the same as this object.
+  NiceMock<MockUserConsentHandler>* mock_handler_;
+  CompletionCallback on_complete_callback_;
+};
+
+TEST_F(SmsServiceTest, SecondRequestDuringPrompt) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  ServiceWithPrompt service(web_contents());
+
+  SmsStatus sms_status1;
+  Optional<string> response1;
+  SmsStatus sms_status2;
+  Optional<string> response2;
+
+  base::RunLoop sms_loop;
+
+  // Expect SMS Prompt to be created once.
+  service.ExpectRequestUserConsent();
+
+  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+    service.NotifyReceive(GURL(kTestUrl), "second");
+  }));
+
+  // First request.
+  service.MakeRequest(
+      BindLambdaForTesting([&sms_status1, &response1, &service](
+                               SmsStatus status, const Optional<string>& otp) {
+        sms_status1 = status;
+        response1 = otp;
+        service.ConfirmPrompt();
+      }));
+
+  // Make second request before confirming prompt.
+  service.MakeRequest(
+      BindLambdaForTesting([&sms_status2, &response2, &sms_loop](
+                               SmsStatus status, const Optional<string>& otp) {
+        sms_status2 = status;
+        response2 = otp;
+        sms_loop.Quit();
+      }));
+
+  sms_loop.Run();
+
+  EXPECT_EQ(base::nullopt, response1);
+  EXPECT_EQ(SmsStatus::kCancelled, sms_status1);
+
+  EXPECT_EQ("second", response2.value());
+  EXPECT_EQ(SmsStatus::kSuccess, sms_status2);
+}
+
+TEST_F(SmsServiceTest, AbortWhilePrompt) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  ServiceWithPrompt service(web_contents());
+
+  base::RunLoop loop;
+
+  service.ExpectRequestUserConsent();
+
+  service.MakeRequest(BindLambdaForTesting(
+      [&loop](SmsStatus status, const Optional<string>& otp) {
+        EXPECT_EQ(SmsStatus::kAborted, status);
+        EXPECT_EQ(base::nullopt, otp);
+        loop.Quit();
+      }));
+
+  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+    service.NotifyReceive(GURL(kTestUrl), "ABC");
+    EXPECT_TRUE(service.IsPromptOpen());
+    service.AbortRequest();
+  }));
+
+  loop.Run();
+
+  ASSERT_FALSE(service.fetcher()->HasSubscribers());
+
+  service.ConfirmPrompt();
+}
+
+TEST_F(SmsServiceTest, RequestAfterAbortWhilePrompt) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  ServiceWithPrompt service(web_contents());
+
+  {
+    base::RunLoop loop;
+
+    service.ExpectRequestUserConsent();
+
+    service.MakeRequest(BindLambdaForTesting(
+        [&loop](SmsStatus status, const Optional<string>& otp) {
+          EXPECT_EQ(SmsStatus::kAborted, status);
+          EXPECT_EQ(base::nullopt, otp);
+          loop.Quit();
+        }));
+
+    EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+      service.NotifyReceive(GURL(kTestUrl), "hi");
+      EXPECT_TRUE(service.IsPromptOpen());
+      service.AbortRequest();
+    }));
+
+    loop.Run();
+  }
+
+  ASSERT_FALSE(service.fetcher()->HasSubscribers());
+
+  // Confirm to dismiss prompt for a request that has already aborted.
+  service.ConfirmPrompt();
+
+  {
+    base::RunLoop loop;
+
+    service.ExpectRequestUserConsent();
+
+    service.MakeRequest(BindLambdaForTesting(
+        [&loop](SmsStatus status, const Optional<string>& otp) {
+          // Verify that the 2nd request completes successfully after prompt
+          // confirmation.
+          EXPECT_EQ(SmsStatus::kSuccess, status);
+          EXPECT_EQ("hi2", otp.value());
+          loop.Quit();
+        }));
+
+    EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+      service.NotifyReceive(GURL(kTestUrl), "hi2");
+      service.ConfirmPrompt();
+    }));
+
+    loop.Run();
+  }
+}
+
+TEST_F(SmsServiceTest, SecondRequestWhilePrompt) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  ServiceWithPrompt service(web_contents());
+
+  base::RunLoop callback_loop1, callback_loop2, req_loop;
+
+  service.ExpectRequestUserConsent();
+
+  service.MakeRequest(BindLambdaForTesting(
+      [&callback_loop1](SmsStatus status, const Optional<string>& otp) {
+        EXPECT_EQ(SmsStatus::kAborted, status);
+        EXPECT_EQ(base::nullopt, otp);
+        callback_loop1.Quit();
+      }));
+
+  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+    service.NotifyReceive(GURL(kTestUrl), "hi");
+    service.AbortRequest();
+  }));
+
+  callback_loop1.Run();
+
+  base::ThreadTaskRunnerHandle::Get()->PostTaskAndReply(
+      FROM_HERE, BindLambdaForTesting([&]() {
+        service.MakeRequest(BindLambdaForTesting(
+            [&callback_loop2](SmsStatus status, const Optional<string>& otp) {
+              EXPECT_EQ(SmsStatus::kSuccess, status);
+              EXPECT_EQ("hi", otp.value());
+              callback_loop2.Quit();
+            }));
+      }),
+      req_loop.QuitClosure());
+
+  req_loop.Run();
+
+  // Simulate pressing 'Verify' on Infobar.
+  service.ConfirmPrompt();
+
+  callback_loop2.Run();
+
+  ASSERT_FALSE(service.fetcher()->HasSubscribers());
+}
+
+TEST_F(SmsServiceTest, RecordTimeMetricsForContinueOnSuccess) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  ServiceWithPrompt service(web_contents());
+
+  base::RunLoop loop;
+
+  service.ExpectRequestUserConsent();
+
+  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+    service.NotifyReceive(GURL(kTestUrl), "ABC");
+    service.ConfirmPrompt();
+  }));
+
+  service.MakeRequest(BindLambdaForTesting(
+      [&loop](SmsStatus status, const Optional<string>& otp) { loop.Quit(); }));
+
+  loop.Run();
+
+  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeContinueOnSuccess",
+                                      1);
+  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeSmsReceive", 1);
+}
+
+TEST_F(SmsServiceTest, RecordMetricsForCancelOnSuccess) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  ServiceWithPrompt service(web_contents());
+
+  // Histogram will be recorded if the SMS has already arrived.
+  base::RunLoop loop;
+
+  service.ExpectRequestUserConsent();
+
+  EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
+    service.NotifyReceive(GURL(kTestUrl), "hi");
+    service.DismissPrompt();
+  }));
+
+  service.MakeRequest(BindLambdaForTesting(
+      [&loop](SmsStatus status, const Optional<string>& otp) { loop.Quit(); }));
+
+  loop.Run();
+
+  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeCancelOnSuccess",
+                                      1);
+  histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeSmsReceive", 1);
+}
+
 TEST_F(SmsServiceTest, RecordMetricsForExistingPage) {
   // This test depends on the page being destroyed on navigation.
   web_contents()->GetController().GetBackForwardCache().DisableForTesting(
diff --git a/content/browser/sms/test/mock_user_consent_handler.cc b/content/browser/sms/test/mock_user_consent_handler.cc
new file mode 100644
index 0000000..d5d0e8e
--- /dev/null
+++ b/content/browser/sms/test/mock_user_consent_handler.cc
@@ -0,0 +1,13 @@
+// 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 "content/browser/sms/test/mock_user_consent_handler.h"
+
+namespace content {
+
+MockUserConsentHandler::MockUserConsentHandler() = default;
+
+MockUserConsentHandler::~MockUserConsentHandler() = default;
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/sms/test/mock_user_consent_handler.h b/content/browser/sms/test/mock_user_consent_handler.h
new file mode 100644
index 0000000..b9241f7c
--- /dev/null
+++ b/content/browser/sms/test/mock_user_consent_handler.h
@@ -0,0 +1,30 @@
+// 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 CONTENT_BROWSER_SMS_TEST_MOCK_USER_CONSENT_HANDLER_H_
+#define CONTENT_BROWSER_SMS_TEST_MOCK_USER_CONSENT_HANDLER_H_
+
+#include "base/macros.h"
+#include "content/browser/sms/user_consent_handler.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+
+class MockUserConsentHandler : public UserConsentHandler {
+ public:
+  MockUserConsentHandler();
+  ~MockUserConsentHandler();
+  MOCK_METHOD(void,
+              RequestUserConsent,
+              (RenderFrameHost * frame_host,
+               const url::Origin& origin,
+               const std::string& one_time_code,
+               CompletionCallback on_complete),
+              (override));
+  MOCK_METHOD(bool, is_active, (), (const, override));
+  MOCK_METHOD(bool, is_async, (), (const, override));
+};
+
+}  // namespace content
+#endif  // CONTENT_BROWSER_SMS_TEST_MOCK_USER_CONSENT_HANDLER_H_
\ No newline at end of file
diff --git a/content/browser/sms/user_consent_handler.cc b/content/browser/sms/user_consent_handler.cc
new file mode 100644
index 0000000..c6b64af
--- /dev/null
+++ b/content/browser/sms/user_consent_handler.cc
@@ -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.
+
+#include "content/browser/sms/user_consent_handler.h"
+#include "base/callback.h"
+#include "content/browser/sms/sms_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+
+using blink::mojom::SmsStatus;
+
+namespace content {
+
+NoopUserConsentHandler::~NoopUserConsentHandler() = default;
+
+void NoopUserConsentHandler::RequestUserConsent(
+    RenderFrameHost* frame_host,
+    const url::Origin& origin,
+    const std::string& one_time_code,
+    CompletionCallback on_complete) {
+  std::move(on_complete).Run(SmsStatus::kSuccess);
+}
+
+bool NoopUserConsentHandler::is_active() const {
+  return false;
+}
+bool NoopUserConsentHandler::is_async() const {
+  return false;
+}
+
+PromptBasedUserConsentHandler::PromptBasedUserConsentHandler() = default;
+PromptBasedUserConsentHandler::~PromptBasedUserConsentHandler() = default;
+
+void PromptBasedUserConsentHandler::RequestUserConsent(
+    RenderFrameHost* frame_host,
+    const url::Origin& origin,
+    const std::string& one_time_code,
+    CompletionCallback on_complete) {
+  WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(frame_host);
+  if (!web_contents->GetDelegate()) {
+    std::move(on_complete).Run(SmsStatus::kCancelled);
+    return;
+  }
+
+  on_complete_ = std::move(on_complete);
+  is_prompt_open_ = true;
+  web_contents->GetDelegate()->CreateSmsPrompt(
+      frame_host, origin, one_time_code,
+      base::BindOnce(&PromptBasedUserConsentHandler::OnConfirm,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&PromptBasedUserConsentHandler::OnCancel,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+bool PromptBasedUserConsentHandler::is_active() const {
+  return is_prompt_open_;
+}
+bool PromptBasedUserConsentHandler::is_async() const {
+  return true;
+}
+
+void PromptBasedUserConsentHandler::OnConfirm() {
+  is_prompt_open_ = false;
+  std::move(on_complete_).Run(SmsStatus::kSuccess);
+}
+
+void PromptBasedUserConsentHandler::OnCancel() {
+  is_prompt_open_ = false;
+  std::move(on_complete_).Run(SmsStatus::kCancelled);
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/sms/user_consent_handler.h b/content/browser/sms/user_consent_handler.h
new file mode 100644
index 0000000..a54cf61
--- /dev/null
+++ b/content/browser/sms/user_consent_handler.h
@@ -0,0 +1,72 @@
+// 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 CONTENT_BROWSER_SMS_USER_CONSENT_HANDLER_H_
+#define CONTENT_BROWSER_SMS_USER_CONSENT_HANDLER_H_
+
+#include "base/callback_forward.h"
+#include "content/common/content_export.h"
+#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-shared.h"
+#include "url/origin.h"
+
+namespace content {
+
+class RenderFrameHost;
+
+using CompletionCallback = base::OnceCallback<void(blink::mojom::SmsStatus)>;
+
+class CONTENT_EXPORT UserConsentHandler {
+ public:
+  virtual ~UserConsentHandler() = default;
+
+  // Ask for the user consent. Once the process is complete it invokes
+  // |on_complete| callback with the appropriate status.
+  virtual void RequestUserConsent(RenderFrameHost* frame_host,
+                                  const url::Origin& origin,
+                                  const std::string& one_time_code,
+                                  CompletionCallback on_complete) = 0;
+
+  // Returns true if it is still processing an inflight request.
+  // Note that this always returns false for not asynchronous handlers.
+  virtual bool is_active() const = 0;
+
+  // Returns true if this handler processes request asynchronously.
+  virtual bool is_async() const = 0;
+};
+
+class CONTENT_EXPORT NoopUserConsentHandler : public UserConsentHandler {
+ public:
+  ~NoopUserConsentHandler() override;
+  void RequestUserConsent(RenderFrameHost* frame_host,
+                          const url::Origin& origin,
+                          const std::string& one_time_code,
+                          CompletionCallback on_complete) override;
+  bool is_active() const override;
+  bool is_async() const override;
+};
+
+class CONTENT_EXPORT PromptBasedUserConsentHandler : public UserConsentHandler {
+ public:
+  PromptBasedUserConsentHandler();
+  ~PromptBasedUserConsentHandler() override;
+  void RequestUserConsent(RenderFrameHost* frame_host,
+                          const url::Origin& origin,
+                          const std::string& one_time_code,
+                          CompletionCallback on_complete) override;
+  bool is_active() const override;
+  bool is_async() const override;
+
+  void OnConfirm();
+  void OnCancel();
+
+ private:
+  bool is_prompt_open_{false};
+  CompletionCallback on_complete_;
+
+  base::WeakPtrFactory<PromptBasedUserConsentHandler> weak_ptr_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SMS_USER_CONSENT_HANDLER_H_
\ No newline at end of file
diff --git a/content/browser/sms/user_consent_handler_unittest.cc b/content/browser/sms/user_consent_handler_unittest.cc
new file mode 100644
index 0000000..59f48f5
--- /dev/null
+++ b/content/browser/sms/user_consent_handler_unittest.cc
@@ -0,0 +1,157 @@
+// 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 "content/browser/sms/user_consent_handler.h"
+
+#include "base/callback.h"
+#include "base/test/bind_test_util.h"
+#include "content/browser/sms/test/mock_sms_web_contents_delegate.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+using ::testing::_;
+using ::testing::ByMove;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::StrictMock;
+using url::Origin;
+
+namespace content {
+
+namespace {
+using blink::mojom::SmsStatus;
+
+const char kTestUrl[] = "https://testing.test";
+
+class PromptBasedUserConsentHandlerTest : public RenderViewHostTestHarness {
+ public:
+  void SetUp() override {
+    RenderViewHostTestHarness::SetUp();
+    WebContentsImpl* web_contents_impl =
+        reinterpret_cast<WebContentsImpl*>(web_contents());
+    web_contents_impl->SetDelegate(&delegate_);
+  }
+
+  void ExpectCreateSmsPrompt(RenderFrameHost* rfh,
+                             const url::Origin& origin,
+                             const std::string& one_time_code) {
+    EXPECT_CALL(delegate_, CreateSmsPrompt(rfh, origin, one_time_code, _, _))
+        .WillOnce(Invoke([=](RenderFrameHost*, const Origin& origin,
+                             const std::string&, base::OnceClosure on_confirm,
+                             base::OnceClosure on_cancel) {
+          confirm_callback_ = std::move(on_confirm);
+          dismiss_callback_ = std::move(on_cancel);
+        }));
+  }
+
+  void ExpectNoSmsPrompt() {
+    EXPECT_CALL(delegate_, CreateSmsPrompt(_, _, _, _, _)).Times(0);
+  }
+
+  void ConfirmPrompt() {
+    if (!confirm_callback_.is_null()) {
+      std::move(confirm_callback_).Run();
+      dismiss_callback_.Reset();
+      return;
+    }
+    FAIL() << "SmsInfobar not available";
+  }
+
+  void DismissPrompt() {
+    if (dismiss_callback_.is_null()) {
+      FAIL() << "SmsInfobar not available";
+      return;
+    }
+    std::move(dismiss_callback_).Run();
+    confirm_callback_.Reset();
+  }
+
+ private:
+  NiceMock<MockSmsWebContentsDelegate> delegate_;
+  base::OnceClosure confirm_callback_;
+  base::OnceClosure dismiss_callback_;
+};
+
+TEST_F(PromptBasedUserConsentHandlerTest, PromptsUser) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  const url::Origin& origin =
+      web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+  base::RunLoop loop;
+
+  ExpectCreateSmsPrompt(main_rfh(), origin, "12345");
+  CompletionCallback callback;
+  PromptBasedUserConsentHandler consent_handler;
+  consent_handler.RequestUserConsent(main_rfh(), origin, "12345",
+                                     std::move(callback));
+}
+
+TEST_F(PromptBasedUserConsentHandlerTest, ConfirmInvokedCallback) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  const url::Origin& origin =
+      web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+
+  ExpectCreateSmsPrompt(main_rfh(), origin, "12345");
+  PromptBasedUserConsentHandler consent_handler;
+  EXPECT_FALSE(consent_handler.is_active());
+  bool succeed;
+  auto callback = base::BindLambdaForTesting(
+      [&](SmsStatus status) { succeed = (status == SmsStatus::kSuccess); });
+  consent_handler.RequestUserConsent(main_rfh(), origin, "12345",
+                                     std::move(callback));
+  EXPECT_TRUE(consent_handler.is_active());
+  ConfirmPrompt();
+  EXPECT_FALSE(consent_handler.is_active());
+  EXPECT_TRUE(succeed);
+}
+
+TEST_F(PromptBasedUserConsentHandlerTest, CancelingInvokedCallback) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  const url::Origin& origin =
+      web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+
+  ExpectCreateSmsPrompt(main_rfh(), origin, "12345");
+  PromptBasedUserConsentHandler consent_handler;
+  EXPECT_FALSE(consent_handler.is_active());
+  bool cancelled;
+  auto callback = base::BindLambdaForTesting(
+      [&](SmsStatus status) { cancelled = (status == SmsStatus::kCancelled); });
+  consent_handler.RequestUserConsent(main_rfh(), origin, "12345",
+                                     std::move(callback));
+  EXPECT_TRUE(consent_handler.is_active());
+  DismissPrompt();
+  EXPECT_FALSE(consent_handler.is_active());
+  EXPECT_TRUE(cancelled);
+}
+
+TEST_F(PromptBasedUserConsentHandlerTest, CancelsWhenNoDelegate) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  const url::Origin& origin =
+      web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+
+  WebContentsImpl* web_contents_impl =
+      reinterpret_cast<WebContentsImpl*>(web_contents());
+  web_contents_impl->SetDelegate(nullptr);
+
+  ExpectNoSmsPrompt();
+
+  PromptBasedUserConsentHandler consent_handler;
+  bool cancelled;
+  auto callback = base::BindLambdaForTesting(
+      [&](SmsStatus status) { cancelled = (status == SmsStatus::kCancelled); });
+  consent_handler.RequestUserConsent(main_rfh(), origin, "12345",
+                                     std::move(callback));
+  EXPECT_TRUE(cancelled);
+}
+
+}  // namespace
+}  // namespace content
\ No newline at end of file
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 25792b205..f11ebdc 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5041,7 +5041,6 @@
                                               const GURL& url,
                                               const base::string16& title,
                                               bool user_gesture) {
-  // TODO(nick): Should we consider |source| here or pass it to the delegate?
   // TODO(nick): Do we need to apply FilterURL to |url|?
   if (!delegate_)
     return;
@@ -5053,14 +5052,13 @@
     return;
   }
 
-  delegate_->RegisterProtocolHandler(this, protocol, url, user_gesture);
+  delegate_->RegisterProtocolHandler(source, protocol, url, user_gesture);
 }
 
 void WebContentsImpl::UnregisterProtocolHandler(RenderFrameHostImpl* source,
                                                 const std::string& protocol,
                                                 const GURL& url,
                                                 bool user_gesture) {
-  // TODO(nick): Should we consider |source| here or pass it to the delegate?
   // TODO(nick): Do we need to apply FilterURL to |url|?
   if (!delegate_)
     return;
@@ -5072,7 +5070,7 @@
     return;
   }
 
-  delegate_->UnregisterProtocolHandler(this, protocol, url, user_gesture);
+  delegate_->UnregisterProtocolHandler(source, protocol, url, user_gesture);
 }
 
 void WebContentsImpl::OnAppCacheAccessed(const GURL& manifest_url,
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 3c19196..3885b25 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2388,7 +2388,7 @@
   MOCK_METHOD2(HandleContextMenu,
                bool(RenderFrameHost*, const ContextMenuParams&));
   MOCK_METHOD4(RegisterProtocolHandler,
-               void(WebContents*, const std::string&, const GURL&, bool));
+               void(RenderFrameHost*, const std::string&, const GURL&, bool));
 };
 
 }  // namespace
@@ -2419,11 +2419,11 @@
 
   // Only the first call to RegisterProtocolHandler should register because the
   // other call has a handler from a different origin.
-  EXPECT_CALL(delegate,
-              RegisterProtocolHandler(contents(), "mailto", handler_url1, true))
+  EXPECT_CALL(delegate, RegisterProtocolHandler(main_test_rfh(), "mailto",
+                                                handler_url1, true))
       .Times(1);
-  EXPECT_CALL(delegate,
-              RegisterProtocolHandler(contents(), "mailto", handler_url2, true))
+  EXPECT_CALL(delegate, RegisterProtocolHandler(main_test_rfh(), "mailto",
+                                                handler_url2, true))
       .Times(0);
 
   {
@@ -2451,8 +2451,8 @@
   contents()->NavigateAndCommit(data);
 
   // Data URLs should fail.
-  EXPECT_CALL(delegate,
-              RegisterProtocolHandler(contents(), "mailto", data_handler, true))
+  EXPECT_CALL(delegate, RegisterProtocolHandler(contents()->GetMainFrame(),
+                                                "mailto", data_handler, true))
       .Times(0);
 
   {
diff --git a/content/browser/webui/url_data_manager_backend.cc b/content/browser/webui/url_data_manager_backend.cc
index 796ef06f..49885ce 100644
--- a/content/browser/webui/url_data_manager_backend.cc
+++ b/content/browser/webui/url_data_manager_backend.cc
@@ -163,6 +163,7 @@
 
     const network::mojom::CSPDirectiveName kAllDirectives[] = {
         network::mojom::CSPDirectiveName::ChildSrc,
+        network::mojom::CSPDirectiveName::ConnectSrc,
         network::mojom::CSPDirectiveName::DefaultSrc,
         network::mojom::CSPDirectiveName::FrameSrc,
         network::mojom::CSPDirectiveName::ImgSrc,
@@ -170,8 +171,7 @@
         network::mojom::CSPDirectiveName::ObjectSrc,
         network::mojom::CSPDirectiveName::ScriptSrc,
         network::mojom::CSPDirectiveName::StyleSrc,
-        network::mojom::CSPDirectiveName::WorkerSrc,
-        network::mojom::CSPDirectiveName::ConnectSrc};
+        network::mojom::CSPDirectiveName::WorkerSrc};
 
     for (auto& directive : kAllDirectives) {
       csp_header.append(source->GetContentSecurityPolicy(directive));
diff --git a/content/browser/webui/web_ui_data_source_unittest.cc b/content/browser/webui/web_ui_data_source_unittest.cc
index e20ea8ba..7ef131ba 100644
--- a/content/browser/webui/web_ui_data_source_unittest.cc
+++ b/content/browser/webui/web_ui_data_source_unittest.cc
@@ -278,6 +278,8 @@
             url_data_source->GetContentSecurityPolicy(
                 network::mojom::CSPDirectiveName::ChildSrc));
   EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
+                    network::mojom::CSPDirectiveName::ConnectSrc));
+  EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
                     network::mojom::CSPDirectiveName::DefaultSrc));
   EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
                     network::mojom::CSPDirectiveName::FrameSrc));
@@ -293,8 +295,6 @@
                 network::mojom::CSPDirectiveName::ScriptSrc));
   EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
                     network::mojom::CSPDirectiveName::StyleSrc));
-  EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
-                    network::mojom::CSPDirectiveName::ConnectSrc));
 
   // Override each directive and test it updates the underlying URLDataSource.
   source()->OverrideContentSecurityPolicy(
@@ -304,6 +304,13 @@
                 network::mojom::CSPDirectiveName::ChildSrc));
 
   source()->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::ConnectSrc,
+      "connect-src 'self' 'unsafe-inline';");
+  EXPECT_EQ("connect-src 'self' 'unsafe-inline';",
+            url_data_source->GetContentSecurityPolicy(
+                network::mojom::CSPDirectiveName::ConnectSrc));
+
+  source()->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::DefaultSrc, "default-src 'self';");
   EXPECT_EQ("default-src 'self';",
             url_data_source->GetContentSecurityPolicy(
@@ -346,13 +353,6 @@
   EXPECT_EQ("style-src 'self' 'unsafe-inline';",
             url_data_source->GetContentSecurityPolicy(
                 network::mojom::CSPDirectiveName::StyleSrc));
-
-  source()->OverrideContentSecurityPolicy(
-      network::mojom::CSPDirectiveName::ConnectSrc,
-      "connect-src 'self' 'unsafe-inline';");
-  EXPECT_EQ("connect-src 'self' 'unsafe-inline';",
-            url_data_source->GetContentSecurityPolicy(
-                network::mojom::CSPDirectiveName::ConnectSrc));
 }
 
 }  // namespace content
diff --git a/content/common/common_param_traits_macros.h b/content/common/common_param_traits_macros.h
index e9c42b31..32faa88 100644
--- a/content/common/common_param_traits_macros.h
+++ b/content/common/common_param_traits_macros.h
@@ -31,15 +31,6 @@
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::DisplayMode,
                           blink::mojom::DisplayMode::kMaxValue)
 
-IPC_STRUCT_TRAITS_BEGIN(cc::BrowserControlsParams)
-  IPC_STRUCT_TRAITS_MEMBER(top_controls_height)
-  IPC_STRUCT_TRAITS_MEMBER(top_controls_min_height)
-  IPC_STRUCT_TRAITS_MEMBER(bottom_controls_height)
-  IPC_STRUCT_TRAITS_MEMBER(bottom_controls_min_height)
-  IPC_STRUCT_TRAITS_MEMBER(animate_browser_controls_height_changes)
-  IPC_STRUCT_TRAITS_MEMBER(browser_controls_shrink_blink_size)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(blink::VisualProperties)
   IPC_STRUCT_TRAITS_MEMBER(screen_info)
   IPC_STRUCT_TRAITS_MEMBER(auto_resize_enabled)
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index 211bc02..eff5643 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -43,6 +43,7 @@
 import "third_party/blink/public/mojom/loader/referrer.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_container.mojom";
+import "third_party/blink/public/mojom/widget/visual_properties.mojom";
 import "third_party/blink/public/mojom/window_features/window_features.mojom";
 import "ui/accessibility/mojom/ax_tree_update.mojom";
 import "ui/base/mojom/window_open_disposition.mojom";
@@ -310,7 +311,7 @@
   int32 main_frame_widget_route_id;
 
   // Initial properties for the main frame RenderWidget.
-  VisualProperties visual_properties;
+  blink.mojom.VisualProperties visual_properties;
 
   // The communication interfaces for the WebFrameWidget in blink.
   pending_associated_remote<blink.mojom.FrameWidgetHost> frame_widget_host;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index e5632a9..dad296f 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -425,10 +425,6 @@
                     content::CustomContextMenuContext /* custom_context */,
                     unsigned /* action */)
 
-// Requests that the RenderFrame send back a response after waiting for the
-// commit, activation and frame swap of the current DOM tree in blink.
-IPC_MESSAGE_ROUTED1(FrameMsg_VisualStateRequest, uint64_t /* id */)
-
 #if BUILDFLAG(ENABLE_PLUGINS)
 // Notifies the renderer of updates to the Plugin Power Saver origin allowlist.
 IPC_MESSAGE_ROUTED1(FrameMsg_UpdatePluginContentOriginAllowlist,
@@ -640,10 +636,6 @@
                     uint32_t /* the offset of the text in the document */,
                     gfx::Range /* selection range in the document */)
 
-// Sent as a response to FrameMsg_VisualStateRequest.
-// The message is delivered using RenderWidget::QueueMessage.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_VisualStateResponse, uint64_t /* id */)
-
 // Adding a new message? Stick to the sort order above: first platform
 // independent FrameMsg, then ifdefs for platform specific FrameMsg, then
 // platform independent FrameHostMsg, then ifdefs for platform specific
diff --git a/content/common/native_types.mojom b/content/common/native_types.mojom
index f26cfe9..f67eac0 100644
--- a/content/common/native_types.mojom
+++ b/content/common/native_types.mojom
@@ -14,9 +14,6 @@
 [Native]
 struct FrameReplicationState;
 
-[Native]
-struct VisualProperties;
-
 // NOTE: This type is only mapped and usable on Mac.
 [Native]
 enum ScrollbarButtonsPlacement;
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index 39a399c..02c9492 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -17,6 +17,7 @@
 import "third_party/blink/public/mojom/page/page.mojom";
 import "third_party/blink/public/mojom/page/widget.mojom";
 import "third_party/blink/public/mojom/renderer_preferences.mojom";
+import "third_party/blink/public/mojom/widget/visual_properties.mojom";
 import "third_party/blink/public/mojom/user_agent/user_agent_metadata.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
@@ -101,7 +102,7 @@
   // Initial state for the main frame RenderWidget.
   // TODO(danakj): This should be optional and not included when there is no
   // main_frame_widget_routing_id.
-  VisualProperties visual_properties;
+  blink.mojom.VisualProperties visual_properties;
 
   // Whether lookup of frames in the created RenderView (e.g. lookup via
   // window.open or via <a target=...>) should be renderer-wide (i.e. going
@@ -127,7 +128,7 @@
   pending_associated_receiver<blink.mojom.Widget> widget;
 
   // The initial visual properties of the widget.
-  VisualProperties visual_properties;
+  blink.mojom.VisualProperties visual_properties;
 };
 
 struct CreateFrameParams {
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h
index 80b86994..b877905 100644
--- a/content/common/widget_messages.h
+++ b/content/common/widget_messages.h
@@ -112,12 +112,6 @@
 IPC_MESSAGE_ROUTED1(WidgetMsg_SetViewportIntersection,
                     blink::ViewportIntersectionState /* intersection_state */)
 
-
-// Sent by the browser to synchronize with the next compositor frame by
-// requesting an ACK be queued. Used only for tests.
-IPC_MESSAGE_ROUTED1(WidgetMsg_WaitForNextFrameForTests,
-                    int /* main_frame_thread_observer_routing_id */)
-
 //
 // Renderer -> Browser Messages.
 //
@@ -150,7 +144,4 @@
 // Close message.
 IPC_MESSAGE_CONTROL1(WidgetHostMsg_Close_ACK, int /* old_route_id */)
 
-// Sent in reply to WidgetMsg_WaitForNextFrameForTests.
-IPC_MESSAGE_ROUTED0(WidgetHostMsg_WaitForNextFrameForTests_ACK)
-
 #endif  //  CONTENT_COMMON_WIDGET_MESSAGES_H_
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h
index 37a3a2fe..5b68eae 100644
--- a/content/public/browser/push_messaging_service.h
+++ b/content/public/browser/push_messaging_service.h
@@ -10,6 +10,8 @@
 #include <vector>
 
 #include "base/callback_forward.h"
+#include "base/optional.h"
+#include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom.h"
 #include "url/gurl.h"
@@ -34,6 +36,7 @@
   using RegisterCallback =
       base::OnceCallback<void(const std::string& registration_id,
                               const GURL& endpoint,
+                              const base::Optional<base::Time>& expiration_time,
                               const std::vector<uint8_t>& p256dh,
                               const std::vector<uint8_t>& auth,
                               blink::mojom::PushRegistrationStatus status)>;
@@ -42,6 +45,7 @@
   using SubscriptionInfoCallback =
       base::OnceCallback<void(bool is_valid,
                               const GURL& endpoint,
+                              const base::Optional<base::Time>& expiration_time,
                               const std::vector<uint8_t>& p256dh,
                               const std::vector<uint8_t>& auth)>;
   using StringCallback = base::OnceCallback<
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index a6ee392..2f51d90f 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -304,7 +304,7 @@
   // changes to the contents resulting from operations executed prior to this
   // call are visible on screen. The call completes asynchronously by running
   // the supplied |callback| with a value of true upon successful completion and
-  // false otherwise (when the frame is destroyed, detached, etc..).
+  // false otherwise when the widget is destroyed.
   using VisualStateCallback = base::OnceCallback<void(bool)>;
   virtual void InsertVisualStateCallback(VisualStateCallback callback) = 0;
 
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 6e40ca1..cca5834 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -476,7 +476,7 @@
   // Register a new handler for URL requests with the given scheme.
   // |user_gesture| is true if the registration is made in the context of a user
   // gesture.
-  virtual void RegisterProtocolHandler(WebContents* web_contents,
+  virtual void RegisterProtocolHandler(RenderFrameHost* requesting_frame,
                                        const std::string& protocol,
                                        const GURL& url,
                                        bool user_gesture) {}
@@ -484,7 +484,7 @@
   // Unregister the registered handler for URL requests with the given scheme.
   // |user_gesture| is true if the registration is made in the context of a user
   // gesture.
-  virtual void UnregisterProtocolHandler(WebContents* web_contents,
+  virtual void UnregisterProtocolHandler(RenderFrameHost* requesting_frame,
                                          const std::string& protocol,
                                          const GURL& url,
                                          bool user_gesture) {}
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestTouchUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestTouchUtils.java
index 485b818..23fbf56 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestTouchUtils.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestTouchUtils.java
@@ -203,7 +203,7 @@
      * @param v The view to call performClick on.
      */
     public static void performClickOnMainSync(Instrumentation instrumentation, final View v) {
-        instrumentation.runOnMainSync(() -> v.performClick());
+        TestThreadUtils.runOnUiThreadBlocking(() -> { v.performClick(); });
     }
 
     /**
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 4ee793f3..561901eb 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2651,40 +2651,25 @@
 MainThreadFrameObserver::MainThreadFrameObserver(
     RenderWidgetHost* render_widget_host)
     : render_widget_host_(render_widget_host),
-      routing_id_(render_widget_host_->GetProcess()->GetNextRoutingID()) {
-  // TODO(lfg): We should look into adding a way to observe RenderWidgetHost
-  // messages similarly to what WebContentsObserver can do with RFH and RVW.
-  render_widget_host_->GetProcess()->AddRoute(routing_id_, this);
-}
+      routing_id_(render_widget_host_->GetProcess()->GetNextRoutingID()) {}
 
-MainThreadFrameObserver::~MainThreadFrameObserver() {
-  render_widget_host_->GetProcess()->RemoveRoute(routing_id_);
-}
+MainThreadFrameObserver::~MainThreadFrameObserver() = default;
 
 void MainThreadFrameObserver::Wait() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  render_widget_host_->Send(new WidgetMsg_WaitForNextFrameForTests(
-      render_widget_host_->GetRoutingID(), routing_id_));
+  static_cast<RenderWidgetHostImpl*>(render_widget_host_)
+      ->InsertVisualStateCallback(base::BindOnce(&MainThreadFrameObserver::Quit,
+                                                 base::Unretained(this)));
   base::RunLoop run_loop;
   quit_closure_ = run_loop.QuitClosure();
   run_loop.Run();
 }
 
-void MainThreadFrameObserver::Quit() {
+void MainThreadFrameObserver::Quit(bool) {
   if (quit_closure_)
     std::move(quit_closure_).Run();
 }
 
-bool MainThreadFrameObserver::OnMessageReceived(const IPC::Message& msg) {
-  if (msg.type() == WidgetHostMsg_WaitForNextFrameForTests_ACK::ID &&
-      msg.routing_id() == routing_id_) {
-    GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadFrameObserver::Quit, base::Unretained(this)));
-  }
-  return true;
-}
-
 InputMsgWatcher::InputMsgWatcher(RenderWidgetHost* render_widget_host,
                                  blink::WebInputEvent::Type type)
     : render_widget_host_(render_widget_host),
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 83514d8..f0ed1a8 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1307,20 +1307,17 @@
 // So while the ACK can arrive before a CompositorFrame submission occurs. The
 // processing does not occur until after the FrameToken for that frame
 // submission arrives to the main thread.
-class MainThreadFrameObserver : public IPC::Listener {
+class MainThreadFrameObserver {
  public:
   explicit MainThreadFrameObserver(RenderWidgetHost* render_widget_host);
-  ~MainThreadFrameObserver() override;
+  ~MainThreadFrameObserver();
 
   // Synchronizes the browser main thread with the renderer main thread and impl
   // thread.
   void Wait();
 
-  // Overridden IPC::Listener methods.
-  bool OnMessageReceived(const IPC::Message& msg) override;
-
  private:
-  void Quit();
+  void Quit(bool);
 
   RenderWidgetHost* render_widget_host_;
   base::OnceClosure quit_closure_;
diff --git a/content/public/test/fake_frame_widget.h b/content/public/test/fake_frame_widget.h
index 40c4de8..7025daf5 100644
--- a/content/public/test/fake_frame_widget.h
+++ b/content/public/test/fake_frame_widget.h
@@ -60,6 +60,9 @@
 #endif
   void ShowContextMenu(ui::MenuSourceType source_type,
                        const gfx::Point& location) override {}
+  void BindWidgetCompositor(
+      mojo::PendingReceiver<blink::mojom::WidgetCompositor> receiver) override {
+  }
 
   mojo::AssociatedReceiver<blink::mojom::FrameWidget> receiver_;
   base::i18n::TextDirection text_direction_ =
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index e34ea57..8c62fd8 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -1235,8 +1235,8 @@
             "These requests cannot be disabled in settings, but they are "
             "sent only if user installs extensions."
           chrome_policy {
-            ExtensionInstallBlacklist {
-              ExtensionInstallBlacklist: {
+            ExtensionInstallBlocklist {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index c10a6ff..25d1da2 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2227,8 +2227,6 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
     IPC_MESSAGE_HANDLER(FrameMsg_SetPepperVolume, OnSetPepperVolume)
 #endif
-    IPC_MESSAGE_HANDLER(FrameMsg_VisualStateRequest,
-                        OnVisualStateRequest)
     IPC_MESSAGE_HANDLER(FrameMsg_MixedContentFound, OnMixedContentFound)
     IPC_MESSAGE_HANDLER(UnfreezableFrameMsg_Delete, OnDeleteFrame)
   IPC_END_MESSAGE_MAP()
@@ -2621,11 +2619,6 @@
   return base::Value();
 }
 
-void RenderFrameImpl::OnVisualStateRequest(uint64_t id) {
-  GetLocalRootRenderWidget()->QueueMessage(
-      std::make_unique<FrameHostMsg_VisualStateResponse>(routing_id_, id));
-}
-
 void RenderFrameImpl::OnPortalActivated(
     const base::UnguessableToken& portal_token,
     mojo::PendingAssociatedRemote<blink::mojom::Portal> portal,
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index c5b50c3..d951a9f 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1009,7 +1009,6 @@
   void OnMoveCaret(const gfx::Point& point);
   void OnScrollFocusedEditableNodeIntoRect(const gfx::Rect& rect);
   void OnSelectRange(const gfx::Point& base, const gfx::Point& extent);
-  void OnVisualStateRequest(uint64_t key);
   void OnSuppressFurtherDialogs();
   void OnMixedContentFound(const FrameMsg_MixedContentFound_Params& params);
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index f16af9a..31c1d05 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -448,8 +448,6 @@
     IPC_MESSAGE_HANDLER(WidgetMsg_UpdateScreenRects, OnUpdateScreenRects)
     IPC_MESSAGE_HANDLER(WidgetMsg_SetViewportIntersection,
                         OnSetViewportIntersection)
-    IPC_MESSAGE_HANDLER(WidgetMsg_WaitForNextFrameForTests,
-                        OnWaitNextFrameForTests)
     IPC_MESSAGE_HANDLER(DragMsg_TargetDragEnter, OnDragTargetDragEnter)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
@@ -1788,13 +1786,6 @@
   render_frames_.RemoveObserver(frame);
 }
 
-void RenderWidget::OnWaitNextFrameForTests(
-    int main_frame_thread_observer_routing_id) {
-  // Sends an ACK to the browser process during the next compositor frame.
-  QueueMessage(std::make_unique<WidgetHostMsg_WaitForNextFrameForTests_ACK>(
-      main_frame_thread_observer_routing_id));
-}
-
 const blink::ScreenInfo& RenderWidget::GetOriginalScreenInfo() const {
   if (device_emulator_)
     return device_emulator_->original_screen_info();
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 8ed53f5..b9a5d33 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -490,7 +490,6 @@
                          const gfx::PointF& screen_point,
                          blink::WebDragOperation drag_operation);
   void OnOrientationChange();
-  void OnWaitNextFrameForTests(int routing_id);
 
   // Sets the "hidden" state of this widget.  All modification of is_hidden_
   // should use this method so that we can properly inform the RenderThread of
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
index 9c92bd2..f89dd3a 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -127,8 +127,7 @@
             std::move(controller_info), subresource_loader_factory_bundle_);
   }
 
-  impl_ = blink::WebSharedWorker::Create(this);
-  impl_->StartWorkerContext(
+  impl_ = blink::WebSharedWorker::CreateAndStart(
       url_, info->options->type, info->options->credentials,
       blink::WebString::FromUTF8(info->options->name),
       blink::WebSecurityOrigin(constructor_origin),
@@ -139,7 +138,7 @@
           info->outside_fetch_client_settings_object),
       appcache_host_id, devtools_worker_token, std::move(content_settings),
       std::move(browser_interface_broker), pause_on_start,
-      std::move(worker_main_script_load_params));
+      std::move(worker_main_script_load_params), this);
 
   // If the host drops its connection, then self-destruct.
   receiver_.set_disconnect_handler(base::BindOnce(
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.cc b/content/shell/browser/web_test/web_test_push_messaging_service.cc
index e7e8192d..401fb327 100644
--- a/content/shell/browser/web_test/web_test_push_messaging_service.cc
+++ b/content/shell/browser/web_test/web_test_push_messaging_service.cc
@@ -6,7 +6,9 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/optional.h"
 #include "base/stl_util.h"
+#include "base/time/time.h"
 #include "content/public/browser/permission_type.h"
 #include "content/shell/browser/web_test/web_test_browser_context.h"
 #include "content/shell/browser/web_test/web_test_content_browser_client.h"
@@ -38,6 +40,10 @@
 static_assert(sizeof(kAuthentication) == 12,
               "The fake authentication key must be at least 12 bytes in size.");
 
+base::Time GetFutureTime() {
+  return base::Time::Now() + base::TimeDelta::FromDays(100);
+}
+
 }  // anonymous namespace
 
 WebTestPushMessagingService::WebTestPushMessagingService()
@@ -79,17 +85,17 @@
                                 kTestP256Key + base::size(kTestP256Key));
     std::vector<uint8_t> auth(kAuthentication,
                               kAuthentication + base::size(kAuthentication));
-
     const std::string subscription_id = "layoutTestRegistrationId";
     const GURL endpoint = CreateEndpoint(subscription_id);
 
     subscribed_service_worker_registration_ = service_worker_registration_id;
     std::move(callback).Run(
-        subscription_id, endpoint, p256dh, auth,
+        subscription_id, endpoint, GetFutureTime(), p256dh, auth,
         blink::mojom::PushRegistrationStatus::SUCCESS_FROM_PUSH_SERVICE);
   } else {
     std::move(callback).Run(
         "registration_id", GURL::EmptyGURL() /* endpoint */,
+        base::nullopt /* expiration_time */,
         std::vector<uint8_t>() /* p256dh */, std::vector<uint8_t>() /* auth */,
         blink::mojom::PushRegistrationStatus::PERMISSION_DENIED);
   }
@@ -105,9 +111,9 @@
                               kTestP256Key + base::size(kTestP256Key));
   std::vector<uint8_t> auth(kAuthentication,
                             kAuthentication + base::size(kAuthentication));
-
   const GURL endpoint = CreateEndpoint(subscription_id);
-  std::move(callback).Run(true /* is_valid */, endpoint, p256dh, auth);
+  std::move(callback).Run(true /* is_valid */, endpoint, GetFutureTime(),
+                          p256dh, auth);
 }
 
 bool WebTestPushMessagingService::SupportNonVisibleMessages() {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 60ade4b..e14d134 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -83,6 +83,8 @@
     "../browser/sms/test/mock_sms_provider.h",
     "../browser/sms/test/mock_sms_web_contents_delegate.cc",
     "../browser/sms/test/mock_sms_web_contents_delegate.h",
+    "../browser/sms/test/mock_user_consent_handler.cc",
+    "../browser/sms/test/mock_user_consent_handler.h",
     "../browser/web_package/mock_signed_exchange_handler.cc",
     "../browser/web_package/mock_signed_exchange_handler.h",
     "../browser/web_package/mock_web_bundle_reader_factory.cc",
@@ -1921,6 +1923,7 @@
     "../browser/sms/sms_fetcher_impl_unittest.cc",
     "../browser/sms/sms_parser_unittest.cc",
     "../browser/sms/sms_service_unittest.cc",
+    "../browser/sms/user_consent_handler_unittest.cc",
     "../browser/speech/tts_controller_unittest.cc",
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_impl_map_unittest.cc",
diff --git a/extensions/browser/api/socket/socket.cc b/extensions/browser/api/socket/socket.cc
index 5d365a7..7e63ec2 100644
--- a/extensions/browser/api/socket/socket.cc
+++ b/extensions/browser/api/socket/socket.cc
@@ -167,8 +167,8 @@
             "uses this API. Other than that, this request will not be sent if "
             "the user does not install a Chrome App that uses the Socket API."
           chrome_policy {
-            ExtensionInstallBlacklist {
-              ExtensionInstallBlacklist: {
+            ExtensionInstallBlocklist {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
diff --git a/extensions/browser/updater/extension_downloader.cc b/extensions/browser/updater/extension_downloader.cc
index 554f06f..00a81f1 100644
--- a/extensions/browser/updater/extension_downloader.cc
+++ b/extensions/browser/updater/extension_downloader.cc
@@ -586,9 +586,9 @@
             "This feature cannot be disabled. It is only enabled when the user "
             "has installed extensions."
           chrome_policy {
-            ExtensionInstallBlacklist {
+            ExtensionInstallBlocklist {
               policy_options {mode: MANDATORY}
-              ExtensionInstallBlacklist: {
+              ExtensionInstallBlocklist: {
                 entries: '*'
               }
             }
diff --git a/gpu/ipc/in_process_gpu_thread_holder.cc b/gpu/ipc/in_process_gpu_thread_holder.cc
index 697b35d..4629f80 100644
--- a/gpu/ipc/in_process_gpu_thread_holder.cc
+++ b/gpu/ipc/in_process_gpu_thread_holder.cc
@@ -99,6 +99,11 @@
 #endif
   use_virtualized_gl_context |=
       gpu_driver_bug_workarounds.use_virtualized_gl_contexts;
+  if (use_passthrough_cmd_decoder) {
+    // Virtualized contexts don't work with passthrough command decoder.
+    // See https://crbug.com/914976
+    use_virtualized_gl_context = false;
+  }
   if (use_virtualized_gl_context)
     share_group_->SetSharedContext(context_.get());
 
diff --git a/gpu/vulkan/vulkan_swap_chain.cc b/gpu/vulkan/vulkan_swap_chain.cc
index c2c49cb6..b939271 100644
--- a/gpu/vulkan/vulkan_swap_chain.cc
+++ b/gpu/vulkan/vulkan_swap_chain.cc
@@ -353,7 +353,7 @@
 
   VkPresentInfoKHR present_info = {
       .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
-      .pNext = &present_regions,
+      .pNext = is_incremental_present_supported_ ? &present_regions : nullptr,
       .waitSemaphoreCount = 1,
       .pWaitSemaphores = &end_write_semaphore_,
       .swapchainCount = 1,
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index c4529752..2537aa0 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -617,7 +617,10 @@
     "test/headless_browser_test.cc",
     "test/headless_browser_test.h",
     "test/headless_client_browsertest.cc",
-    "test/headless_protocol_browsertest.cc",
+
+    # TODO(https://crbug.com/1107396): These tests flakily timeout.
+    #"test/headless_protocol_browsertest.cc",
+
     "test/headless_test_launcher.cc",
     "test/test_network_interceptor.cc",
     "test/test_network_interceptor.h",
diff --git a/ios/chrome/browser/ui/download/download_manager_egtest.mm b/ios/chrome/browser/ui/download/download_manager_egtest.mm
index b9886c43..868f523 100644
--- a/ios/chrome/browser/ui/download/download_manager_egtest.mm
+++ b/ios/chrome/browser/ui/download/download_manager_egtest.mm
@@ -203,7 +203,8 @@
 }
 
 // Tests "Open in New Tab" on download link.
-- (void)testDownloadInNewTab {
+// TODO(crbug.com/1107378): reenable this test.
+- (void)DISABLED_testDownloadInNewTab {
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/")];
   [ChromeEarlGrey waitForWebStateContainingText:"Download"];
 
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
index e7c771f7..496a9e24 100644
--- a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
+++ b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
@@ -103,15 +103,7 @@
 
 // Tests that find in page allows iteration between search results and displays
 // correct number of results.
-// TODO(crbug.com/1030701): Test fails on iOS 13 when rolling EG2 version.
-- (void)DISABLED_testFindInPage {
-// TODO(crbug.com/1030701): Test fails on iOS 12 when run as EG2 test.
-#if defined(CHROME_EARL_GREY_2)
-  if (!base::ios::IsRunningOnOrLater(13, 0, 0)) {
-    EARL_GREY_TEST_DISABLED(@"Fails on iOS 12.0.");
-  }
-#endif
-
+- (void)testFindInPage {
   // Type "find".
   [self typeFindInPageText:@"find"];
   // Should be highlighting result 1 of 2.
@@ -128,8 +120,7 @@
 // Tests that Find In Page search term retention is working as expected, e.g.
 // the search term is persisted between FIP runs, but in incognito search term
 // is not retained and not autofilled.
-// TODO(crbug.com/1029709): Test failed.
-- (void)DISABLED_testFindInPageRetainsSearchTerm {
+- (void)testFindInPageRetainsSearchTerm {
   // Type "find".
   [self typeFindInPageText:@"find"];
   [self assertResultStringIsResult:1 outOfTotal:2];
@@ -169,20 +160,10 @@
 }
 
 // Tests accessibility of the Find in Page screen.
-// TODO(crbug.com/1030701): Test fails on iOS 13 when rolling EG2 version.
-- (void)DISABLED_testAccessibilityOnFindInPage {
-// TODO(crbug.com/1030701): Test fails on iOS 12 when run as EG2 test.
-#if defined(CHROME_EARL_GREY_2)
-  if (!base::ios::IsRunningOnOrLater(13, 0, 0)) {
-    EARL_GREY_TEST_DISABLED(@"Fails on iOS 12.0.");
-  }
-#endif
-
+- (void)testAccessibilityOnFindInPage {
   [self typeFindInPageText:@"find"];
+  [self assertResultStringIsResult:1 outOfTotal:2];
 
-  // Wait for UI to finish loading screen, before programatically verifying
-  // accessibility.
-  [ChromeEarlGreyUI waitForAppToIdle];
   [ChromeEarlGrey verifyAccessibilityForCurrentScreen];
 }
 
@@ -194,7 +175,7 @@
       selectElementWithMatcher:grey_allOf(
                                    grey_accessibilityID(kToolsMenuFindInPageId),
                                    grey_sufficientlyVisible(), nil)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 250)
       onElementWithMatcher:grey_accessibilityID(kPopupMenuToolsMenuTableViewId)]
       performAction:grey_tap()];
 }
@@ -206,21 +187,8 @@
 }
 
 - (void)typeFindInPageText:(NSString*)text {
-#if defined(CHROME_EARL_GREY_1)
   [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
-      performAction:grey_replaceText(text)];
-#elif defined(CHROME_EARL_GREY_2)
-  // There are two elements in the DOM (UITextField and
-  // UIAccessibilityTextFieldElement) that match the acessibilityID of
-  // kFindInPageInputFieldId. Choose the accessibility element.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
-                                              kFindInPageInputFieldId),
-                                          grey_accessibilityElement(), nil)]
-      performAction:grey_replaceText(text)];
-#else
-#error Must define either CHROME_EARL_GREY_1 or CHROME_EARL_GREY_2.
-#endif
+      performAction:grey_typeText(text)];
 }
 
 - (id<GREYMatcher>)findInPageInputField {
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 144ba5a..38d520b 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -347,14 +347,6 @@
   ]
 }
 
-source_set("constants") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "lookalike_url_constants.h",
-    "lookalike_url_constants.mm",
-  ]
-}
-
 source_set("eg_test_support+eg2") {
   defines = [ "CHROME_EARL_GREY_2" ]
   configs += [
@@ -363,10 +355,7 @@
   ]
   testonly = true
 
-  sources = [
-    "lookalike_url_app_interface.h",
-    "progress_indicator_app_interface.h",
-  ]
+  sources = [ "progress_indicator_app_interface.h" ]
 
   deps = [
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
@@ -383,23 +372,15 @@
   defines = [ "CHROME_EARL_GREY_2" ]
 
   sources = [
-    "lookalike_url_app_interface.h",
-    "lookalike_url_app_interface.mm",
     "progress_indicator_app_interface.h",
     "progress_indicator_app_interface.mm",
   ]
 
   deps = [
-    ":constants",
     "//base",
-    "//components/lookalikes/core",
-    "//ios/chrome/test/app:test_support",
-    "//ios/components/security_interstitials/lookalikes",
     "//ios/testing/earl_grey:eg_app_support+eg2",
     "//ios/third_party/earl_grey2:app_framework+link",
     "//ios/third_party/material_components_ios",
-    "//ios/web/public",
-    "//net",
   ]
 }
 
@@ -421,7 +402,6 @@
     "forms_egtest.mm",
     "http_auth_egtest.mm",
     "js_print_egtest.mm",
-    "lookalike_url_egtest.mm",
     "navigation_egtest.mm",
     "progress_indicator_egtest.mm",
     "push_and_replace_state_navigation_egtest.mm",
@@ -434,7 +414,6 @@
   ]
 
   deps = [
-    ":constants",
     ":eg_test_support+eg2",
     "//components/content_settings/core/common",
     "//components/strings",
diff --git a/ios/chrome/browser/web/lookalike_url_app_interface.h b/ios/chrome/browser/web/lookalike_url_app_interface.h
deleted file mode 100644
index a4f864c..0000000
--- a/ios/chrome/browser/web/lookalike_url_app_interface.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 IOS_CHROME_BROWSER_WEB_LOOKALIKE_URL_APP_INTERFACE_H_
-#define IOS_CHROME_BROWSER_WEB_LOOKALIKE_URL_APP_INTERFACE_H_
-
-#import <Foundation/Foundation.h>
-
-// The app interface for lookalike URL blocking page tests. It sets
-// up LookalikeUrlDecider, which does the following:
-//   - Lets a navigation proceed if the domain is explicitly allowed
-//   - Cancels the navigation and shows error page with a suggested URL
-//      for the /lookalike.html path
-//   - Cancels the navigation and shows error page with no suggested URL
-//      for the /lookalike-empty.html path
-//   - Allows other navigations to proceed
-@interface LookalikeUrlAppInterface : NSObject
-
-// Sets up lookalike policy decider. Used for testing.
-+ (void)setUpLookalikeUrlDeciderForWebState;
-
-// Tear down lookalike policy decider. Used for testing.
-+ (void)tearDownLookalikeUrlDeciderForWebState;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_WEB_LOOKALIKE_URL_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/web/lookalike_url_app_interface.mm b/ios/chrome/browser/web/lookalike_url_app_interface.mm
deleted file mode 100644
index b2ec43e..0000000
--- a/ios/chrome/browser/web/lookalike_url_app_interface.mm
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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.
-
-#import "ios/chrome/browser/web/lookalike_url_app_interface.h"
-
-#include "components/lookalikes/core/lookalike_url_util.h"
-#import "ios/chrome/browser/web/lookalike_url_constants.h"
-#import "ios/chrome/test/app/chrome_test_util.h"
-#import "ios/chrome/test/app/tab_test_util.h"
-#include "ios/components/security_interstitials/lookalikes/lookalike_url_container.h"
-#include "ios/components/security_interstitials/lookalikes/lookalike_url_error.h"
-#include "ios/components/security_interstitials/lookalikes/lookalike_url_tab_allow_list.h"
-#import "ios/web/public/navigation/web_state_policy_decider.h"
-#import "ios/web/public/web_state_user_data.h"
-#import "net/base/mac/url_conversions.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-// This decider determines whether a URL is a lookalike. If so, it cancels
-// navigation and shows an error.
-class LookalikeUrlDecider : public web::WebStatePolicyDecider,
-                            public web::WebStateUserData<LookalikeUrlDecider> {
- public:
-  LookalikeUrlDecider(web::WebState* web_state)
-      : web::WebStatePolicyDecider(web_state), web_state_(web_state) {}
-
-  void ShouldAllowResponse(
-      NSURLResponse* response,
-      bool for_main_frame,
-      web::WebStatePolicyDecider::PolicyDecisionCallback callback) override {
-    LookalikeUrlContainer* lookalike_container =
-        LookalikeUrlContainer::FromWebState(web_state_);
-    LookalikeUrlTabAllowList* allow_list =
-        LookalikeUrlTabAllowList::FromWebState(web_state_);
-
-    GURL response_url = net::GURLWithNSURL(response.URL);
-    if (allow_list->IsDomainAllowed(response_url.host())) {
-      return std::move(callback).Run(
-          web::WebStatePolicyDecider::PolicyDecision::Allow());
-    }
-    if (response_url.path() == kLookalikePagePathForTesting) {
-      GURL::Replacements safeReplacements;
-      safeReplacements.SetPathStr("echo");
-      lookalike_container->SetLookalikeUrlInfo(
-          response_url.ReplaceComponents(safeReplacements), response_url,
-          LookalikeUrlMatchType::kSkeletonMatchTop5k);
-      std::move(callback).Run(CreateLookalikeErrorDecision());
-      return;
-    }
-    if (response_url.path() == kLookalikePageEmptyUrlPathForTesting) {
-      lookalike_container->SetLookalikeUrlInfo(
-          GURL::EmptyGURL(), response_url,
-          LookalikeUrlMatchType::kSkeletonMatchTop5k);
-      std::move(callback).Run(CreateLookalikeErrorDecision());
-      return;
-    }
-    return std::move(callback).Run(
-        web::WebStatePolicyDecider::PolicyDecision::Allow());
-  }
-
-  WEB_STATE_USER_DATA_KEY_DECL();
-
- private:
-  web::WebState* web_state_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(LookalikeUrlDecider);
-};
-
-WEB_STATE_USER_DATA_KEY_IMPL(LookalikeUrlDecider)
-
-}  // namespace
-
-@implementation LookalikeUrlAppInterface
-
-+ (void)setUpLookalikeUrlDeciderForWebState {
-  LookalikeUrlDecider::CreateForWebState(
-      chrome_test_util::GetCurrentWebState());
-}
-
-+ (void)tearDownLookalikeUrlDeciderForWebState {
-  LookalikeUrlDecider::RemoveFromWebState(
-      chrome_test_util::GetCurrentWebState());
-}
-
-@end
diff --git a/ios/chrome/browser/web/lookalike_url_constants.h b/ios/chrome/browser/web/lookalike_url_constants.h
deleted file mode 100644
index c8b88d4..0000000
--- a/ios/chrome/browser/web/lookalike_url_constants.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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 IOS_CHROME_BROWSER_WEB_LOOKALIKE_URL_CONSTANTS_H_
-#define IOS_CHROME_BROWSER_WEB_LOOKALIKE_URL_CONSTANTS_H_
-
-// Constant used for testing a lookalike URL interstitial.
-extern const char kLookalikePagePathForTesting[];
-
-// Constant used for testing a lookalike URL interstitial with no suggested URL.
-extern const char kLookalikePageEmptyUrlPathForTesting[];
-
-#endif  // IOS_CHROME_BROWSER_WEB_LOOKALIKE_URL_CONSTANTS_H_
diff --git a/ios/chrome/browser/web/lookalike_url_constants.mm b/ios/chrome/browser/web/lookalike_url_constants.mm
deleted file mode 100644
index eec1f20..0000000
--- a/ios/chrome/browser/web/lookalike_url_constants.mm
+++ /dev/null
@@ -1,12 +0,0 @@
-// 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.
-
-#import "ios/chrome/browser/web/lookalike_url_constants.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-const char kLookalikePagePathForTesting[] = "/lookalike.html";
-const char kLookalikePageEmptyUrlPathForTesting[] = "/lookalike-empty.html";
diff --git a/ios/chrome/browser/web/lookalike_url_egtest.mm b/ios/chrome/browser/web/lookalike_url_egtest.mm
deleted file mode 100644
index ef0147cc..0000000
--- a/ios/chrome/browser/web/lookalike_url_egtest.mm
+++ /dev/null
@@ -1,300 +0,0 @@
-// 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 <string>
-
-#include "base/bind.h"
-#include "base/strings/stringprintf.h"
-#include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/web/lookalike_url_app_interface.h"
-#import "ios/chrome/browser/web/lookalike_url_constants.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#import "ios/testing/earl_grey/earl_grey_test.h"
-#include "ios/testing/embedded_test_server_handlers.h"
-#include "ios/web/common/features.h"
-#include "ios/web/public/test/element_selector.h"
-#include "net/test/embedded_test_server/default_handlers.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "net/test/embedded_test_server/request_handler_util.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-#if defined(CHROME_EARL_GREY_2)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
-GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(LookalikeUrlAppInterface);
-#pragma clang diagnostic pop
-#endif  // defined(CHROME_EARL_GREY_2)
-
-using chrome_test_util::BackButton;
-using chrome_test_util::ForwardButton;
-
-namespace {
-// Relative paths used for a page that opens a lookalike in a new tab.
-const char kLookalikeInNewTab[] = "/lookalike-newtab.html";
-
-// Text that is found on the lookalike page.
-const char kLookalikeContent[] = "Lookalike - Safety warning bypassed";
-// Text that is found on a page that opens a lookalike in a new tab.
-const char kLookalikeInNewTabContent[] = "New tab";
-}  // namespace
-
-// Tests lookalike URL blocking.
-@interface LookalikeUrlTestCase : ChromeTestCase {
-  // A URL that is treated as a lookalike.
-  GURL _lookalikeURL;
-  // A URL that is treated as a safe page.
-  GURL _safeURL;
-  // Text that is found on the safe page.
-  std::string _safeContent;
-  // Text that is found on the lookalike interstitial.
-  std::string _lookalikeBlockingPageContent;
-}
-@end
-
-@implementation LookalikeUrlTestCase
-
-- (AppLaunchConfiguration)appConfigurationForTestCase {
-  AppLaunchConfiguration config;
-  config.features_enabled.push_back(
-      web::features::kIOSLookalikeUrlNavigationSuggestionsUI);
-  config.features_enabled.push_back(web::features::kSSLCommittedInterstitials);
-  config.relaunch_policy = NoForceRelaunchAndResetState;
-  return config;
-}
-
-- (void)setUp {
-  [super setUp];
-  [LookalikeUrlAppInterface setUpLookalikeUrlDeciderForWebState];
-  std::string lookalikeHTML =
-      base::StringPrintf("<html><body>%s</body></html>", kLookalikeContent);
-  self.testServer->RegisterRequestHandler(base::BindRepeating(
-      &net::test_server::HandlePrefixedRequest, kLookalikePagePathForTesting,
-      base::BindRepeating(&testing::HandlePageWithHtml, lookalikeHTML)));
-  self.testServer->RegisterRequestHandler(base::BindRepeating(
-      &net::test_server::HandlePrefixedRequest,
-      kLookalikePageEmptyUrlPathForTesting,
-      base::BindRepeating(&testing::HandlePageWithHtml, lookalikeHTML)));
-  std::string lookalikeNewTabHTML = base::StringPrintf(
-      "<html><body><a target=\"_blank\" href=\"%s\" "
-      "id=\"lookalike-newtab\">%s</a></body></html>",
-      kLookalikePageEmptyUrlPathForTesting, kLookalikeInNewTabContent);
-  self.testServer->RegisterRequestHandler(base::BindRepeating(
-      &net::test_server::HandlePrefixedRequest, kLookalikeInNewTab,
-      base::BindRepeating(&testing::HandlePageWithHtml, lookalikeNewTabHTML)));
-  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
-  _safeURL = self.testServer->GetURL("/echo");
-  _safeContent = "Echo";
-  _lookalikeURL = self.testServer->GetURL(kLookalikePagePathForTesting);
-  _lookalikeBlockingPageContent =
-      l10n_util::GetStringUTF8(IDS_LOOKALIKE_URL_PRIMARY_PARAGRAPH);
-}
-
-- (void)tearDown {
-  [LookalikeUrlAppInterface tearDownLookalikeUrlDeciderForWebState];
-  [super tearDown];
-}
-
-// Tests that non-lookalike URLs are not blocked.
-- (void)testSafePage {
-  [ChromeEarlGrey loadURL:_safeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-}
-
-// Tests that a lookalike URL navigation is blocked, and the Go to suggested
-// site button works. Also tests that navigating back to the site shows the
-// interstitial and that navigating forward again works.
-- (void)testLookalikeUrlPage {
-  // Load the lookalike page and verify a warning is shown.
-  [ChromeEarlGrey loadURL:_lookalikeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  // Tap on the "Go to" button and verify that the suggested page
-  // contents are loaded.
-  [ChromeEarlGrey tapWebStateElementWithID:@"primary-button"];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-
-  // Verify that the warning is shown when navigating back and that safe
-  // content is shown when navigating forward again.
-  [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-  [[EarlGrey selectElementWithMatcher:ForwardButton()]
-      performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-}
-
-// Tests that a lookalike URL navigation is blocked, and the text link for
-// suggested site works. Also tests that navigating back to the site shows
-// the interstitial and that navigating forward again works.
-- (void)testLookalikeUrlPageSiteLink {
-  // Load the lookalike page and verify a warning is shown.
-  [ChromeEarlGrey loadURL:_lookalikeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  // Tap on the site suggestion link and verify that the suggested page
-  // contents are loaded.
-  [ChromeEarlGrey tapWebStateElementWithID:@"dont-proceed-link"];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-
-  // Verify that the warning is shown when navigating back and that safe
-  // content is shown when navigating forward again.
-  [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-  [[EarlGrey selectElementWithMatcher:ForwardButton()]
-      performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-}
-
-// Tests that Back to safety works when there is no suggested URL. Also tests
-// that navigating forward to the site shows the interstitial and that
-// navigating back again works.
-- (void)testLookalikeUrlPageNoSuggestion {
-  // Navigate to safe page first to enable later verification of
-  // back/forward navigation.
-  [ChromeEarlGrey loadURL:_safeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-
-  // Navigate to a lookalike page with no suggestion and verify that a warning
-  // and the correct button is shown.
-  [ChromeEarlGrey
-      loadURL:self.testServer->GetURL(kLookalikePageEmptyUrlPathForTesting)];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-  [ChromeEarlGrey
-      waitForWebStateContainingText:l10n_util::GetStringUTF8(
-                                        IDS_LOOKALIKE_URL_BACK_TO_SAFETY)];
-
-  // Tap on the "Back to safety" button and verify that the safe content
-  // is loaded.
-  [ChromeEarlGrey tapWebStateElementWithID:@"primary-button"];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-
-  // Verify that the warning is shown when navigating forward and that safe
-  // content is shown when navigating back again.
-  [[EarlGrey selectElementWithMatcher:ForwardButton()]
-      performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-  [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-}
-
-// Tests that Close page works when there is no suggested URL and unable
-// to go back.
-- (void)testLookalikeUrlPageNoSuggestionClosePage {
-  // First navigate to a page that will open the lookalike URL in a new tab,
-  // then open the lookalike page.
-  [ChromeEarlGrey loadURL:self.testServer->GetURL(kLookalikeInNewTab)];
-  [ChromeEarlGrey tapWebStateElementWithID:@"lookalike-newtab"];
-
-  // Verify that the new tab has loaded before setting up the policy decider
-  // for the new web state. Then reload to make sure the interstitial is
-  // displayed.
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeContent];
-  [LookalikeUrlAppInterface setUpLookalikeUrlDeciderForWebState];
-  [ChromeEarlGrey reload];
-
-  // Verify that a warning and the correct button is shown.
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-  [ChromeEarlGrey
-      waitForWebStateContainingText:l10n_util::GetStringUTF8(
-                                        IDS_LOOKALIKE_URL_CLOSE_PAGE)];
-
-  // Tap on the "Close" button and verify that the page closes.
-  [ChromeEarlGrey tapWebStateElementWithID:@"primary-button"];
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeInNewTabContent];
-}
-
-// Tests proceeding past the lookalike warning and that opening the page in
-// a new tab will bypass the warning.
-- (void)testProceedingPastLookalikeUrlWarning {
-  // Load the lookalike page and verify a warning is shown.
-  [ChromeEarlGrey loadURL:_lookalikeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  // Tap on the link to ignore the warning, and verify that the page is loaded.
-  [ChromeEarlGrey tapWebStateElementWithID:@"proceed-button"];
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeContent];
-
-  // In a new tab, the warning should not be shown.
-  [ChromeEarlGrey openNewTab];
-  [ChromeEarlGrey loadURL:_lookalikeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeContent];
-}
-
-// Tests displaying a warning for an lookalike URL, proceeding past the warning,
-// and navigating back/forward, in incognito.
-- (void)testProceedingPastLookalikeWarningInIncognito {
-  [ChromeEarlGrey openNewIncognitoTab];
-  [LookalikeUrlAppInterface setUpLookalikeUrlDeciderForWebState];
-
-  // Navigate to safe page first to enable later verification of
-  // back/forward navigation.
-  [ChromeEarlGrey loadURL:_safeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-
-  // Load the lookalike page and verify a warning is shown.
-  [ChromeEarlGrey loadURL:_lookalikeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  // Tap on the link to ignore the warning, and verify that the page is loaded.
-  [ChromeEarlGrey tapWebStateElementWithID:@"proceed-button"];
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeContent];
-
-  // Verify that no warning is shown when navigating back and then forward to
-  // the unsafe page.
-  [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-  [[EarlGrey selectElementWithMatcher:ForwardButton()]
-      performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeContent];
-}
-
-// Tests that performing session restoration to a lookalike URL warning page
-// preserves navigation history.
-- (void)testRestoreToWarningPagePreservesHistory {
-  // Build up navigation history that consists of a safe URL, a warning page,
-  // and the suggested safe URL.
-  [ChromeEarlGrey loadURL:self.testServer->GetURL("/echoall")];
-  std::string safeContent2 = "Request Body";
-  [ChromeEarlGrey waitForWebStateContainingText:safeContent2];
-
-  // Load the lookalike URL page and verify a warning is shown.
-  [ChromeEarlGrey loadURL:_lookalikeURL];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  // Tap on the "Go to" button and verify that the suggested page contents
-  // are loaded.
-  [ChromeEarlGrey tapWebStateElementWithID:@"primary-button"];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-
-  // Navigate back to the interstitial. Now both the back list and the forward
-  // list are non-empty.
-  [ChromeEarlGrey goBack];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  // Do a session restoration and verify that all navigation history is
-  // preserved. For this test, the policy decider doesn't get installed for
-  // the first page load, so expect the page content instead of the warning.
-  [ChromeEarlGrey triggerRestoreViaTabGridRemoveAllUndo];
-  [ChromeEarlGrey waitForWebStateContainingText:kLookalikeContent];
-  [LookalikeUrlAppInterface setUpLookalikeUrlDeciderForWebState];
-
-  [ChromeEarlGrey goBack];
-  [ChromeEarlGrey waitForWebStateContainingText:safeContent2];
-
-  // The policy decider will trigger at this point, so the warning should
-  // be shown.
-  [ChromeEarlGrey goForward];
-  [ChromeEarlGrey waitForWebStateContainingText:_lookalikeBlockingPageContent];
-
-  [ChromeEarlGrey goForward];
-  [ChromeEarlGrey waitForWebStateContainingText:_safeContent];
-}
-
-@end
diff --git a/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni b/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
index cffa1a1..6a30fcb 100644
--- a/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
+++ b/ios/chrome/test/earl_grey2/chrome_ios_eg2_test.gni
@@ -33,6 +33,13 @@
       if (ios_chrome_info_plist_additions != []) {
         info_plists += ios_chrome_info_plist_additions
       }
+      if (ios_enable_multi_window) {
+        info_plists +=
+            [ "//ios/chrome/app/resources/MultiWindowEnabled+Info.plist" ]
+      } else if (ios_enable_scene_startup) {
+        info_plists +=
+            [ "//ios/chrome/app/resources/MultiWindowDisabled+Info.plist" ]
+      }
       if (ios_chrome_info_plist_addition_targets != []) {
         if (!defined(deps)) {
           deps = []
diff --git a/ios/components/security_interstitials/lookalikes/lookalike_url_controller_client.mm b/ios/components/security_interstitials/lookalikes/lookalike_url_controller_client.mm
index 769ff52..a11b3eb 100644
--- a/ios/components/security_interstitials/lookalikes/lookalike_url_controller_client.mm
+++ b/ios/components/security_interstitials/lookalikes/lookalike_url_controller_client.mm
@@ -46,10 +46,9 @@
   if (!safe_url_.is_valid()) {
     IOSBlockingPageControllerClient::GoBack();
   } else {
-    // For simplicity and because replacement doesn't always work, the
-    // navigation to the safe URL does not replace the navigation to
-    // the interstitial. However, this is acceptable since if a user
-    // navigates back to the lookalike, the interstitial will be shown.
+    // TODO(crbug.com/1058898): Replace the last committed navigation
+    // (the interstitial) with the safe URL navigation to prevent the
+    // back button from returning to the bad site.
     OpenUrlInCurrentTab(safe_url_);
   }
 }
diff --git a/ios/components/security_interstitials/lookalikes/lookalike_url_error.h b/ios/components/security_interstitials/lookalikes/lookalike_url_error.h
index e8606a0..5183cf70 100644
--- a/ios/components/security_interstitials/lookalikes/lookalike_url_error.h
+++ b/ios/components/security_interstitials/lookalikes/lookalike_url_error.h
@@ -7,16 +7,10 @@
 
 #import <Foundation/Foundation.h>
 
-#import "ios/web/public/navigation/web_state_policy_decider.h"
-
 // The error domain for lookalike URL errors.
 extern const NSErrorDomain kLookalikeUrlErrorDomain;
 
 // Error code for navigations to lookalike URLs.
 extern const NSInteger kLookalikeUrlErrorCode;
 
-// Creates a PolicyDecision that cancels a navigation to show a lookalike
-// error.
-web::WebStatePolicyDecider::PolicyDecision CreateLookalikeErrorDecision();
-
 #endif  // IOS_COMPONENTS_SECURITY_INTERSTITIALS_LOOKALIKES_LOOKALIKE_URL_ERROR_H_
diff --git a/ios/components/security_interstitials/lookalikes/lookalike_url_error.mm b/ios/components/security_interstitials/lookalikes/lookalike_url_error.mm
index 00e19439..fffae48 100644
--- a/ios/components/security_interstitials/lookalikes/lookalike_url_error.mm
+++ b/ios/components/security_interstitials/lookalikes/lookalike_url_error.mm
@@ -11,10 +11,3 @@
 const NSErrorDomain kLookalikeUrlErrorDomain =
     @"com.google.chrome.lookalike_url";
 const NSInteger kLookalikeUrlErrorCode = -1003;
-
-web::WebStatePolicyDecider::PolicyDecision CreateLookalikeErrorDecision() {
-  return web::WebStatePolicyDecider::PolicyDecision::CancelAndDisplayError(
-      [NSError errorWithDomain:kLookalikeUrlErrorDomain
-                          code:kLookalikeUrlErrorCode
-                      userInfo:nil]);
-}
diff --git a/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm b/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm
index 371c01e..555ed74 100644
--- a/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm
+++ b/ios/components/security_interstitials/lookalikes/lookalike_url_tab_helper.mm
@@ -19,6 +19,15 @@
 #endif
 
 namespace {
+// Creates a PolicyDecision that cancels a navigation to show a lookalike
+// error.
+web::WebStatePolicyDecider::PolicyDecision CreateLookalikeErrorDecision() {
+  return web::WebStatePolicyDecider::PolicyDecision::CancelAndDisplayError(
+      [NSError errorWithDomain:kLookalikeUrlErrorDomain
+                          code:kLookalikeUrlErrorCode
+                      userInfo:nil]);
+}
+
 // Creates a PolicyDecision that allows the navigation.
 web::WebStatePolicyDecider::PolicyDecision CreateAllowDecision() {
   return web::WebStatePolicyDecider::PolicyDecision::Allow();
@@ -41,7 +50,7 @@
     return;
   }
 
-  // TODO(crbug.com/1104386): Create container and ReleaseInterstitialParams.
+  // TODO(crbug.com/1058898): Create container and ReleaseInterstitialParams.
   // Get stored interstitial parameters early. Doing so ensures that a
   // navigation to an irrelevant (for this interstitial's purposes) URL such as
   // chrome://settings while the lookalike interstitial is being shown clears
@@ -69,7 +78,7 @@
     return;
   }
 
-  // TODO(crbug.com/1104386): If this is a reload and if the current
+  // TODO(crbug.com/1058898): If this is a reload and if the current
   // URL is the last URL of the stored redirect chain, the interstitial
   // was probably reloaded. Stop the reload and navigate back to the
   // original lookalike URL so that the full checks are exercised again.
@@ -82,7 +91,7 @@
     return;
   }
 
-  // TODO(crbug.com/1104384): After site engagement has been componentized,
+  // TODO(crbug.com/1058898): After site engagement has been componentized,
   // fetch and set |engaged_sites| here so that an interstitial won't be
   // shown on engaged sites, and so that the interstitial will be shown on
   // lookalikes of engaged sites.
diff --git a/ios/web_view/internal/sync/web_view_sync_client_unittest.mm b/ios/web_view/internal/sync/web_view_sync_client_unittest.mm
index c8b651d..d0e2147 100644
--- a/ios/web_view/internal/sync/web_view_sync_client_unittest.mm
+++ b/ios/web_view/internal/sync/web_view_sync_client_unittest.mm
@@ -6,11 +6,13 @@
 
 #include <memory>
 
+#include "base/feature_list.h"
 #include "base/memory/scoped_refptr.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h"
 #include "components/history/core/common/pref_names.h"
 #include "components/invalidation/impl/fake_invalidation_service.h"
 #include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
@@ -40,8 +42,11 @@
         profile_password_store_(
             base::MakeRefCounted<password_manager::TestPasswordStore>()),
         account_password_store_(
-            base::MakeRefCounted<password_manager::TestPasswordStore>(
-                /*is_account_store=*/true)),
+            base::FeatureList::IsEnabled(
+                password_manager::features::kEnablePasswordsAccountStorage)
+                ? base::MakeRefCounted<password_manager::TestPasswordStore>(
+                      /*is_account_store=*/true)
+                : nullptr),
         client_(profile_web_data_service_.get(),
                 account_web_data_service_.get(),
                 profile_password_store_.get(),
@@ -54,12 +59,16 @@
     pref_service_.registry()->RegisterBooleanPref(
         prefs::kSavingBrowserHistoryDisabled, true);
     profile_password_store_->Init(&pref_service_, base::DoNothing());
-    account_password_store_->Init(&pref_service_, base::DoNothing());
+    if (account_password_store_) {
+      account_password_store_->Init(&pref_service_, base::DoNothing());
+    }
   }
 
   ~WebViewSyncClientTest() override {
     profile_password_store_->ShutdownOnUIThread();
-    account_password_store_->ShutdownOnUIThread();
+    if (account_password_store_) {
+      account_password_store_->ShutdownOnUIThread();
+    }
   }
 
   web::WebTaskEnvironment task_environment_;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 8cc8cea..e5fc6b4 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -410,7 +410,7 @@
 
 // Enables using the media history store to store media engagement metrics.
 const base::Feature kUseMediaHistoryStore{"UseMediaHistoryStore",
-                                          base::FEATURE_ENABLED_BY_DEFAULT};
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Use R16 texture for 9-16 bit channel instead of half-float conversion by CPU.
 const base::Feature kUseR16Texture{"use-r16-texture",
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index 99a9d037..e9a4ed6 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -48,6 +48,7 @@
 
     deps = [
       ":internal",
+      ":out_of_process_instance",
       ":pdf",
       "//base",
       "//ppapi/cpp:objects",
@@ -87,8 +88,6 @@
       "draw_utils/coordinates.h",
       "draw_utils/shadow.cc",
       "draw_utils/shadow.h",
-      "out_of_process_instance.cc",
-      "out_of_process_instance.h",
       "page_orientation.cc",
       "page_orientation.h",
       "paint_aggregator.cc",
@@ -185,6 +184,27 @@
     ]
   }
 
+  source_set("out_of_process_instance") {
+    visibility = [ ":*" ]
+
+    sources = [
+      "out_of_process_instance.cc",
+      "out_of_process_instance.h",
+    ]
+
+    configs += [ ":pdf_common_config" ]
+
+    deps = [
+      ":features",
+      ":internal",
+      "//base",
+      "//net",
+      "//ppapi/cpp:objects",
+      "//ppapi/cpp/private:internal_module",
+      "//ui/base",
+    ]
+  }
+
   source_set("pdf_test_utils") {
     visibility = [ ":*" ]
 
@@ -246,6 +266,7 @@
       ":assert_enums",
       ":features",
       ":internal",
+      ":out_of_process_instance",
       ":pdf",
       ":pdf_test_utils",
       "//base",
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index 78dcb8a..920461ed 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -360,7 +360,7 @@
   // a Linux OS.
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   if (is_googler_) {
-    heartbeat.set_hostname(base::MD5String(net::GetHostName()));
+    heartbeat.set_hostname_hash(base::MD5String(net::GetHostName()));
   }
 #endif
   return heartbeat;
diff --git a/remoting/host/heartbeat_sender_unittest.cc b/remoting/host/heartbeat_sender_unittest.cc
index c155c283..590848c 100644
--- a/remoting/host/heartbeat_sender_unittest.cc
+++ b/remoting/host/heartbeat_sender_unittest.cc
@@ -77,9 +77,9 @@
   is_linux = true;
 #endif
   if (is_googler && is_linux) {
-    ASSERT_TRUE(request.has_hostname());
+    ASSERT_TRUE(request.has_hostname_hash());
   } else {
-    ASSERT_FALSE(request.has_hostname());
+    ASSERT_FALSE(request.has_hostname_hash());
   }
 }
 
diff --git a/remoting/proto/remoting/v1/directory_messages.proto b/remoting/proto/remoting/v1/directory_messages.proto
index 50ff460..e55f6fd 100644
--- a/remoting/proto/remoting/v1/directory_messages.proto
+++ b/remoting/proto/remoting/v1/directory_messages.proto
@@ -76,7 +76,7 @@
   optional bool is_initial_heartbeat = 11;
 
   // A hash of the local hostname of the machine.
-  optional string hostname = 12;
+  optional string hostname_hash = 12;
 }
 
 // Sent in response to a HeartbeatRequest.
diff --git a/services/viz/public/mojom/BUILD.gn b/services/viz/public/mojom/BUILD.gn
index d6d676d..0eb153d 100644
--- a/services/viz/public/mojom/BUILD.gn
+++ b/services/viz/public/mojom/BUILD.gn
@@ -129,6 +129,16 @@
         "//ui/gfx/geometry/mojom",
       ]
     },
+    {
+      types = [
+        {
+          mojom = "viz.mojom.LocalSurfaceIdAllocation"
+          cpp = "::viz::LocalSurfaceIdAllocation"
+        },
+      ]
+      traits_headers = [ "//services/viz/public/cpp/compositing/local_surface_id_allocation_mojom_traits.h" ]
+      traits_public_deps = [ "//components/viz/common" ]
+    },
   ]
 
   cpp_typemaps = shared_cpp_typemaps
@@ -309,16 +319,6 @@
     {
       types = [
         {
-          mojom = "viz.mojom.LocalSurfaceIdAllocation"
-          cpp = "::viz::LocalSurfaceIdAllocation"
-        },
-      ]
-      traits_headers = [ "//services/viz/public/cpp/compositing/local_surface_id_allocation_mojom_traits.h" ]
-      traits_public_deps = [ "//components/viz/common" ]
-    },
-    {
-      types = [
-        {
           mojom = "viz.mojom.PaintFilter"
           cpp = "::sk_sp<::cc::PaintFilter>"
         },
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index 9749a09..f33a9b6 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -134507,18 +134507,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "weblayer_browsertests",
         "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
       },
@@ -156165,18 +156153,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "weblayer_browsertests",
         "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
       },
@@ -157626,18 +157602,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "weblayer_browsertests",
         "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
       },
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 9331e428..f55b24f 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -1157,18 +1157,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "weblayer_browsertests",
         "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
       },
@@ -6975,18 +6963,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "weblayer_browsertests",
         "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
       },
@@ -8436,18 +8412,6 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "weblayer_browsertests",
         "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index ae42c443..5634b44 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2527,6 +2527,12 @@
       'android-pie-x86-rel',
       'VR Linux',
       'android-code-coverage-native', # crbug/1018431
+      # These tests fail on Win7, and given that We don't support VR there,
+      # these tests are unnecessary there.
+      # chromium.win
+      'Win 7 Tests x64 (1)',
+      'Win7 Tests (1)',
+      'Win7 Tests (dbg)(1)',
     ],
   },
   'wayland_client_perftests': {
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index cb8de7ee..5a35d2b 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -151,6 +151,8 @@
     "user_agent/user_agent_metadata.cc",
     "web_package/signed_exchange_consts.cc",
     "web_package/web_package_request_matcher.cc",
+    "widget/screen_info_mojom_traits.cc",
+    "widget/visual_properties_mojom_traits.cc",
   ]
 
   sources += get_target_outputs(":make_generated_document_policy_features")
diff --git a/third_party/blink/common/DEPS b/third_party/blink/common/DEPS
index 352de21d..46ba7ce8 100644
--- a/third_party/blink/common/DEPS
+++ b/third_party/blink/common/DEPS
@@ -8,6 +8,7 @@
     # It is allowed to depend on common chromium stuff (and itself).
     "+base",
     "+build",
+    "+cc/mojom/browser_controls_params.mojom.h",
     "+net",
     "+media",
     "+mojo",
@@ -19,6 +20,7 @@
     "+services/network/public/mojom/url_loader_factory.mojom.h",
     "+services/network/public/mojom/url_response_head.mojom.h",
     "+services/network/public/mojom/url_response_head.mojom-forward.h",
+    "+services/viz/public/mojom/compositing/local_surface_id_allocation.mojom.h",
     "+skia/public/mojom/bitmap_skbitmap_mojom_traits.h",
     "+testing/gmock/include/gmock",
     "+testing/gtest/include/gtest",
@@ -29,6 +31,7 @@
     "+ui/events/base_event_utils.h",
     "+ui/gfx/transform.h",
     "+ui/gfx/geometry",
+    "+ui/gfx/mojom/color_space.mojom.h",
     "+ui/gfx/win/direct_write.h",
     "+ui/latency/mojom/latency_info_mojom_traits.h",
     "+url",
diff --git a/third_party/blink/common/widget/OWNERS b/third_party/blink/common/widget/OWNERS
new file mode 100644
index 0000000..d5fefd8
--- /dev/null
+++ b/third_party/blink/common/widget/OWNERS
@@ -0,0 +1,2 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/common/widget/screen_info_mojom_traits.cc b/third_party/blink/common/widget/screen_info_mojom_traits.cc
new file mode 100644
index 0000000..f12ebd7
--- /dev/null
+++ b/third_party/blink/common/widget/screen_info_mojom_traits.cc
@@ -0,0 +1,29 @@
+// 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 "third_party/blink/public/common/widget/screen_info_mojom_traits.h"
+
+#include "ui/gfx/geometry/mojom/geometry.mojom.h"
+#include "ui/gfx/mojom/color_space.mojom.h"
+
+namespace mojo {
+
+bool StructTraits<blink::mojom::ScreenInfoDataView, blink::ScreenInfo>::Read(
+    blink::mojom::ScreenInfoDataView data,
+    blink::ScreenInfo* out) {
+  if (!data.ReadColorSpace(&out->color_space) || !data.ReadRect(&out->rect) ||
+      !data.ReadAvailableRect(&out->available_rect))
+    return false;
+
+  out->device_scale_factor = data.device_scale_factor();
+  out->depth = data.depth();
+  out->depth_per_component = data.depth_per_component();
+  out->is_monochrome = data.is_monochrome();
+  out->display_frequency = data.display_frequency();
+  out->orientation_type = data.orientation_type();
+  out->orientation_angle = data.orientation_angle();
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/common/widget/visual_properties_mojom_traits.cc b/third_party/blink/common/widget/visual_properties_mojom_traits.cc
new file mode 100644
index 0000000..1eef5140
--- /dev/null
+++ b/third_party/blink/common/widget/visual_properties_mojom_traits.cc
@@ -0,0 +1,39 @@
+// 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 "third_party/blink/public/common/widget/visual_properties_mojom_traits.h"
+
+#include "cc/mojom/browser_controls_params.mojom.h"
+#include "services/viz/public/mojom/compositing/local_surface_id_allocation.mojom.h"
+#include "third_party/blink/public/mojom/widget/screen_info.mojom.h"
+
+namespace mojo {
+
+bool StructTraits<
+    blink::mojom::VisualPropertiesDataView,
+    blink::VisualProperties>::Read(blink::mojom::VisualPropertiesDataView data,
+                                   blink::VisualProperties* out) {
+  if (!data.ReadScreenInfo(&out->screen_info) ||
+      !data.ReadMinSizeForAutoResize(&out->min_size_for_auto_resize) ||
+      !data.ReadMaxSizeForAutoResize(&out->max_size_for_auto_resize) ||
+      !data.ReadNewSize(&out->new_size) ||
+      !data.ReadVisibleViewportSize(&out->visible_viewport_size) ||
+      !data.ReadCompositorViewportPixelRect(
+          &out->compositor_viewport_pixel_rect) ||
+      !data.ReadBrowserControlsParams(&out->browser_controls_params) ||
+      !data.ReadLocalSurfaceIdAllocation(&out->local_surface_id_allocation) ||
+      !data.ReadRootWidgetWindowSegments(&out->root_widget_window_segments))
+    return false;
+  out->auto_resize_enabled = data.auto_resize_enabled();
+  out->scroll_focused_node_into_view = data.scroll_focused_node_into_view();
+  out->is_fullscreen_granted = data.is_fullscreen_granted();
+  out->display_mode = data.display_mode();
+  out->capture_sequence_number = data.capture_sequence_number();
+  out->zoom_level = data.zoom_level();
+  out->page_scale_factor = data.page_scale_factor();
+  out->is_pinch_gesture_active = data.is_pinch_gesture_active();
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/public/common/widget/OWNERS b/third_party/blink/public/common/widget/OWNERS
new file mode 100644
index 0000000..d5fefd8
--- /dev/null
+++ b/third_party/blink/public/common/widget/OWNERS
@@ -0,0 +1,2 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/common/widget/screen_info_mojom_traits.h b/third_party/blink/public/common/widget/screen_info_mojom_traits.h
new file mode 100644
index 0000000..3355f82
--- /dev/null
+++ b/third_party/blink/public/common/widget/screen_info_mojom_traits.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_WIDGET_SCREEN_INFO_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_WIDGET_SCREEN_INFO_MOJOM_TRAITS_H_
+
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/widget/screen_info.h"
+#include "third_party/blink/public/mojom/widget/screen_info.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct BLINK_COMMON_EXPORT
+    StructTraits<blink::mojom::ScreenInfoDataView, blink::ScreenInfo> {
+  static float device_scale_factor(const blink::ScreenInfo& r) {
+    return r.device_scale_factor;
+  }
+
+  static const gfx::ColorSpace& color_space(const blink::ScreenInfo& r) {
+    return r.color_space;
+  }
+
+  static int depth(const blink::ScreenInfo& r) { return r.depth; }
+
+  static int depth_per_component(const blink::ScreenInfo& r) {
+    return r.depth_per_component;
+  }
+
+  static bool is_monochrome(const blink::ScreenInfo& r) {
+    return r.is_monochrome;
+  }
+
+  static int display_frequency(const blink::ScreenInfo& r) {
+    return r.display_frequency;
+  }
+
+  static const gfx::Rect& rect(const blink::ScreenInfo& r) { return r.rect; }
+
+  static const gfx::Rect& available_rect(const blink::ScreenInfo& r) {
+    return r.available_rect;
+  }
+
+  static blink::mojom::ScreenOrientation orientation_type(
+      const blink::ScreenInfo& r) {
+    return r.orientation_type;
+  }
+
+  static uint16_t orientation_angle(const blink::ScreenInfo& r) {
+    return r.orientation_angle;
+  }
+
+  static bool Read(blink::mojom::ScreenInfoDataView r, blink::ScreenInfo* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_WIDGET_SCREEN_INFO_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/common/widget/visual_properties_mojom_traits.h b/third_party/blink/public/common/widget/visual_properties_mojom_traits.h
new file mode 100644
index 0000000..625d502
--- /dev/null
+++ b/third_party/blink/public/common/widget/visual_properties_mojom_traits.h
@@ -0,0 +1,100 @@
+// 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 THIRD_PARTY_BLINK_PUBLIC_COMMON_WIDGET_VISUAL_PROPERTIES_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_WIDGET_VISUAL_PROPERTIES_MOJOM_TRAITS_H_
+
+#include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/common/widget/visual_properties.h"
+#include "third_party/blink/public/mojom/widget/visual_properties.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::VisualPropertiesDataView,
+                                        blink::VisualProperties> {
+  static const blink::ScreenInfo& screen_info(
+      const blink::VisualProperties& r) {
+    return r.screen_info;
+  }
+
+  static bool auto_resize_enabled(const blink::VisualProperties& r) {
+    return r.auto_resize_enabled;
+  }
+
+  static const gfx::Size& min_size_for_auto_resize(
+      const blink::VisualProperties& r) {
+    return r.min_size_for_auto_resize;
+  }
+
+  static const gfx::Size& max_size_for_auto_resize(
+      const blink::VisualProperties& r) {
+    return r.max_size_for_auto_resize;
+  }
+
+  static const gfx::Size& new_size(const blink::VisualProperties& r) {
+    return r.new_size;
+  }
+
+  static const gfx::Size& visible_viewport_size(
+      const blink::VisualProperties& r) {
+    return r.visible_viewport_size;
+  }
+
+  static const gfx::Rect& compositor_viewport_pixel_rect(
+      const blink::VisualProperties& r) {
+    return r.compositor_viewport_pixel_rect;
+  }
+
+  static base::Optional<cc::BrowserControlsParams> browser_controls_params(
+      const blink::VisualProperties& r) {
+    return r.browser_controls_params;
+  }
+
+  static bool scroll_focused_node_into_view(const blink::VisualProperties& r) {
+    return r.scroll_focused_node_into_view;
+  }
+
+  static base::Optional<viz::LocalSurfaceIdAllocation>
+  local_surface_id_allocation(const blink::VisualProperties& r) {
+    return r.local_surface_id_allocation;
+  }
+
+  static bool is_fullscreen_granted(const blink::VisualProperties& r) {
+    return r.is_fullscreen_granted;
+  }
+
+  static blink::mojom::DisplayMode display_mode(
+      const blink::VisualProperties& r) {
+    return r.display_mode;
+  }
+
+  static uint32_t capture_sequence_number(const blink::VisualProperties& r) {
+    return r.capture_sequence_number;
+  }
+
+  static double zoom_level(const blink::VisualProperties& r) {
+    return r.zoom_level;
+  }
+
+  static double page_scale_factor(const blink::VisualProperties& r) {
+    return r.page_scale_factor;
+  }
+
+  static const std::vector<gfx::Rect>& root_widget_window_segments(
+      const blink::VisualProperties& r) {
+    return r.root_widget_window_segments;
+  }
+
+  static bool is_pinch_gesture_active(const blink::VisualProperties& r) {
+    return r.is_pinch_gesture_active;
+  }
+
+  static bool Read(blink::mojom::VisualPropertiesDataView r,
+                   blink::VisualProperties* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_WIDGET_VISUAL_PROPERTIES_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 1a55ea5..5792b5d 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -5289,6 +5289,8 @@
       optional boolean showAreaNames
       # Show line name labels (default: false).
       optional boolean showLineNames
+      # Show track size labels (default: false).
+      optional boolean showTrackSizes
       # The grid container border highlight color (default: transparent).
       optional DOM.RGBA gridBorderColor
       # The cell border color (default: transparent).
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 9ad400d..1960b2e 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -168,7 +168,9 @@
     "webdatabase/web_database.mojom",
     "websockets/websocket_connector.mojom",
     "webtransport/quic_transport_connector.mojom",
+    "widget/screen_info.mojom",
     "widget/screen_orientation.mojom",
+    "widget/visual_properties.mojom",
     "window_features/window_features.mojom",
     "worker/shared_worker_client.mojom",
     "worker/shared_worker_creation_context_type.mojom",
@@ -280,6 +282,26 @@
       ]
       traits_headers = [ "//third_party/blink/public/common/input/web_coalesced_input_event_mojom_traits.h" ]
     },
+    {
+      types = [
+        {
+          mojom = "blink.mojom.ScreenInfo"
+          cpp = "::blink::ScreenInfo"
+        },
+      ]
+      traits_headers = [
+        "//third_party/blink/public/common/widget/screen_info_mojom_traits.h",
+      ]
+    },
+    {
+      types = [
+        {
+          mojom = "blink.mojom.VisualProperties"
+          cpp = "::blink::VisualProperties"
+        },
+      ]
+      traits_headers = [ "//third_party/blink/public/common/widget/visual_properties_mojom_traits.h" ]
+    },
   ]
   cpp_typemaps = [
     {
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
index a8fb913e..2a9355833 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
@@ -134,6 +134,9 @@
   // Controls use of Clipboard API Write.
   kClipboardWrite = 77,
 
+  // Controls use of Web Share API.
+  kWebShare = 78,
+
   // Don't change assigned numbers of any item, and don't reuse removed slots.
   // Add new features at the end of the enum.
   // Also, run update_feature_policy_enum.py in
diff --git a/third_party/blink/public/mojom/page/widget.mojom b/third_party/blink/public/mojom/page/widget.mojom
index b1bd731..59deb9d 100644
--- a/third_party/blink/public/mojom/page/widget.mojom
+++ b/third_party/blink/public/mojom/page/widget.mojom
@@ -82,6 +82,9 @@
   // The parameter specifies the location in the render widget's coordinates.
   ShowContextMenu(
       ui.mojom.MenuSourceType source_type, gfx.mojom.Point location);
+
+  // Binds an WidgetCompositor interface.
+  BindWidgetCompositor(pending_receiver<WidgetCompositor> host);
 };
 
 // Implemented in Browser, this interface defines frame-widget-specific methods that
@@ -169,3 +172,10 @@
              mojo_base.mojom.TextDirection focus_dir,
              bool is_anchor_first);
 };
+
+// This interface is bound on the compositor thread.
+interface WidgetCompositor {
+  // Requests that the RenderWidget sends back a response after the next main
+  // frame is generated and presented in the display compositor.
+  VisualStateRequest() => ();
+};
diff --git a/third_party/blink/public/mojom/widget/screen_info.mojom b/third_party/blink/public/mojom/widget/screen_info.mojom
new file mode 100644
index 0000000..baa2958e
--- /dev/null
+++ b/third_party/blink/public/mojom/widget/screen_info.mojom
@@ -0,0 +1,47 @@
+// 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.
+
+module blink.mojom;
+
+import "third_party/blink/public/mojom/widget/screen_orientation.mojom";
+import "ui/gfx/geometry/mojom/geometry.mojom";
+import "ui/gfx/mojom/color_space.mojom";
+
+// Information about the screen on which a WidgetBase is being displayed. This
+// is the content counterpart to blink::ScreenInfo.
+struct ScreenInfo {
+  // Device scale factor. Specifies the ratio between physical and logical
+  // pixels.
+  float device_scale_factor = 1;
+
+  gfx.mojom.ColorSpace color_space;
+
+  // The screen depth in bits per pixel
+  uint32 depth;
+
+  // The bits per colour component. This assumes that the colours are balanced
+  // equally.
+  uint32 depth_per_component;
+
+  // This can be true for black and white printers
+  bool is_monochrome = false;
+
+  // The display frequency in Hz of the monitor. Set to 0 if it fails in the
+  // monitor frequency query.
+  uint32 display_frequency;
+
+  // The display monitor rectangle in virtual-screen coordinates. Note that
+  // this may be negative.
+  gfx.mojom.Rect rect;
+
+  // The portion of the monitor's rectangle that can be used by applications.
+  gfx.mojom.Rect available_rect;
+
+  // The monitor's orientation.
+  ScreenOrientation orientation_type = ScreenOrientation.kUndefined;
+
+  // This is the orientation angle of the displayed content in degrees.
+  // It is the opposite of the physical rotation.
+  uint16 orientation_angle;
+};
\ No newline at end of file
diff --git a/third_party/blink/public/mojom/widget/visual_properties.mojom b/third_party/blink/public/mojom/widget/visual_properties.mojom
new file mode 100644
index 0000000..1786849
--- /dev/null
+++ b/third_party/blink/public/mojom/widget/visual_properties.mojom
@@ -0,0 +1,91 @@
+// 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.
+
+module blink.mojom;
+
+import "cc/mojom/browser_controls_params.mojom";
+import "services/viz/public/mojom/compositing/local_surface_id_allocation.mojom";
+import "third_party/blink/public/mojom/manifest/display_mode.mojom";
+import "third_party/blink/public/mojom/widget/screen_info.mojom";
+import "ui/gfx/geometry/mojom/geometry.mojom";
+
+// See public/common/widget/visual_properties.h
+struct VisualProperties {
+  // Information about the screen (dpi, depth, etc..).
+  ScreenInfo screen_info;
+
+  // Whether or not blink should be in auto-resize mode.
+  bool auto_resize_enabled = false;
+
+  // The minimum size for Blink if auto-resize is enabled.
+  gfx.mojom.Size min_size_for_auto_resize;
+
+  // The maximum size for Blink if auto-resize is enabled.
+  gfx.mojom.Size max_size_for_auto_resize;
+
+  // The size for the widget in DIPs.
+  gfx.mojom.Size new_size;
+
+  // The size of the area of the widget that is visible to the user, in DIPs.
+  // The visible area may be empty if the visible area does not intersect with
+  // the widget, for example in the case of a child frame that is entirely
+  // scrolled out of the main frame's viewport. It may also be smaller than the
+  // widget's size in |new_size| due to the UI hiding part of the widget, such
+  // as with an on-screen keyboard.
+  gfx.mojom.Size visible_viewport_size;
+
+  // The rect of compositor's viewport in pixels. Note that for top level
+  // widgets this is roughly the DSF scaled new_size put into a rect. For child
+  // frame widgets it is a pixel-perfect bounds of the visible region of the
+  // widget. The size would be similar to visible_viewport_size, but in physical
+  // pixels and computed via very different means.
+  // TODO(danakj): It would be super nice to remove one of |new_size|,
+  // |visible_viewport_size| and |compositor_viewport_pixel_rect|. Their values
+  // overlap in purpose, creating a very confusing situation about which to use
+  // for what, and how they should relate or not.
+  gfx.mojom.Rect compositor_viewport_pixel_rect;
+
+  // Browser controls params such as top and bottom controls heights, whether
+  // controls shrink blink size etc.
+  cc.mojom.BrowserControlsParams? browser_controls_params;
+
+  // Whether or not the focused node should be scrolled into view after the
+  // resize.
+  // Page scoped value.
+  bool scroll_focused_node_into_view;
+
+  // The local surface ID to use (if valid) and its allocation time.
+  viz.mojom.LocalSurfaceIdAllocation? local_surface_id_allocation;
+
+  // Indicates whether tab-initiated fullscreen was granted.
+  // Page scoped value.
+  bool is_fullscreen_granted;
+
+  // The display mode. This will be replicated amongst subframes.
+  DisplayMode display_mode = DisplayMode.kUndefined;
+
+  // This represents the latest capture sequence number requested. When this is
+  // incremented, that means the caller wants to synchronize surfaces which
+  // should cause a new LocalSurfaceId to be generated.
+  uint32 capture_sequence_number;
+
+  // This represents the page zoom level for a WebContents.
+  // (0 is the default value which results in 1.0 zoom factor).
+  // Page scoped value.
+  double zoom_level;
+
+  // This represents the page's scale factor, which changes during pinch zoom.
+  // It needs to be shared with subframes.
+  float page_scale_factor = 1;
+
+  // The logical segments of the root widget, in widget-relative DIPs. This
+  // property is set by the root RenderWidget in the renderer process, then
+  // propagated to child local frame roots via RenderFrameProxy/
+  // CrossProcessFrameConnector.
+  array<gfx.mojom.Rect> root_widget_window_segments;
+
+  // Indicates whether a pinch gesture is currently active. Originates in the
+  // main frame's renderer, and needs to be shared with subframes.
+  bool is_pinch_gesture_active;
+};
diff --git a/third_party/blink/public/web/web_shared_worker.h b/third_party/blink/public/web/web_shared_worker.h
index b087ef1..25a2b5f4 100644
--- a/third_party/blink/public/web/web_shared_worker.h
+++ b/third_party/blink/public/web/web_shared_worker.h
@@ -60,11 +60,10 @@
  public:
   virtual ~WebSharedWorker() {}
 
-  // Instantiate a WebSharedWorker that interacts with the shared worker.
+  // Instantiates a WebSharedWorker that interacts with the shared worker and
+  // starts a worker context.
   // WebSharedWorkerClient given here should own this instance.
-  static std::unique_ptr<WebSharedWorker> Create(WebSharedWorkerClient*);
-
-  virtual void StartWorkerContext(
+  static std::unique_ptr<WebSharedWorker> CreateAndStart(
       const WebURL& script_url,
       mojom::ScriptType script_type,
       network::mojom::CredentialsMode,
@@ -84,7 +83,8 @@
           browser_interface_broker,
       bool pause_worker_context_on_start,
       std::unique_ptr<blink::WorkerMainScriptLoadParameters>
-          worker_main_script_load_params) = 0;
+          worker_main_script_load_params,
+      WebSharedWorkerClient*);
 
   // Sends a connect event to the SharedWorker context.
   virtual void Connect(MessagePortChannel) = 0;
diff --git a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
index 4ec693b..d081598 100644
--- a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
+++ b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
@@ -650,6 +650,28 @@
 
 `[GetterCallWith]` and `[SetterCallWith]` apply to attributes, and only affects the signature of the getter and setter, respectively.
 
+NOTE: The `ExecutionContext` that you can get with [CallWith=ExecutionContext] or that you can extract from [CallWith=ScriptState] is the ExecutionContext associated with the V8 wrapper of the receiver object, which might be different from the ExecutionContext of the document tree to which the receiver object belongs.  See the following example.
+
+```js
+// V8 wrapper of |span| is associated with |windowA|.
+span = windowA.document.createElement("span");
+// |span| belongs to the document tree of |windowB|.
+windowB.document.body.appendChild(span);
+```
+
+```c++
+// Suppose [CallWith=ExecutionContext] void foo();
+void HTMLSpanElement::foo(ExecutionContext* execution_context) {
+  // The ExecutionContext associated with the creation context of the V8 wrapper
+  // of the receiver object.
+  execution_context;  // the ExecutionContext of |windowA|
+
+  // Node::GetExecutionContext() returns the ExecutionContext of the document
+  // tree to which |this| (the receiver object) belongs.
+  GetExecutionContext();  // the ExecutionContext of |windowB|
+}
+```
+
 #### [CallWith=ScriptState] _(m, a*)_
 
 `[CallWith=ScriptState]` is used in a number of places for methods.
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
index 03110a4..3e69ed9 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
@@ -167,7 +167,10 @@
   return v8::Undefined(isolate);
 }
 
-static const size_t kMaximumDepth = 2000;
+// Non-standard limits, selected to avoid breaking real-world use of the API
+// while also preventing buggy (or malicious) code from causing crashes.
+const size_t kMaximumDepth = 2000;
+const size_t kMaximumArraySize = 1000000;
 
 // Convert a simple (non-Array) script value to an Indexed DB key. If the
 // conversion fails due to a detached buffer, an exception is thrown. If
@@ -261,6 +264,9 @@
   // Initial state.
   {
     v8::Local<v8::Array> array = value.As<v8::Array>();
+    if (array->Length() > kMaximumArraySize)
+      return IDBKey::CreateInvalid();
+
     stack.push_back(std::make_unique<Record>(array));
     seen.push_back(array);
   }
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc
index 4831f78..730210b 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules_test.cc
@@ -382,6 +382,10 @@
   V8TestingScope scope;
   auto key = ScriptToKey(scope, "[,1]");
   EXPECT_FALSE(key->IsValid());
+
+  // Ridiculously large sparse array - ensure we check before allocating.
+  key = ScriptToKey(scope, "Object.assign([], {length: 2e9})");
+  EXPECT_FALSE(key->IsValid());
 }
 
 TEST(IDBKeyFromValue, ShrinkingArray) {
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index bb2d0b7..0d10ce4 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -125,8 +125,10 @@
   if (!baseline)
     return nullptr;
   if (preference && preference->GetValueID() == CSSValueID::kLast) {
-    return MakeGarbageCollected<CSSValuePair>(
-        preference, baseline, CSSValuePair::kDropIdenticalValues);
+    // We still don't have support for 'last baseline' in layout
+    // https://crbug.com/885175
+    // https://crbug.com/886585
+    return nullptr;
   }
   return baseline;
 }
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index d055ef0..30c740c 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -74,8 +74,15 @@
 
 namespace blink {
 
-WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client)
-    : client_(client) {
+WebSharedWorkerImpl::WebSharedWorkerImpl(
+    const base::UnguessableToken& appcache_host_id,
+    WebSharedWorkerClient* client)
+    : reporting_proxy_(MakeGarbageCollected<SharedWorkerReportingProxy>(
+          this,
+          ParentExecutionContextTaskRunners::Create())),
+      worker_thread_(std::make_unique<SharedWorkerThread>(*reporting_proxy_,
+                                                          appcache_host_id)),
+      client_(client) {
   DCHECK(IsMainThread());
 }
 
@@ -88,15 +95,6 @@
   if (asked_to_terminate_)
     return;
   asked_to_terminate_ = true;
-
-  if (!worker_thread_) {
-    client_->WorkerScriptLoadFailed(/*error_message=*/"");
-    // The worker thread hasn't been started yet. Immediately notify the client
-    // of worker termination.
-    client_->WorkerContextDestroyed();
-    // |this| is deleted at this point.
-    return;
-  }
   worker_thread_->Terminate();
   // DidTerminateWorkerThread() will be called asynchronously.
 }
@@ -178,7 +176,6 @@
     network::mojom::ContentSecurityPolicyType policy_type,
     network::mojom::IPAddressSpace creation_address_space,
     const WebFetchClientSettingsObject& outside_fetch_client_settings_object,
-    const base::UnguessableToken& appcache_host_id,
     const base::UnguessableToken& devtools_worker_token,
     CrossVariantMojoRemote<
         mojom::blink::WorkerContentSettingsProxyInterfaceBase> content_settings,
@@ -250,11 +247,6 @@
       std::move(browser_interface_broker), BeginFrameProviderParams(),
       nullptr /* parent_feature_policy */, base::UnguessableToken());
 
-  reporting_proxy_ = MakeGarbageCollected<SharedWorkerReportingProxy>(
-      this, ParentExecutionContextTaskRunners::Create());
-  worker_thread_ =
-      std::make_unique<SharedWorkerThread>(*reporting_proxy_, appcache_host_id);
-
   auto thread_startup_data = WorkerBackingThreadStartupData::CreateDefault();
   thread_startup_data.atomics_wait_mode =
       WorkerBackingThreadStartupData::AtomicsWaitMode::kAllow;
@@ -297,9 +289,38 @@
   TerminateWorkerThread();
 }
 
-std::unique_ptr<WebSharedWorker> WebSharedWorker::Create(
+std::unique_ptr<WebSharedWorker> WebSharedWorker::CreateAndStart(
+    const WebURL& script_request_url,
+    mojom::ScriptType script_type,
+    network::mojom::CredentialsMode credentials_mode,
+    const WebString& name,
+    WebSecurityOrigin constructor_origin,
+    const WebString& user_agent,
+    const UserAgentMetadata& ua_metadata,
+    const WebString& content_security_policy,
+    network::mojom::ContentSecurityPolicyType policy_type,
+    network::mojom::IPAddressSpace creation_address_space,
+    const WebFetchClientSettingsObject& outside_fetch_client_settings_object,
+    const base::UnguessableToken& appcache_host_id,
+    const base::UnguessableToken& devtools_worker_token,
+    CrossVariantMojoRemote<
+        mojom::blink::WorkerContentSettingsProxyInterfaceBase> content_settings,
+    CrossVariantMojoRemote<mojom::blink::BrowserInterfaceBrokerInterfaceBase>
+        browser_interface_broker,
+    bool pause_worker_context_on_start,
+    std::unique_ptr<WorkerMainScriptLoadParameters>
+        worker_main_script_load_params,
     WebSharedWorkerClient* client) {
-  return base::WrapUnique(new WebSharedWorkerImpl(client));
+  auto worker =
+      base::WrapUnique(new WebSharedWorkerImpl(appcache_host_id, client));
+  worker->StartWorkerContext(
+      script_request_url, script_type, credentials_mode, name,
+      constructor_origin, user_agent, ua_metadata, content_security_policy,
+      policy_type, creation_address_space, outside_fetch_client_settings_object,
+      devtools_worker_token, std::move(content_settings),
+      std::move(browser_interface_broker), pause_worker_context_on_start,
+      std::move(worker_main_script_load_params));
+  return worker;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index be255ca..8df6558 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -69,32 +69,9 @@
 // WebSharedWorkerClient::WorkerContextDestroyed().
 class CORE_EXPORT WebSharedWorkerImpl final : public WebSharedWorker {
  public:
-  explicit WebSharedWorkerImpl(WebSharedWorkerClient*);
   ~WebSharedWorkerImpl() override;
 
   // WebSharedWorker methods:
-  void StartWorkerContext(
-      const WebURL&,
-      mojom::ScriptType,
-      network::mojom::CredentialsMode,
-      const WebString& name,
-      WebSecurityOrigin constructor_origin,
-      const WebString& user_agent,
-      const blink::UserAgentMetadata& ua_metadata,
-      const WebString& content_security_policy,
-      network::mojom::ContentSecurityPolicyType,
-      network::mojom::IPAddressSpace,
-      const WebFetchClientSettingsObject& outside_fetch_client_settings_object,
-      const base::UnguessableToken& appcache_host_id,
-      const base::UnguessableToken& devtools_worker_token,
-      CrossVariantMojoRemote<
-          mojom::blink::WorkerContentSettingsProxyInterfaceBase>
-          ontent_settings,
-      CrossVariantMojoRemote<mojom::blink::BrowserInterfaceBrokerInterfaceBase>
-          browser_interface_broker,
-      bool pause_worker_context_on_start,
-      std::unique_ptr<WorkerMainScriptLoadParameters>
-          worker_main_script_load_params) override;
   void Connect(MessagePortChannel) override;
   void TerminateWorkerContext() override;
 
@@ -109,6 +86,33 @@
   void DidTerminateWorkerThread();
 
  private:
+  friend class WebSharedWorker;
+
+  WebSharedWorkerImpl(const base::UnguessableToken& appcache_host_id,
+                      WebSharedWorkerClient*);
+
+  void StartWorkerContext(
+      const WebURL&,
+      mojom::ScriptType,
+      network::mojom::CredentialsMode,
+      const WebString& name,
+      WebSecurityOrigin constructor_origin,
+      const WebString& user_agent,
+      const blink::UserAgentMetadata& ua_metadata,
+      const WebString& content_security_policy,
+      network::mojom::ContentSecurityPolicyType,
+      network::mojom::IPAddressSpace,
+      const WebFetchClientSettingsObject& outside_fetch_client_settings_object,
+      const base::UnguessableToken& devtools_worker_token,
+      CrossVariantMojoRemote<
+          mojom::blink::WorkerContentSettingsProxyInterfaceBase>
+          ontent_settings,
+      CrossVariantMojoRemote<mojom::blink::BrowserInterfaceBrokerInterfaceBase>
+          browser_interface_broker,
+      bool pause_worker_context_on_start,
+      std::unique_ptr<WorkerMainScriptLoadParameters>
+          worker_main_script_load_params);
+
   SharedWorkerThread* GetWorkerThread() { return worker_thread_.get(); }
 
   // Shuts down the worker thread. This may synchronously destroy |this|.
@@ -116,8 +120,8 @@
 
   void ConnectTaskOnWorkerThread(MessagePortChannel);
 
-  Persistent<SharedWorkerReportingProxy> reporting_proxy_;
-  std::unique_ptr<SharedWorkerThread> worker_thread_;
+  const Persistent<SharedWorkerReportingProxy> reporting_proxy_;
+  const std::unique_ptr<SharedWorkerThread> worker_thread_;
 
   // |client_| owns |this|.
   WebSharedWorkerClient* client_;
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.dict b/third_party/blink/renderer/core/feature_policy/feature_policy.dict
index 38cb4eb7..d7dcd9a 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy.dict
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.dict
@@ -41,6 +41,7 @@
 "usb"
 "vertical-scroll"
 "vr"
+"web-share"
 "\"https://example.com/\""
 "*"
 "'self'"
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
index ced3954..d62c4a7 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
@@ -332,6 +332,11 @@
       depends_on: ["ExperimentalProductivityFeatures"],
     },
     {
+      name: "WebShare",
+      feature_policy_name: "web-share",
+      depends_on: ["WebShare"],
+    },
+    {
       name: "WebXr",
       feature_policy_name: "xr-spatial-tracking",
       depends_on: ["WebXR"],
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index 5444575..d13cda4 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -318,6 +318,11 @@
 }
 #endif
 
+void WebFrameWidgetBase::BindWidgetCompositor(
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
+  widget_base_->BindWidgetCompositor(std::move(receiver));
+}
+
 void WebFrameWidgetBase::CancelDrag() {
   // It's possible for this to be called while we're not doing a drag if
   // it's from a previous page that got unloaded.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 9360dda..f3b324e 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -376,6 +376,9 @@
 
   base::Optional<gfx::Point> GetAndResetContextMenuLocation();
 
+  void BindWidgetCompositor(
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) override;
+
   // Called when the FrameView for this Widget's local root is created.
   virtual void DidCreateLocalRootView() {}
 
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 163f3f1..f968e0a 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -1394,7 +1394,7 @@
   ScanAndPreload(preload_scanner_.get());
 }
 
-void HTMLDocumentParser::NotifyScriptLoaded(PendingScript* pending_script) {
+void HTMLDocumentParser::NotifyScriptLoaded() {
   TRACE_EVENT1("blink", "HTMLDocumentParser::NotifyScriptLoaded", "parser",
                (void*)this);
   DCHECK(script_runner_);
@@ -1412,7 +1412,7 @@
     return;
   }
 
-  script_runner_->ExecuteScriptsWaitingForLoad(pending_script);
+  script_runner_->ExecuteScriptsWaitingForLoad();
   if (!IsPaused())
     ResumeParsingAfterPause();
 }
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index a921448..ba05df1 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -168,7 +168,7 @@
   void DocumentElementAvailable() override;
 
   // HTMLParserScriptRunnerHost
-  void NotifyScriptLoaded(PendingScript*) final;
+  void NotifyScriptLoaded() final;
   HTMLInputStream& InputStream() final { return input_; }
   bool HasPreloadScanner() const final {
     return preload_scanner_.get() && !CanParseAsynchronously();
diff --git a/third_party/blink/renderer/core/inspector/console_message_storage.cc b/third_party/blink/renderer/core/inspector/console_message_storage.cc
index 4278aa8..ccb13e8 100644
--- a/third_party/blink/renderer/core/inspector/console_message_storage.cc
+++ b/third_party/blink/renderer/core/inspector/console_message_storage.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
 
 namespace blink {
 
@@ -48,14 +49,24 @@
   return nullptr;
 }
 
+std::unique_ptr<TracedValue> MessageTracedValue(ConsoleMessage* message) {
+  auto value = std::make_unique<TracedValue>();
+  value->SetString("content", message->Message());
+  if (!message->Location()->Url().IsEmpty()) {
+    value->SetString("url", message->Location()->Url());
+  }
+  return value;
+}
+
 void TraceConsoleMessageEvent(ConsoleMessage* message) {
   // Change in this function requires adjustment of Catapult/Telemetry metric
   // tracing/tracing/metrics/console_error_metric.html.
   // See https://crbug.com/880432
   if (message->Level() == mojom::ConsoleMessageLevel::kError) {
-    TRACE_EVENT_INSTANT1("blink.console", "ConsoleMessage::Error",
+    TRACE_EVENT_INSTANT2("blink.console", "ConsoleMessage::Error",
                          TRACE_EVENT_SCOPE_THREAD, "source",
-                         MessageSourceToString(message->Source()));
+                         MessageSourceToString(message->Source()), "message",
+                         MessageTracedValue(message));
   }
 }
 }  // anonymous namespace
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 1cbee31..151b249 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
+#include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
 #include "third_party/blink/renderer/platform/graphics/path.h"
 #include "third_party/blink/renderer/platform/web_test_support.h"
 
@@ -402,6 +403,39 @@
   return grid_config_info;
 }
 
+std::unique_ptr<protocol::ListValue> BuildGridTrackSizes(
+    LayoutGrid* layout_grid,
+    GridTrackSizingDirection direction,
+    float scale,
+    LayoutUnit gap) {
+  std::unique_ptr<protocol::ListValue> sizes = protocol::ListValue::create();
+
+  unsigned i = 0;
+  float start = 0;
+  for (LayoutUnit track : layout_grid->TrackSizesForComputedStyle(direction)) {
+    if (i > 0)
+      start += gap;
+    std::unique_ptr<protocol::DictionaryValue> size_info =
+        protocol::DictionaryValue::create();
+    auto adjusted_size = AdjustForAbsoluteZoom::AdjustFloat(
+        track.ToFloat() * scale, layout_grid->StyleRef());
+    size_info->setDouble("computedSize", adjusted_size);
+    // TODO (alexrudenko): Add authored sizes here
+    float offset = start + track / 2;
+    FloatPoint local_arrow_pos(direction == kForColumns ? offset : 0,
+                               direction == kForColumns ? 0 : offset);
+    FloatPoint abs_arrow_pos =
+        layout_grid->LocalToAbsoluteFloatPoint(local_arrow_pos);
+    size_info->setDouble("x", abs_arrow_pos.X());
+    size_info->setDouble("y", abs_arrow_pos.Y());
+    sizes->pushValue(std::move(size_info));
+    start += track;
+    i++;
+  }
+
+  return sizes;
+}
+
 std::unique_ptr<protocol::ListValue> BuildGridPositiveLineNumberOffsets(
     LayoutGrid* layout_grid,
     const Vector<LayoutUnit>& trackPositions,
@@ -549,6 +583,22 @@
   return lines;
 }
 
+// Gets the rotation angle of the grid layout (clock-wise).
+int GetRotationAngle(LayoutGrid* layout_grid) {
+  // Local vector has 135deg bearing to the Y axis.
+  int local_vector_bearing = 135;
+  FloatPoint local_a(0, 0);
+  FloatPoint local_b(1, 1);
+  FloatPoint abs_a = layout_grid->LocalToAbsoluteFloatPoint(local_a);
+  FloatPoint abs_b = layout_grid->LocalToAbsoluteFloatPoint(local_b);
+  // Compute bearing of the absolute vector against the Y axis.
+  double theta = atan2(abs_b.X() - abs_a.X(), abs_a.Y() - abs_b.Y());
+  if (theta < 0.0)
+    theta += kTwoPiDouble;
+  int bearing = std::round(rad2deg(theta));
+  return bearing - local_vector_bearing;
+}
+
 std::unique_ptr<protocol::DictionaryValue> BuildGridInfo(
     LocalFrameView* containing_view,
     LayoutGrid* layout_grid,
@@ -561,11 +611,22 @@
   const auto& rows = layout_grid->RowPositions();
   const auto& columns = layout_grid->ColumnPositions();
 
+  grid_info->setInteger("rotationAngle", GetRotationAngle(layout_grid));
+
   auto row_gap =
       layout_grid->GridGap(kForRows) + layout_grid->GridItemOffset(kForRows);
   auto column_gap = layout_grid->GridGap(kForColumns) +
                     layout_grid->GridItemOffset(kForColumns);
 
+  if (grid_highlight_config.show_track_sizes) {
+    grid_info->setValue(
+        "columnTrackSizes",
+        BuildGridTrackSizes(layout_grid, kForColumns, scale, column_gap));
+    grid_info->setValue(
+        "rowTrackSizes",
+        BuildGridTrackSizes(layout_grid, kForRows, scale, row_gap));
+  }
+
   PathBuilder row_builder;
   PathBuilder row_gap_builder;
   LayoutUnit row_left = columns.front();
@@ -780,7 +841,8 @@
       show_positive_line_numbers(false),
       show_negative_line_numbers(false),
       show_area_names(false),
-      show_line_names(false) {}
+      show_line_names(false),
+      show_track_sizes(false) {}
 
 InspectorHighlight::InspectorHighlight(
     Node* node,
@@ -1332,6 +1394,7 @@
   config.show_line_names = true;
   config.grid_border_dash = false;
   config.cell_border_dash = true;
+  config.show_track_sizes = true;
   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 698110f..df447b3 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.h
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.h
@@ -39,6 +39,7 @@
   bool show_negative_line_numbers;
   bool show_area_names;
   bool show_line_names;
+  bool show_track_sizes;
 };
 
 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 6b946fd4..c94bb59 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -1375,6 +1375,7 @@
       config->getShowGridExtensionLines(false);
   highlight_config->grid_border_dash = config->getGridBorderDash(false);
   highlight_config->cell_border_dash = config->getCellBorderDash(false);
+  highlight_config->show_track_sizes = config->getShowTrackSizes(false);
   highlight_config->grid_color =
       InspectorDOMAgent::ParseColor(config->getGridBorderColor(nullptr));
   highlight_config->cell_color =
diff --git a/third_party/blink/renderer/core/layout/README.md b/third_party/blink/renderer/core/layout/README.md
index 54270b2..40b7730 100644
--- a/third_party/blink/renderer/core/layout/README.md
+++ b/third_party/blink/renderer/core/layout/README.md
@@ -5,7 +5,7 @@
 
 # Blink Layout
 
-The `Source/core/layout` directory contains the implementation of layout objects.
+The `renderer/core/layout` directory contains the implementation of layout objects.
 It covers the following document lifecycle states:
 
 * LayoutSubtreeChange (`InLayoutSubtreeChange` and `LayoutSubtreeChangeClean`)
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h
index 15236cff..ad1c091 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.h
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -123,7 +123,8 @@
 //   block coordinates", without considering text direction. Examples are
 //   "LogicalLeft" and "LogicalRight".
 //
-// For more, see Source/core/layout/README.md ### Coordinate Spaces.
+// For more information, see the following doc about coordinate spaces:
+// https://chromium.googlesource.com/chromium/src.git/+/master/third_party/blink/renderer/core/layout/README.md#coordinate-spaces
 class CORE_EXPORT LayoutBoxModelObject : public LayoutObject {
  public:
   LayoutBoxModelObject(ContainerNode*);
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index 4cb960b3..efce103 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -354,7 +354,7 @@
     if (!ShouldCreateBoxFragment()) {
       UpdateShouldCreateBoxFragment();
     }
-    if (diff.NeedsCollectInlines()) {
+    if (diff.NeedsReshape()) {
       SetNeedsCollectInlines();
     }
   }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
index 511fc70..96895ee 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
@@ -1268,4 +1268,31 @@
   EXPECT_EQ(String(u"abc \uFFFCx"), GetText());
 }
 
+// https://crbug.com/109654
+TEST_F(NGInlineNodeTest, ReusingRTLAsLTR) {
+  SetupHtml("container",
+            "<div id=container>"
+            "<span id='text' dir=rtl>"
+            "[Text]text"
+            "</span>"
+            "</div>");
+  EXPECT_EQ(String(u"\u2067[Text]text\u2069"), GetText());
+  EXPECT_EQ(Items().size(), 8u);
+  TEST_ITEM_OFFSET_DIR(Items()[0], 0u, 1u, TextDirection::kLtr);
+  TEST_ITEM_OFFSET_DIR(Items()[1], 1u, 1u, TextDirection::kRtl);
+  TEST_ITEM_OFFSET_DIR(Items()[2], 1u, 2u, TextDirection::kRtl);
+  TEST_ITEM_OFFSET_DIR(Items()[3], 2u, 6u, TextDirection::kLtr);
+  TEST_ITEM_OFFSET_DIR(Items()[4], 6u, 7u, TextDirection::kRtl);
+  TEST_ITEM_OFFSET_DIR(Items()[5], 7u, 11u, TextDirection::kLtr);
+  TEST_ITEM_OFFSET_DIR(Items()[6], 11u, 11u, TextDirection::kLtr);
+  TEST_ITEM_OFFSET_DIR(Items()[7], 11u, 12u, TextDirection::kLtr);
+  GetElementById("text")->removeAttribute(html_names::kDirAttr);
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_EQ(String("[Text]text"), GetText());
+  EXPECT_EQ(Items().size(), 3u);
+  TEST_ITEM_OFFSET_DIR(Items()[0], 0u, 0u, TextDirection::kLtr);
+  TEST_ITEM_OFFSET_DIR(Items()[1], 0u, 10u, TextDirection::kLtr);
+  TEST_ITEM_OFFSET_DIR(Items()[2], 10u, 10u, TextDirection::kLtr);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
index 00d719f..1925bc4 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
@@ -47,7 +47,7 @@
     const ComputedStyle* old_style) {
   Base::StyleDidChange(diff, old_style);
 
-  if (diff.NeedsCollectInlines()) {
+  if (diff.NeedsReshape()) {
     Base::SetNeedsCollectInlines();
   }
 }
diff --git a/third_party/blink/renderer/core/page/scrolling/README.md b/third_party/blink/renderer/core/page/scrolling/README.md
index b7c4e88..2a5da0f4 100644
--- a/third_party/blink/renderer/core/page/scrolling/README.md
+++ b/third_party/blink/renderer/core/page/scrolling/README.md
@@ -1,6 +1,6 @@
 # Scrolling
 
-The `Source/core/page/scrolling` directory contains utilities and classes for
+The `renderer/core/page/scrolling` directory contains utilities and classes for
 scrolling that don't belong anywhere else. For example, the majority of
 document.rootScroller's implementation as well as overscroll and some scroll
 customization types live here.
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.h b/third_party/blink/renderer/core/paint/paint_invalidator.h
index 03eeb5c..e0adb49 100644
--- a/third_party/blink/renderer/core/paint/paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/paint_invalidator.h
@@ -116,10 +116,9 @@
   const LayoutBoxModelObject* directly_composited_container = nullptr;
 
   // The current directly composited container for stacked contents (stacking
-  // contexts or positioned objects).  It is the nearest ancestor composited
-  // object which establishes a stacking context.  See
-  // Source/core/paint/README.md ### PaintInvalidationState for details on how
-  // stacked contents' paint invalidation containers differ.
+  // contexts or positioned objects). It is the nearest ancestor composited
+  // object which establishes a stacking context. For more information, see:
+  // https://chromium.googlesource.com/chromium/src.git/+/master/third_party/blink/renderer/core/paint/README.md#Stacked-elements-and-stacking-contexts
   const LayoutBoxModelObject*
       directly_composited_container_for_stacked_contents = nullptr;
 
diff --git a/third_party/blink/renderer/core/script/html_parser_script_runner.cc b/third_party/blink/renderer/core/script/html_parser_script_runner.cc
index 3d974fa..9d2e2ddc 100644
--- a/third_party/blink/renderer/core/script/html_parser_script_runner.cc
+++ b/third_party/blink/renderer/core/script/html_parser_script_runner.cc
@@ -311,8 +311,7 @@
   document_->GetTaskRunner(TaskType::kInternalContinueScriptLoading)
       ->PostTask(FROM_HERE,
                  WTF::Bind(&HTMLParserScriptRunnerHost::NotifyScriptLoaded,
-                           WrapPersistent(host_.Get()),
-                           WrapPersistent(pending_script)));
+                           WrapPersistent(host_.Get())));
 }
 
 // <specdef href="https://html.spec.whatwg.org/C/#scriptEndTag">
@@ -380,11 +379,20 @@
   // that is blocking scripts and the script's "ready to be parser-executed"
   // flag is set.</spec>
   //
-  // These conditions correspond to isParserBlockingScriptReady() and
-  // if it is false, executeParsingBlockingScripts() will be called later
-  // when isParserBlockingScriptReady() becomes true:
-  // (1) from HTMLParserScriptRunner::executeScriptsWaitingForResources(), or
-  // (2) from HTMLParserScriptRunner::executeScriptsWaitingForLoad().
+  // These conditions correspond to IsParserBlockingScriptReady().
+  // If it is false at the time of #prepare-a-script,
+  // ExecuteParsingBlockingScripts() will be called later
+  // when IsParserBlockingScriptReady() might become true:
+  // - Called from HTMLParserScriptRunner::ExecuteScriptsWaitingForResources()
+  //   when the parser's Document has no style sheet that is blocking scripts,
+  // - Called from HTMLParserScriptRunner::ExecuteScriptsWaitingForLoad()
+  //   when the script's "ready to be parser-executed" flag is set, or
+  // - Other cases where any of the conditions isn't met or even when there are
+  //   no longer parser blocking scripts at all.
+  //   (For example, see the comment in ExecuteScriptsWaitingForLoad())
+  //
+  // Because we check the conditions below and do nothing if the conditions
+  // aren't met, it's safe to have extra ExecuteParsingBlockingScripts() calls.
   while (HasParserBlockingScript() && IsParserBlockingScriptReady()) {
     DCHECK(document_);
     DCHECK(!IsExecutingScript());
@@ -406,13 +414,17 @@
   }
 }
 
-void HTMLParserScriptRunner::ExecuteScriptsWaitingForLoad(
-    PendingScript* pending_script) {
+void HTMLParserScriptRunner::ExecuteScriptsWaitingForLoad() {
+  // Note(https://crbug.com/1093051): ExecuteScriptsWaitingForLoad() is
+  // triggered asynchronously from PendingScriptFinished(pending_script), but
+  // the |pending_script| might be no longer the ParserBlockginScript() here,
+  // because it might have been evaluated or disposed after
+  // PendingScriptFinished() before ExecuteScriptsWaitingForLoad(). Anyway we
+  // call ExecuteParsingBlockingScripts(), because necessary conditions for
+  // evaluation are checked safely there.
+
   TRACE_EVENT0("blink", "HTMLParserScriptRunner::executeScriptsWaitingForLoad");
   DCHECK(!IsExecutingScript());
-  DCHECK(HasParserBlockingScript());
-  DCHECK_EQ(pending_script, ParserBlockingScript());
-  DCHECK(ParserBlockingScript()->IsReady());
   ExecuteParsingBlockingScripts();
 }
 
diff --git a/third_party/blink/renderer/core/script/html_parser_script_runner.h b/third_party/blink/renderer/core/script/html_parser_script_runner.h
index b12d7cec..02877d1 100644
--- a/third_party/blink/renderer/core/script/html_parser_script_runner.h
+++ b/third_party/blink/renderer/core/script/html_parser_script_runner.h
@@ -86,7 +86,7 @@
 
   // Invoked when the parsing-blocking script resource has loaded, to execute
   // parsing-blocking scripts.
-  void ExecuteScriptsWaitingForLoad(PendingScript*);
+  void ExecuteScriptsWaitingForLoad();
 
   // Invoked when all script-blocking resources (e.g., stylesheets) have loaded,
   // to execute parsing-blocking scripts.
diff --git a/third_party/blink/renderer/core/script/html_parser_script_runner_host.h b/third_party/blink/renderer/core/script/html_parser_script_runner_host.h
index 90d1e97..d098672 100644
--- a/third_party/blink/renderer/core/script/html_parser_script_runner_host.h
+++ b/third_party/blink/renderer/core/script/html_parser_script_runner_host.h
@@ -32,14 +32,13 @@
 namespace blink {
 
 class HTMLInputStream;
-class PendingScript;
 
 class CORE_EXPORT HTMLParserScriptRunnerHost : public GarbageCollectedMixin {
  public:
   virtual ~HTMLParserScriptRunnerHost() = default;
   void Trace(Visitor* visitor) const override {}
 
-  virtual void NotifyScriptLoaded(PendingScript*) = 0;
+  virtual void NotifyScriptLoaded() = 0;
   virtual HTMLInputStream& InputStream() = 0;
 
   virtual bool HasPreloadScanner() const = 0;
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 1cb7f1c..bfb8604 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -608,14 +608,6 @@
     diff.SetNeedsPaintInvalidation();
   }
 
-  if ((!diff.NeedsCollectInlines() || !diff.NeedsFullLayout() ||
-       !diff.NeedsPaintInvalidation()) &&
-      DiffNeedsCollectInlinesAndFullLayoutAndPaintInvalidation(*this, other)) {
-    diff.SetNeedsCollectInlines();
-    diff.SetNeedsFullLayout();
-    diff.SetNeedsPaintInvalidation();
-  }
-
   if ((!diff.NeedsFullLayout() || !diff.NeedsPaintInvalidation()) &&
       DiffNeedsFullLayoutAndPaintInvalidation(other)) {
     diff.SetNeedsFullLayout();
diff --git a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5 b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
index 73f43d6d..caff873 100644
--- a/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
+++ b/third_party/blink/renderer/core/style/computed_style_diff_functions.json5
@@ -59,11 +59,6 @@
             method: "WhiteSpace()",
             field_dependencies: ["white-space"]
           },
-        ]
-    },
-    {
-        name: "DiffNeedsCollectInlinesAndFullLayoutAndPaintInvalidation",
-        methods_to_diff: [
           {
             method: "Direction()",
             field_dependencies: ["direction"]
diff --git a/third_party/blink/renderer/core/style/style_difference.cc b/third_party/blink/renderer/core/style/style_difference.cc
index 04f0d300..66c835a 100644
--- a/third_party/blink/renderer/core/style/style_difference.cc
+++ b/third_party/blink/renderer/core/style/style_difference.cc
@@ -26,7 +26,6 @@
       break;
   }
 
-  out << ", collectInlines=" << diff.needs_collect_inlines_;
   out << ", reshape=" << diff.needs_reshape_;
   out << ", paintInvalidation=" << diff.needs_paint_invalidation_;
   out << ", recomputeVisualOverflow=" << diff.recompute_visual_overflow_;
diff --git a/third_party/blink/renderer/core/style/style_difference.h b/third_party/blink/renderer/core/style/style_difference.h
index 14e468cf..51ddf4f 100644
--- a/third_party/blink/renderer/core/style/style_difference.h
+++ b/third_party/blink/renderer/core/style/style_difference.h
@@ -38,7 +38,6 @@
   StyleDifference()
       : needs_paint_invalidation_(false),
         layout_type_(kNoLayout),
-        needs_collect_inlines_(false),
         needs_reshape_(false),
         recompute_visual_overflow_(false),
         visual_rect_update_(false),
@@ -49,7 +48,6 @@
   void Merge(StyleDifference other) {
     needs_paint_invalidation_ |= other.needs_paint_invalidation_;
     layout_type_ = std::max(layout_type_, other.layout_type_);
-    needs_collect_inlines_ |= other.needs_collect_inlines_;
     needs_reshape_ |= other.needs_reshape_;
     recompute_visual_overflow_ |= other.recompute_visual_overflow_;
     visual_rect_update_ |= other.visual_rect_update_;
@@ -60,8 +58,7 @@
   }
 
   bool HasDifference() const {
-    return needs_paint_invalidation_ || layout_type_ ||
-           needs_collect_inlines_ || needs_reshape_ ||
+    return needs_paint_invalidation_ || layout_type_ || needs_reshape_ ||
            property_specific_differences_ || recompute_visual_overflow_ ||
            visual_rect_update_ || scroll_anchor_disabling_property_changed_ ||
            compositing_reasons_changed_;
@@ -91,9 +88,6 @@
   bool NeedsFullLayout() const { return layout_type_ == kFullLayout; }
   void SetNeedsFullLayout() { layout_type_ = kFullLayout; }
 
-  bool NeedsCollectInlines() const { return needs_collect_inlines_; }
-  void SetNeedsCollectInlines() { needs_collect_inlines_ = true; }
-
   bool NeedsReshape() const { return needs_reshape_; }
   void SetNeedsReshape() { needs_reshape_ = true; }
 
@@ -190,7 +184,6 @@
 
   enum LayoutType { kNoLayout = 0, kPositionedMovement, kFullLayout };
   unsigned layout_type_ : 2;
-  unsigned needs_collect_inlines_ : 1;
   unsigned needs_reshape_ : 1;
   unsigned recompute_visual_overflow_ : 1;
   unsigned visual_rect_update_ : 1;
diff --git a/third_party/blink/renderer/core/style/style_difference_test.cc b/third_party/blink/renderer/core/style/style_difference_test.cc
index d1e2806..4d5a9d558 100644
--- a/third_party/blink/renderer/core/style/style_difference_test.cc
+++ b/third_party/blink/renderer/core/style/style_difference_test.cc
@@ -15,7 +15,7 @@
   string_stream << diff;
   EXPECT_EQ(
       "StyleDifference{layoutType=NoLayout, "
-      "collectInlines=0, reshape=0, "
+      "reshape=0, "
       "paintInvalidation=0, recomputeVisualOverflow=0, "
       "visualRectUpdate=0, propertySpecificDifferences=, "
       "scrollAnchorDisablingPropertyChanged=0}",
@@ -28,7 +28,6 @@
   diff.SetNeedsPaintInvalidation();
   diff.SetNeedsPositionedMovementLayout();
   diff.SetNeedsReshape();
-  diff.SetNeedsCollectInlines();
   diff.SetNeedsRecomputeVisualOverflow();
   diff.SetNeedsVisualRectUpdate();
   diff.SetTransformChanged();
@@ -36,7 +35,7 @@
   string_stream << diff;
   EXPECT_EQ(
       "StyleDifference{layoutType=PositionedMovement, "
-      "collectInlines=1, reshape=1, paintInvalidation=1, "
+      "reshape=1, paintInvalidation=1, "
       "recomputeVisualOverflow=1, visualRectUpdate=1, "
       "propertySpecificDifferences=TransformChanged, "
       "scrollAnchorDisablingPropertyChanged=1}",
@@ -57,7 +56,7 @@
   string_stream << diff;
   EXPECT_EQ(
       "StyleDifference{layoutType=NoLayout, "
-      "collectInlines=0, reshape=0, paintInvalidation=0, "
+      "reshape=0, paintInvalidation=0, "
       "recomputeVisualOverflow=0, visualRectUpdate=0, "
       "propertySpecificDifferences=TransformChanged|OpacityChanged|"
       "ZIndexChanged|FilterChanged|BackdropFilterChanged|CSSClipChanged|"
diff --git a/third_party/blink/renderer/core/url/dom_url_utils.cc b/third_party/blink/renderer/core/url/dom_url_utils.cc
index 50dcb710..694b341 100644
--- a/third_party/blink/renderer/core/url/dom_url_utils.cc
+++ b/third_party/blink/renderer/core/url/dom_url_utils.cc
@@ -141,10 +141,13 @@
   // FIXME: have KURL handle the clearing of the fragment component
   // on the same input.
   if (value[0] == '#')
-    kurl.SetFragmentIdentifier(value.length() == 1 ? String()
-                                                   : value.Substring(1));
-  else
-    kurl.SetFragmentIdentifier(value.IsEmpty() ? String() : value);
+    kurl.SetFragmentIdentifier(value.Substring(1));
+  else {
+    if (value.IsEmpty())
+      kurl.RemoveFragmentIdentifier();
+    else
+      kurl.SetFragmentIdentifier(value);
+  }
 
   SetURL(kurl);
 }
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
index ab6ea03..080b5a2 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.cc
@@ -157,7 +157,6 @@
 
   void Write(const String& sanitized_html, KURL url) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    String plain_text = "";
     system_clipboard()->WriteHTML(sanitized_html, url);
     promise_->CompleteWriteRepresentation();
   }
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
index 04e5f7f..ef5c3aba 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_test.cc
@@ -233,7 +233,7 @@
   void Pause() override;
 
   // Methods for test use
-  void QueueFrames(const std::vector<int>& timestamps_or_frame_type,
+  void QueueFrames(const Vector<int>& timestamps_or_frame_type,
                    bool opaque_frame = true,
                    bool odd_size_frame = false,
                    int double_size_index = -1,
@@ -316,7 +316,7 @@
 }
 
 void MockMediaStreamVideoRenderer::QueueFrames(
-    const std::vector<int>& timestamp_or_frame_type,
+    const Vector<int>& timestamp_or_frame_type,
     bool opaque_frame,
     bool odd_size_frame,
     int double_size_index,
@@ -880,9 +880,8 @@
 
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(true);
 
-  int tokens[] = {0,   33,  66,  100, 133, 166, 200, 233, 266, 300,
-                  333, 366, 400, 433, 466, 500, 533, 566, 600};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0, 33, 66, 100, 133, 166, 200, 233, 266, 300, 333,
+                          366, 400, 433, 466, 500, 533, 566, 600});
   provider->QueueFrames(timestamps);
 
   if (enable_surface_layer_for_video_) {
@@ -921,9 +920,9 @@
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(false);
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0,   33,  66,  100, 133, kTestBrake, 166, 200, 233, 266,
-                  300, 333, 366, 400, 433, 466,        500, 533, 566, 600};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0,   33,  66,  100, 133, kTestBrake, 166,
+                          200, 233, 266, 300, 333, 366,        400,
+                          433, 466, 500, 533, 566, 600});
   provider->QueueFrames(timestamps, opaque_frame, odd_size_frame);
 
   if (enable_surface_layer_for_video_) {
@@ -971,10 +970,9 @@
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(false);
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0,   33,         66,  100, 133, kTestBrake, 166,
-                  200, 233,        266, 300, 333, 366,        400,
-                  433, kTestBrake, 466, 500, 533, 566,        600};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0,   33,         66,  100, 133, kTestBrake, 166,
+                          200, 233,        266, 300, 333, 366,        400,
+                          433, kTestBrake, 466, 500, 533, 566,        600});
   provider->QueueFrames(timestamps, opaque_frame, odd_size_frame);
 
   if (enable_surface_layer_for_video_) {
@@ -1039,8 +1037,7 @@
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(true);
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0, kTestBrake};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0, kTestBrake});
   provider->QueueFrames(timestamps, false, false, 17, media::VIDEO_ROTATION_90);
   if (enable_surface_layer_for_video_) {
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
@@ -1067,8 +1064,7 @@
   EXPECT_EQ(kStandardWidth, natural_size.height());
 
   // Change rotation.
-  tokens[0] = 33;
-  timestamps = std::vector<int>(tokens, tokens + base::size(tokens));
+  timestamps = Vector<int>({33, kTestBrake});
   provider->QueueFrames(timestamps, false, false, 17, media::VIDEO_ROTATION_0);
   if (enable_surface_layer_for_video_) {
     EXPECT_CALL(*submitter_ptr_, SetRotation(media::VIDEO_ROTATION_0));
@@ -1085,8 +1081,7 @@
   EXPECT_EQ(kStandardWidth, natural_size.width());
 
   // Change rotation again.
-  tokens[0] = 66;
-  timestamps = std::vector<int>(tokens, tokens + base::size(tokens));
+  timestamps = Vector<int>({66, kTestBrake});
   provider->QueueFrames(timestamps, false, false, 17, media::VIDEO_ROTATION_90);
   if (enable_surface_layer_for_video_) {
     EXPECT_CALL(*submitter_ptr_, SetRotation(media::VIDEO_ROTATION_90));
@@ -1119,8 +1114,7 @@
 
   // Push one opaque frame.
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0, kTestBrake};
-  std::vector<int> timestamps(tokens, tokens + base::size(tokens));
+  Vector<int> timestamps({0, kTestBrake});
   provider->QueueFrames(timestamps, true);
 
   if (enable_surface_layer_for_video_) {
@@ -1147,8 +1141,7 @@
   }
 
   // Push one transparent frame.
-  tokens[0] = 33;
-  timestamps = std::vector<int>(tokens, tokens + base::size(tokens));
+  timestamps = Vector<int>({33, kTestBrake});
   provider->QueueFrames(timestamps, false);
   if (enable_surface_layer_for_video_) {
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
@@ -1161,8 +1154,7 @@
   }
 
   // Push another transparent frame.
-  tokens[0] = 66;
-  timestamps = std::vector<int>(tokens, tokens + base::size(tokens));
+  timestamps = Vector<int>({66, kTestBrake});
   provider->QueueFrames(timestamps, true);
   if (enable_surface_layer_for_video_) {
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(true));
@@ -1192,10 +1184,9 @@
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(true);
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0,   33,         66,  100, 133, kTestBrake, 166,
-                  200, 233,        266, 300, 333, 366,        400,
-                  433, kTestBrake, 466, 500, 533, 566,        600};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0,   33,         66,  100, 133, kTestBrake, 166,
+                          200, 233,        266, 300, 333, 366,        400,
+                          433, kTestBrake, 466, 500, 533, 566,        600});
   provider->QueueFrames(timestamps);
 
   if (enable_surface_layer_for_video_) {
@@ -1252,9 +1243,8 @@
   InitializeWebMediaPlayerMS();
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(true);
 
-  int tokens[] = {0,   33,  66,  100, 133, 166, 200, 233, 266, 300,
-                  333, 366, 400, 433, 466, 500, 533, 566, 600};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0, 33, 66, 100, 133, 166, 200, 233, 266, 300, 333,
+                          366, 400, 433, 466, 500, 533, 566, 600});
   provider->QueueFrames(timestamps, false, false, 7);
 
   if (enable_surface_layer_for_video_) {
@@ -1290,8 +1280,7 @@
   SetGpuMemoryBufferVideoForTesting();
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  static int tokens[] = {0, kTestBrake};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0, kTestBrake});
   provider->QueueFrames(timestamps);
   message_loop_controller_.RunAndWaitForStatus(
       media::PipelineStatus::PIPELINE_OK);
@@ -1383,8 +1372,7 @@
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(true);
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0, 33, kTestBrake, 66, 100, 133, 166};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0, 33, kTestBrake, 66, 100, 133, 166});
   provider->QueueFrames(timestamps);
 
   // Verify a basic call to RAF.
@@ -1439,8 +1427,7 @@
   MockMediaStreamVideoRenderer* provider = LoadAndGetFrameProvider(true);
 
   const int kTestBrake = static_cast<int>(FrameType::TEST_BRAKE);
-  int tokens[] = {0, kTestBrake, 33, kTestBrake, 66, kTestBrake};
-  std::vector<int> timestamps(tokens, tokens + sizeof(tokens) / sizeof(int));
+  Vector<int> timestamps({0, kTestBrake, 33, kTestBrake, 66, kTestBrake});
   provider->QueueFrames(timestamps);
 
   // Chain calls to video.rAF.
diff --git a/third_party/blink/renderer/modules/push_messaging/BUILD.gn b/third_party/blink/renderer/modules/push_messaging/BUILD.gn
index 4bc6d94..bc91b77 100644
--- a/third_party/blink/renderer/modules/push_messaging/BUILD.gn
+++ b/third_party/blink/renderer/modules/push_messaging/BUILD.gn
@@ -18,8 +18,6 @@
     "push_messaging_bridge.h",
     "push_messaging_client.cc",
     "push_messaging_client.h",
-    "push_messaging_type_converter.cc",
-    "push_messaging_type_converter.h",
     "push_messaging_utils.cc",
     "push_messaging_utils.h",
     "push_provider.cc",
@@ -32,6 +30,8 @@
     "push_subscription_change_event.h",
     "push_subscription_options.cc",
     "push_subscription_options.h",
+    "push_type_converter.cc",
+    "push_type_converter.h",
     "service_worker_global_scope_push.h",
     "service_worker_registration_push.cc",
     "service_worker_registration_push.h",
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc b/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
index 30adf9d..8371eec 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
@@ -17,10 +17,10 @@
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/modules/manifest/manifest_manager.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_error.h"
-#include "third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
+#include "third_party/blink/renderer/modules/push_messaging/push_type_converter.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
diff --git a/third_party/blink/renderer/modules/push_messaging/push_provider.cc b/third_party/blink/renderer/modules/push_messaging/push_provider.cc
index 61b924d..5e5dc5f 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_provider.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_provider.cc
@@ -10,10 +10,10 @@
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_error.h"
-#include "third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
+#include "third_party/blink/renderer/modules/push_messaging/push_type_converter.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
index d67b744..b068cb8 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
@@ -49,6 +49,19 @@
   return value;
 }
 
+// Converts a {base::Optional<base::Time>} into a
+// {base::Optional<base::DOMTimeStamp>} object.
+// base::Time is in milliseconds from Windows epoch (1601-01-01 00:00:00 UTC)
+// while blink::DOMTimeStamp is in milliseconds from UNIX epoch (1970-01-01
+// 00:00:00 UTC)
+base::Optional<blink::DOMTimeStamp> ToDOMTimeStamp(
+    const base::Optional<base::Time>& time) {
+  if (time)
+    return ConvertSecondsToDOMTimeStamp(time->ToDoubleT());
+
+  return base::nullopt;
+}
+
 }  // namespace
 
 // static
@@ -58,7 +71,8 @@
   return MakeGarbageCollected<PushSubscription>(
       subscription->endpoint, subscription->options->user_visible_only,
       subscription->options->application_server_key, subscription->p256dh,
-      subscription->auth, service_worker_registration);
+      subscription->auth, ToDOMTimeStamp(subscription->expirationTime),
+      service_worker_registration);
 }
 
 PushSubscription::PushSubscription(
@@ -67,8 +81,8 @@
     const WTF::Vector<uint8_t>& application_server_key,
     const WTF::Vector<unsigned char>& p256dh,
     const WTF::Vector<unsigned char>& auth,
-    ServiceWorkerRegistration* service_worker_registration,
-    const base::Optional<DOMTimeStamp> expiration_time)
+    const base::Optional<DOMTimeStamp>& expiration_time,
+    ServiceWorkerRegistration* service_worker_registration)
     : endpoint_(endpoint),
       options_(MakeGarbageCollected<PushSubscriptionOptions>(
           user_visible_only,
@@ -77,8 +91,8 @@
                                      SafeCast<unsigned>(p256dh.size()))),
       auth_(
           DOMArrayBuffer::Create(auth.data(), SafeCast<unsigned>(auth.size()))),
-      service_worker_registration_(service_worker_registration),
-      expiration_time_(expiration_time) {}
+      expiration_time_(expiration_time),
+      service_worker_registration_(service_worker_registration) {}
 
 PushSubscription::~PushSubscription() = default;
 
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription.h b/third_party/blink/renderer/modules/push_messaging/push_subscription.h
index d4b77b00..fb0d2374 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription.h
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription.h
@@ -34,14 +34,13 @@
       mojom::blink::PushSubscriptionPtr subscription,
       ServiceWorkerRegistration* service_worker_registration);
 
-  PushSubscription(
-      const KURL& endpoint,
-      bool user_visible_only,
-      const WTF::Vector<uint8_t>& application_server_key,
-      const WTF::Vector<unsigned char>& p256dh,
-      const WTF::Vector<unsigned char>& auth,
-      ServiceWorkerRegistration* service_worker_registration,
-      const base::Optional<DOMTimeStamp> expiration_time = base::nullopt);
+  PushSubscription(const KURL& endpoint,
+                   bool user_visible_only,
+                   const WTF::Vector<uint8_t>& application_server_key,
+                   const WTF::Vector<unsigned char>& p256dh,
+                   const WTF::Vector<unsigned char>& auth,
+                   const base::Optional<DOMTimeStamp>& expiration_time,
+                   ServiceWorkerRegistration* service_worker_registration);
 
   ~PushSubscription() override;
 
@@ -68,9 +67,9 @@
   Member<DOMArrayBuffer> p256dh_;
   Member<DOMArrayBuffer> auth_;
 
-  Member<ServiceWorkerRegistration> service_worker_registration_;
-
   base::Optional<DOMTimeStamp> expiration_time_;
+
+  Member<ServiceWorkerRegistration> service_worker_registration_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription_test.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription_test.cc
index 367a74af..4ac9882 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription_test.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription_test.cc
@@ -37,6 +37,7 @@
   PushSubscription subscription(
       KURL() /* endpoint */, true /* user_visible_only */,
       Vector<uint8_t>() /* application_server_key */, kP256DH, kAuthSecret,
+      base::nullopt /* expiration_time */,
       nullptr /* service_worker_registration */);
 
   ScriptValue json_object =
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.cc b/third_party/blink/renderer/modules/push_messaging/push_type_converter.cc
similarity index 97%
rename from third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.cc
rename to third_party/blink/renderer/modules/push_messaging/push_type_converter.cc
index 21c85a9e..4bbd8bc 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_type_converter.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.h"
+#include "third_party/blink/renderer/modules/push_messaging/push_type_converter.h"
 
 #include "base/numerics/safe_conversions.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.h b/third_party/blink/renderer/modules/push_messaging/push_type_converter.h
similarity index 81%
rename from third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.h
rename to third_party/blink/renderer/modules/push_messaging/push_type_converter.h
index f155f25..6fd6e12 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converter.h
+++ b/third_party/blink/renderer/modules/push_messaging/push_type_converter.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_TYPE_CONVERTER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_TYPE_CONVERTER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_TYPE_CONVERTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_TYPE_CONVERTER_H_
 
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-blink.h"
@@ -11,7 +11,6 @@
 namespace blink {
 
 class PushSubscriptionOptions;
-class PushSubscription;
 
 }  // namespace blink
 
@@ -31,4 +30,4 @@
 
 }  // namespace mojo
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_TYPE_CONVERTER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_TYPE_CONVERTER_H_
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.cc b/third_party/blink/renderer/modules/webshare/navigator_share.cc
index 520b1a5..edae7ee9 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.cc
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_share_data.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -202,6 +203,13 @@
     return ScriptPromise();
   }
 
+  if (!ExecutionContext::From(script_state)
+           ->IsFeatureEnabled(mojom::blink::FeaturePolicyFeature::kWebShare)) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotAllowedError,
+                                      "Permission denied");
+    return ScriptPromise();
+  }
+
   LocalDOMWindow* window = LocalDOMWindow::From(script_state);
   if (!LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
     exception_state.ThrowDOMException(
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 984e5cc..5f85046 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1469,6 +1469,12 @@
     "widget/compositing/layer_tree_view.cc",
     "widget/compositing/layer_tree_view.h",
     "widget/compositing/layer_tree_view_delegate.h",
+    "widget/compositing/queue_report_time_swap_promise.cc",
+    "widget/compositing/queue_report_time_swap_promise.h",
+    "widget/compositing/widget_compositor.cc",
+    "widget/compositing/widget_compositor.h",
+    "widget/compositing/widget_swap_queue.cc",
+    "widget/compositing/widget_swap_queue.h",
     "widget/frame_widget.cc",
     "widget/frame_widget.h",
     "widget/input/compositor_thread_event_queue.cc",
@@ -2046,6 +2052,7 @@
     "widget/compositing/layer_tree_settings_unittest.cc",
     "widget/compositing/layer_tree_view_unittest.cc",
     "widget/compositing/test/stub_layer_tree_view_delegate.h",
+    "widget/compositing/widget_compositor_unittest.cc",
     "widget/input/input_event_prediction_unittest.cc",
     "widget/input/input_handler_proxy_unittest.cc",
     "widget/input/input_scroll_elasticity_controller_unittest.cc",
diff --git a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc
new file mode 100644
index 0000000..14f77cc
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc
@@ -0,0 +1,55 @@
+// 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 "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h"
+
+namespace blink {
+
+QueueReportTimeSwapPromise::QueueReportTimeSwapPromise(
+    int source_frame_number,
+    DrainCallback drain_callback,
+    base::OnceClosure swap_callback,
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner)
+    : source_frame_number_(source_frame_number),
+      drain_callback_(std::move(drain_callback)),
+      swap_callback_(std::move(swap_callback)),
+      compositor_task_runner_(std::move(compositor_task_runner)) {}
+
+QueueReportTimeSwapPromise::~QueueReportTimeSwapPromise() {
+  if (compositor_task_runner_ && (drain_callback_ || swap_callback_)) {
+    DCHECK(!compositor_task_runner_->BelongsToCurrentThread());
+    compositor_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce([](DrainCallback, base::OnceClosure) {},
+                       std::move(drain_callback_), std::move(swap_callback_)));
+  }
+}
+
+void QueueReportTimeSwapPromise::WillSwap(
+    viz::CompositorFrameMetadata* metadata) {
+  DCHECK_GT(metadata->frame_token, 0u);
+}
+
+void QueueReportTimeSwapPromise::DidSwap() {
+  if (swap_callback_)
+    std::move(swap_callback_).Run();
+}
+
+cc::SwapPromise::DidNotSwapAction QueueReportTimeSwapPromise::DidNotSwap(
+    DidNotSwapReason reason) {
+  if (reason == cc::SwapPromise::SWAP_FAILS ||
+      reason == cc::SwapPromise::COMMIT_NO_UPDATE) {
+    if (drain_callback_)
+      std::move(drain_callback_).Run(source_frame_number_);
+    if (swap_callback_)
+      std::move(swap_callback_).Run();
+  }
+  return DidNotSwapAction::BREAK_PROMISE;
+}
+
+void QueueReportTimeSwapPromise::DidActivate() {
+  if (drain_callback_)
+    std::move(drain_callback_).Run(source_frame_number_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h
new file mode 100644
index 0000000..b0ef8ad
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h
@@ -0,0 +1,48 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/trees/swap_promise.h"
+
+namespace blink {
+
+// This class invokes DrainCallback to drain queued callbacks for frame numbers
+// lower or equal to |source_frame_number| when the commit results in a
+// successful activation of the pending layer tree in swap promise.
+//
+// This class doesn't have the reporting callback of the swap time.
+class QueueReportTimeSwapPromise : public cc::SwapPromise {
+ public:
+  using DrainCallback = base::OnceCallback<void(int)>;
+  QueueReportTimeSwapPromise(
+      int source_frame_number,
+      DrainCallback drain_callback,
+      base::OnceClosure swap_callback,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner);
+  ~QueueReportTimeSwapPromise() override;
+  QueueReportTimeSwapPromise(const QueueReportTimeSwapPromise&) = delete;
+  QueueReportTimeSwapPromise& operator=(const QueueReportTimeSwapPromise&) =
+      delete;
+
+  void WillSwap(viz::CompositorFrameMetadata* metadata) override;
+  void DidSwap() override;
+  cc::SwapPromise::DidNotSwapAction DidNotSwap(
+      DidNotSwapReason reason) override;
+  void DidActivate() override;
+  int64_t TraceId() const override { return 0; }
+
+ private:
+  int source_frame_number_;
+  DrainCallback drain_callback_;
+  base::OnceClosure swap_callback_;
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc
new file mode 100644
index 0000000..88d33c7
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc
@@ -0,0 +1,126 @@
+// 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 "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
+
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h"
+#include "third_party/blink/renderer/platform/widget/widget_base.h"
+
+namespace blink {
+
+WidgetCompositor::WidgetCompositor(
+    base::WeakPtr<WidgetBase> widget_base,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver)
+    : widget_base_(widget_base),
+      main_task_runner_(std::move(main_task_runner)),
+      compositor_task_runner_(std::move(compositor_task_runner)),
+      swap_queue_(std::make_unique<WidgetSwapQueue>()) {
+  if (!compositor_task_runner_) {
+    BindOnThread(std::move(receiver));
+  } else {
+    compositor_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&WidgetCompositor::BindOnThread,
+                                  base::Unretained(this), std::move(receiver)));
+  }
+}
+
+void WidgetCompositor::Shutdown() {
+  if (!compositor_task_runner_) {
+    ResetOnThread();
+  } else {
+    compositor_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&WidgetCompositor::ResetOnThread,
+                                  scoped_refptr<WidgetCompositor>(this)));
+  }
+}
+
+void WidgetCompositor::BindOnThread(
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
+  DCHECK(CalledOnValidCompositorThread());
+  receiver_.Bind(std::move(receiver), compositor_task_runner_);
+}
+
+void WidgetCompositor::ResetOnThread() {
+  DCHECK(CalledOnValidCompositorThread());
+  receiver_.reset();
+}
+
+void WidgetCompositor::VisualStateRequest(VisualStateRequestCallback callback) {
+  DCHECK(CalledOnValidCompositorThread());
+
+  auto drain_callback =
+      WTF::Bind(&WidgetCompositor::DrainQueue, base::RetainedRef(this));
+  auto swap_callback = WTF::Bind(&WidgetCompositor::VisualStateResponse,
+                                 base::RetainedRef(this));
+  if (!compositor_task_runner_) {
+    CreateQueueSwapPromise(std::move(drain_callback), std::move(swap_callback),
+                           std::move(callback));
+  } else {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WidgetCompositor::CreateQueueSwapPromise,
+                       base::RetainedRef(this), std::move(drain_callback),
+                       std::move(swap_callback), std::move(callback)));
+  }
+}
+
+cc::LayerTreeHost* WidgetCompositor::LayerTreeHost() const {
+  return widget_base_->LayerTreeHost();
+}
+
+void WidgetCompositor::CreateQueueSwapPromise(
+    base::OnceCallback<void(int)> drain_callback,
+    base::OnceClosure swap_callback,
+    VisualStateRequestCallback callback) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+  if (!widget_base_)
+    return;
+
+  bool first_message_for_frame = false;
+  int source_frame_number = LayerTreeHost()->SourceFrameNumber();
+  swap_queue_->Queue(source_frame_number, std::move(callback),
+                     &first_message_for_frame);
+  if (first_message_for_frame) {
+    LayerTreeHost()->QueueSwapPromise(
+        std::make_unique<QueueReportTimeSwapPromise>(
+            source_frame_number, std::move(drain_callback),
+            std::move(swap_callback), compositor_task_runner_));
+    // Request a main frame if one is not already in progress. This might either
+    // A) request a commit ahead of time or B) request a commit which is not
+    // needed because there are not pending updates. If B) then the frame will
+    // be aborted early and the swap promises will be broken (see
+    // EarlyOut_NoUpdates).
+    LayerTreeHost()->SetNeedsAnimateIfNotInsideMainFrame();
+  } else if (compositor_task_runner_) {
+    // Delete callbacks on the compositor thread.
+    compositor_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce([](base::OnceCallback<void(int)>, base::OnceClosure) {},
+                       std::move(drain_callback), std::move(swap_callback)));
+  }
+}
+
+void WidgetCompositor::VisualStateResponse() {
+  DCHECK(CalledOnValidCompositorThread());
+  Vector<VisualStateRequestCallback> callbacks;
+  swap_queue_->GetCallbacks(&callbacks);
+  for (auto& callback : callbacks)
+    std::move(callback).Run();
+}
+
+void WidgetCompositor::DrainQueue(int source_frame_number) {
+  DCHECK(CalledOnValidCompositorThread());
+  swap_queue_->Drain(source_frame_number);
+}
+
+bool WidgetCompositor::CalledOnValidCompositorThread() {
+  return !compositor_task_runner_ ||
+         compositor_task_runner_->BelongsToCurrentThread();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h
new file mode 100644
index 0000000..dd8832f
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h
@@ -0,0 +1,63 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
+
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "third_party/blink/public/mojom/page/widget.mojom-blink.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h"
+
+namespace cc {
+class LayerTreeHost;
+}
+
+namespace blink {
+
+class WidgetBase;
+
+class PLATFORM_EXPORT WidgetCompositor
+    : public base::RefCountedThreadSafe<WidgetCompositor>,
+      public mojom::blink::WidgetCompositor {
+ public:
+  WidgetCompositor(
+      base::WeakPtr<WidgetBase> widget_base,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver);
+  ~WidgetCompositor() override = default;
+  WidgetCompositor(const WidgetCompositor&) = delete;
+  WidgetCompositor& operator=(const WidgetCompositor&) = delete;
+
+  void Shutdown();
+
+  // mojom::WidgetCompositor:
+  void VisualStateRequest(VisualStateRequestCallback callback) override;
+
+  virtual cc::LayerTreeHost* LayerTreeHost() const;
+
+ private:
+  void BindOnThread(
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver);
+  void ResetOnThread();
+  void CreateQueueSwapPromise(base::OnceCallback<void(int)> drain_callback,
+                              base::OnceClosure swap_callback,
+                              VisualStateRequestCallback callback);
+  void VisualStateResponse();
+  void DrainQueue(int source_frame_number);
+  bool CalledOnValidCompositorThread();
+
+  // Note that |widget_base_| is safe to be accessed on the main thread.
+  base::WeakPtr<WidgetBase> widget_base_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+  mojo::Receiver<mojom::blink::WidgetCompositor> receiver_{this};
+  std::unique_ptr<WidgetSwapQueue> swap_queue_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc
new file mode 100644
index 0000000..e3c44549
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc
@@ -0,0 +1,99 @@
+// 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.
+
+#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
+
+#include "base/test/task_environment.h"
+#include "cc/test/layer_tree_test.h"
+#include "cc/trees/layer_tree_host.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/renderer/platform/widget/widget_base.h"
+#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
+
+namespace blink {
+
+class StubWidgetBaseClient : public WidgetBaseClient {
+ public:
+  void BeginMainFrame(base::TimeTicks) override {}
+  void RecordTimeToFirstActivePaint(base::TimeDelta) override {}
+  void UpdateLifecycle(WebLifecycleUpdate, DocumentUpdateReason) override {}
+  void RequestNewLayerTreeFrameSink(LayerTreeFrameSinkCallback) override {}
+  WebInputEventResult DispatchBufferedTouchEvents() override {
+    return WebInputEventResult::kNotHandled;
+  }
+  WebInputEventResult HandleInputEvent(const WebCoalescedInputEvent&) override {
+    return WebInputEventResult::kNotHandled;
+  }
+  bool SupportsBufferedTouchEvents() override { return false; }
+  bool WillHandleGestureEvent(const WebGestureEvent&) override { return false; }
+  bool WillHandleMouseEvent(const WebMouseEvent&) override { return false; }
+  void ObserveGestureEventAndResult(const WebGestureEvent&,
+                                    const gfx::Vector2dF&,
+                                    const cc::OverscrollBehavior&,
+                                    bool) override {}
+  void FocusChanged(bool) override {}
+};
+
+class FakeWidgetCompositor : public WidgetCompositor {
+ public:
+  FakeWidgetCompositor(
+      cc::LayerTreeHost* layer_tree_host,
+      base::WeakPtr<WidgetBase> widget_base,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver)
+      : WidgetCompositor(widget_base,
+                         std::move(main_task_runner),
+                         std::move(compositor_task_runner),
+                         std::move(receiver)),
+        layer_tree_host_(layer_tree_host) {}
+
+  cc::LayerTreeHost* LayerTreeHost() const override { return layer_tree_host_; }
+
+  cc::LayerTreeHost* layer_tree_host_;
+};
+
+class WidgetCompositorTest : public cc::LayerTreeTest {
+ public:
+  using CompositorMode = cc::CompositorMode;
+
+  void BeginTest() override {
+    widget_base_ = std::make_unique<WidgetBase>(
+        &client_,
+        blink::CrossVariantMojoAssociatedRemote<
+            blink::mojom::WidgetHostInterfaceBase>(),
+        blink::CrossVariantMojoAssociatedReceiver<
+            blink::mojom::WidgetInterfaceBase>());
+
+    widget_compositor_ = base::MakeRefCounted<FakeWidgetCompositor>(
+        layer_tree_host(), widget_base_->GetWeakPtr(),
+        layer_tree_host()->GetTaskRunnerProvider()->MainThreadTaskRunner(),
+        layer_tree_host()->GetTaskRunnerProvider()->ImplThreadTaskRunner(),
+        remote_.BindNewPipeAndPassReceiver());
+
+    remote_->VisualStateRequest(base::BindOnce(
+        &WidgetCompositorTest::VisualStateResponse, base::Unretained(this)));
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void VisualStateResponse() {
+    is_callback_run_ = true;
+    widget_compositor_->Shutdown();
+    widget_compositor_ = nullptr;
+    EndTest();
+  }
+
+  void AfterTest() override { EXPECT_TRUE(is_callback_run_); }
+
+  mojo::Remote<mojom::blink::WidgetCompositor> remote_;
+  StubWidgetBaseClient client_;
+  std::unique_ptr<WidgetBase> widget_base_;
+  scoped_refptr<FakeWidgetCompositor> widget_compositor_;
+  bool is_callback_run_ = false;
+  base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(WidgetCompositorTest);
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc
new file mode 100644
index 0000000..bcb3794
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc
@@ -0,0 +1,37 @@
+// 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 "third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h"
+
+namespace blink {
+
+void WidgetSwapQueue::Queue(int source_frame_number,
+                            VisualStateRequestCallback callback,
+                            bool* is_first) {
+  base::AutoLock lock(lock_);
+  if (is_first)
+    *is_first = (queue_.count(source_frame_number) == 0);
+
+  queue_[source_frame_number].push_back(std::move(callback));
+}
+
+void WidgetSwapQueue::Drain(int source_frame_number) {
+  base::AutoLock lock(lock_);
+  auto end = queue_.upper_bound(source_frame_number);
+  for (auto i = queue_.begin(); i != end; i++) {
+    DCHECK(i->first <= source_frame_number);
+    std::move(i->second.begin(), i->second.end(),
+              std::back_inserter(next_callbacks_));
+  }
+  queue_.erase(queue_.begin(), end);
+}
+
+void WidgetSwapQueue::GetCallbacks(
+    Vector<VisualStateRequestCallback>* callbacks) {
+  std::move(next_callbacks_.begin(), next_callbacks_.end(),
+            std::back_inserter(*callbacks));
+  next_callbacks_.clear();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h
new file mode 100644
index 0000000..8f73484
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h
@@ -0,0 +1,61 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
+
+#include <map>
+#include "base/synchronization/lock.h"
+#include "third_party/blink/public/mojom/page/widget.mojom-blink.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Queue used to keep track of which VisualStateRequestCallback should be
+// invoked after a particular compositor frame swap. The callbacks are
+// guaranteed to be processed after the frame is processed, but there is no
+// guarantee that nothing else happens between processing the frame and
+// processing the callback.
+class WidgetSwapQueue {
+ public:
+  using VisualStateRequestCallback =
+      mojom::blink::WidgetCompositor::VisualStateRequestCallback;
+  WidgetSwapQueue() = default;
+  ~WidgetSwapQueue() = default;
+  WidgetSwapQueue(const WidgetSwapQueue&) = delete;
+  WidgetSwapQueue& operator=(const WidgetSwapQueue&) = delete;
+
+  // Returns true if there are no callback in the queue.
+  bool Empty() const { return queue_.empty(); }
+
+  // Queues the callback to be returned on a matching Drain call.
+  //
+  // |source_frame_number| - frame number to queue |callback| for.
+  // |callback| - callback to queue. The method takes ownership of |callback|.
+  // |is_first| - output parameter. Set to true if this was the first message
+  //              enqueued for the given source_frame_number.
+  void Queue(int source_frame_number,
+             VisualStateRequestCallback callback,
+             bool* is_first);
+
+  // The method will append cllbacks queued for frame numbers lower or equal to
+  // |source_frame_number|
+  //
+  // |source_frame_number| - swapped frame number.
+  void Drain(int source_frame_number);
+
+  // The method will clear |next_callbacks_| after copying to |callbacks|.
+  //
+  // |callbacks| - vector to store callbacks.
+  void GetCallbacks(Vector<VisualStateRequestCallback>* callbacks);
+
+ private:
+  base::Lock lock_;
+  std::map<int, Vector<VisualStateRequestCallback>> queue_;
+  Vector<VisualStateRequestCallback> next_callbacks_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
diff --git a/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc b/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
index 81fcef6..404e4b5 100644
--- a/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
@@ -138,7 +138,9 @@
 }
 
 void SynchronousCompositorProxy::WillSkipDraw() {
-  layer_tree_frame_sink_->WillSkipDraw();
+  if (layer_tree_frame_sink_) {
+    layer_tree_frame_sink_->WillSkipDraw();
+  }
 }
 
 struct SynchronousCompositorProxy::SharedMemoryWithSize {
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc
index 540d1ab..951c02f8 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.h"
 #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h"
+#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
 #include "third_party/blink/renderer/platform/widget/frame_widget.h"
 #include "third_party/blink/renderer/platform/widget/input/ime_event_guard.h"
 #include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
@@ -189,6 +190,11 @@
       FROM_HERE,
       base::BindOnce([](scoped_refptr<WidgetInputHandlerManager> manager) {},
                      std::move(widget_input_handler_manager_)));
+
+  if (widget_compositor_) {
+    widget_compositor_->Shutdown();
+    widget_compositor_ = nullptr;
+  }
 }
 
 cc::LayerTreeHost* WidgetBase::LayerTreeHost() const {
@@ -575,6 +581,18 @@
   client_->FocusChanged(enable);
 }
 
+void WidgetBase::BindWidgetCompositor(
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
+  if (widget_compositor_)
+    widget_compositor_->Shutdown();
+
+  widget_compositor_ = base::MakeRefCounted<WidgetCompositor>(
+      weak_ptr_factory_.GetWeakPtr(),
+      LayerTreeHost()->GetTaskRunnerProvider()->MainThreadTaskRunner(),
+      LayerTreeHost()->GetTaskRunnerProvider()->ImplThreadTaskRunner(),
+      std::move(receiver));
+}
+
 void WidgetBase::UpdateCompositionInfo(bool immediate_request) {
   if (!monitor_composition_info_ && !immediate_request)
     return;  // Do not calculate composition info if not requested.
diff --git a/third_party/blink/renderer/platform/widget/widget_base.h b/third_party/blink/renderer/platform/widget/widget_base.h
index e9505e2..b59af4cb 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.h
+++ b/third_party/blink/renderer/platform/widget/widget_base.h
@@ -39,6 +39,7 @@
 class LayerTreeView;
 class WidgetBaseClient;
 class WidgetInputHandlerManager;
+class WidgetCompositor;
 
 namespace scheduler {
 class WebRenderWidgetSchedulingState;
@@ -210,6 +211,13 @@
                CrossVariantMojoRemote<
                    mojom::blink::PointerLockContextInterfaceBase>)> callback);
 
+  void BindWidgetCompositor(
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver);
+
+  base::WeakPtr<WidgetBase> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
  private:
   bool CanComposeInline();
   void UpdateTextInputStateInternal(bool show_virtual_keyboard,
@@ -237,6 +245,7 @@
   base::TimeTicks was_shown_time_ = base::TimeTicks::Now();
   bool has_focus_ = false;
   WidgetBaseInputHandler input_handler_{this};
+  scoped_refptr<WidgetCompositor> widget_compositor_;
 
   // Stores the current selection bounds.
   gfx::Rect selection_focus_rect_;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a3f0c57..1e04fcc 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2901,15 +2901,6 @@
 crbug.com/626703 [ Mac10.14 ] virtual/omt-worker-fetch/external/wpt/service-workers/cache-storage/worker/cache-abort.https.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.15 ] virtual/omt-worker-fetch/external/wpt/service-workers/cache-storage/worker/cache-abort.https.html [ Failure Timeout ]
 crbug.com/626703 [ Retina ] virtual/omt-worker-fetch/external/wpt/service-workers/cache-storage/worker/cache-abort.https.html [ Failure Timeout ]
-crbug.com/626703 [ Linux ] virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-014.html [ Failure ]
-crbug.com/626703 [ Mac ] virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-014.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-014.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/alignment/grid-baseline-justify-001.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/alignment/grid-baseline-justify-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/alignment/grid-baseline-justify-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/alignment/grid-baseline-align-001.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/alignment/grid-baseline-align-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/alignment/grid-baseline-align-001.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/wasm/jsapi/functions/incumbent.html [ Crash ]
 crbug.com/626703 [ Mac ] external/wpt/wasm/jsapi/functions/incumbent.html [ Crash ]
 crbug.com/626703 [ Win ] external/wpt/wasm/jsapi/functions/incumbent.html [ Crash ]
@@ -3901,10 +3892,6 @@
 crbug.com/921722 external/wpt/css/css-grid/abspos/descendant-static-position-002.html [ Failure ]
 crbug.com/921722 external/wpt/css/css-grid/abspos/descendant-static-position-003.html [ Failure ]
 crbug.com/941987 external/wpt/css/css-grid/alignment/grid-baseline-align-cycles-001.html [ Failure ]
-crbug.com/1005519 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-001.html [ Failure ]
-crbug.com/1005519 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-002.html [ Failure ]
-crbug.com/1005519 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-004.html [ Failure ]
-crbug.com/1005519 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-003.html [ Failure ]
 crbug.com/759665 external/wpt/css/css-grid/animation/grid-template-columns-001.html [ Failure ]
 crbug.com/759665 external/wpt/css/css-grid/animation/grid-template-rows-001.html [ Failure ]
 crbug.com/1018439 external/wpt/css/css-grid/grid-child-percent-basis-resize-1.html [ Failure ]
@@ -3919,7 +3906,11 @@
 crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-003.html [ Failure ]
 
 # The 'last baseline' keyword is not implemented yet
-crbug.com/885175 external/wpt/css/css-grid/alignment/grid-item-self-baseline-001.html [ Failure ]
+crbug.com/885175 external/wpt/css/css-grid/alignment/grid-item-self-baseline-001.html [ SKip ]
+crbug.com/885175 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-001.html [ Skip ]
+crbug.com/885175 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-002.html [ Skip ]
+crbug.com/885175 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-004.html [ Skip ]
+crbug.com/885175 external/wpt/css/css-grid/alignment/grid-self-alignment-baseline-with-grid-003.html [ Skip ]
 
 # Baseline Content-Alignment is not implemented yet for CSS Grid Layout
 crbug.com/764235 external/wpt/css/css-grid/alignment/grid-item-content-baseline-001.html [ Skip ]
@@ -3930,6 +3921,8 @@
 crbug.com/764235 external/wpt/css/css-grid/alignment/grid-item-mixed-baseline-002.html [ Skip ]
 crbug.com/764235 external/wpt/css/css-grid/alignment/grid-item-mixed-baseline-003.html [ Skip ]
 crbug.com/764235 external/wpt/css/css-grid/alignment/grid-item-mixed-baseline-004.html [ Skip ]
+crbug.com/764235 external/wpt/css/css-grid/alignment/grid-baseline-align-001.html [ Skip ]
+crbug.com/764235 external/wpt/css/css-grid/alignment/grid-baseline-justify-001.html [ Skip ]
 
 # Subgrid is not implemented yet
 crbug.com/618969 external/wpt/css/css-grid/subgrid/* [ Skip ]
@@ -4200,6 +4193,7 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-011.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-012.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-013.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-014.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-gutters-and-alignment.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-002.html [ Failure ]
@@ -4585,6 +4579,8 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-placement-using-named-grid-lines-007.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-placement-using-named-grid-lines-008.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-placement-using-named-grid-lines-009.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-baseline-justify-001.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-baseline-align-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/auto-content-resolution-columns.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/auto-content-resolution-rows.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/breadth-size-resolution-grid.html [ Failure ]
@@ -6972,17 +6968,6 @@
 
 crbug.com/1092135 [ Linux ] virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Pass Timeout ]
 
-# FIXME(michaelludwig) - Suppress for Skia roll
-crbug.com/1097104 css3/filters/effect-reference-zoom-hw.html [ Pass Failure ]
-crbug.com/1097104 virtual/gpu-rasterization/images/color-profile-svg-fill-text.html [ Pass Failure ]
-crbug.com/1098743 css3/filters/backdrop-filter-boundary.html [ Pass Failure ]
-crbug.com/1098743 virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png.html [ Pass Failure ]
-crbug.com/1098743 virtual/gpu-rasterization/images/color-profile-background-image-cross-fade.html [ Pass Failure ]
-crbug.com/1098743 virtual/gpu-rasterization/images/color-profile-image-canvas-pattern.html [ Pass Failure ]
-crbug.com/1098743 virtual/gpu-rasterization/images/color-profile-mask-image-svg.html [ Pass Failure ]
-crbug.com/1098743 virtual/gpu-rasterization/images/cross-fade-background-size.html [ Pass Failure ]
-crbug.com/1098743 virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels.html [ Pass Failure ]
-
 # Sheriff 2020-06-16
 crbug.com/1044825 http/tests/devtools/network/resource-priority.js [ Pass Timeout ]
 
@@ -7035,6 +7020,9 @@
 crbug.com/1101699 external/wpt/webrtc/RTCPeerConnection-setLocalDescription-pranswer.html [ Pass Failure ]
 crbug.com/1101699 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-rollback.html [ Pass Failure ]
 
+# Temporarily disable to land a devtools change
+crbug.com/1107339 http/tests/devtools/extensions/extensions-api.js [ Pass Failure ]
+
 # Sheriff 2020-07-07
 # Additionally disabled on mac due to crbug.com/988248
 crbug.com/1083302 media/controls/volumechange-muted-attribute.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png
index 5dd7300..7e4d4423 100644
--- a/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
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 2bed024..2e14ecb 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
@@ -58283,7 +58283,7 @@
        ]
       ],
       "font-display.html": [
-       "006091f33475abc1686864f024b75a8659544e0e",
+       "b688bc2ea9d549f1db09a4fd452536710cfdd06c",
        [
         null,
         [
@@ -169378,7 +169378,7 @@
        []
       ],
       "font-display-ref.html": [
-       "b2cfcb5494287084298ffccb8ffa5135b55d1c98",
+       "2b894ed78c0e1b11fc0d27eaace8a6c39facba6f",
        []
       ]
      },
@@ -200173,19 +200173,19 @@
       }
      },
      "idlharness.any-expected.txt": [
-      "1c8a0efd1d51017aa439a6e90d41efa0032f9d90",
+      "efee9890d5a201a3e793794c34f0cedc0d63521a",
       []
      ],
      "idlharness.any.serviceworker-expected.txt": [
-      "f9ad60d25b9407d01166e9771e38cbb5aefb46d1",
+      "32d2d6a864a30c638f23863d5eff59d89aad64e3",
       []
      ],
      "idlharness.any.sharedworker-expected.txt": [
-      "f9ad60d25b9407d01166e9771e38cbb5aefb46d1",
+      "32d2d6a864a30c638f23863d5eff59d89aad64e3",
       []
      ],
      "idlharness.any.worker-expected.txt": [
-      "f9ad60d25b9407d01166e9771e38cbb5aefb46d1",
+      "32d2d6a864a30c638f23863d5eff59d89aad64e3",
       []
      ],
      "policies": {
@@ -200380,7 +200380,7 @@
        []
       ],
       "request-disturbed-expected.txt": [
-       "728414e56b902ed7665ad858d8f32e4217e9ae8d",
+       "fd6112d5743af5e118f294cfb070f5c62cec64ee",
        []
       ],
       "request-error-expected.txt": [
@@ -200392,19 +200392,19 @@
        []
       ],
       "request-init-stream.any-expected.txt": [
-       "f7dbf317da7070b1d4db683d97a916d7a9b0fad9",
+       "43ad8c18016b2d327428b0c344ea64496105fef0",
        []
       ],
       "request-init-stream.any.serviceworker-expected.txt": [
-       "f7dbf317da7070b1d4db683d97a916d7a9b0fad9",
+       "43ad8c18016b2d327428b0c344ea64496105fef0",
        []
       ],
       "request-init-stream.any.sharedworker-expected.txt": [
-       "f7dbf317da7070b1d4db683d97a916d7a9b0fad9",
+       "43ad8c18016b2d327428b0c344ea64496105fef0",
        []
       ],
       "request-init-stream.any.worker-expected.txt": [
-       "f7dbf317da7070b1d4db683d97a916d7a9b0fad9",
+       "43ad8c18016b2d327428b0c344ea64496105fef0",
        []
       ],
       "request-reset-attributes.https-expected.txt": [
@@ -217436,7 +217436,7 @@
        []
       ],
       "mpadded-002-expected.txt": [
-       "3e4389234558dbeef5425af9a38a2d9b385bdadf",
+       "671c76339140ffe8835354fe82f6eb0dd19ef1ee",
        []
       ],
       "mpadded-003-expected.txt": [
@@ -236525,6 +236525,10 @@
      "5238ce954992f94507668167696b314cfddcf71d",
      []
     ],
+    "disabled-by-feature-policy.https.sub.html.headers": [
+     "a9b2b95074245b7319d360303c2ff1d9d05655c5",
+     []
+    ],
     "resources": {
      "manual-helper.js": [
       "15bb17936e4479f7338b7cefbaa87522a2a02df4",
@@ -312797,7 +312801,7 @@
        ]
       ],
       "request-init-stream.any.js": [
-       "e7ffa729d15b841e307d49cbdc7994ce78b2f059",
+       "8c50c4929e75dacb61a3a74054a169ae2346bbd8",
        [
         "fetch/api/request/request-init-stream.any.html",
         {
@@ -354683,7 +354687,7 @@
        ]
       ],
       "mpadded-002.html": [
-       "5eed04bb0f599998db531bb56badf07e59129a11",
+       "420eda93887cd8a694da7e673cfd5cb89dcaa110",
        [
         null,
         {}
@@ -398153,6 +398157,15 @@
       {}
      ]
     ],
+    "disabled-by-feature-policy.https.sub.html": [
+     "9c1893232997ac27d493d54c74d0bfd0b8b8888b",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "idlharness.https.window.js": [
      "fbe59864af99c0a55b83e942f0e40f4d45de72a6",
      [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/parse-align-content-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/parse-align-content-001-expected.txt
new file mode 100644
index 0000000..e482c099
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/parse-align-content-001-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS Checking align-content: normal
+PASS Checking align-content: start
+PASS Checking align-content: end
+PASS Checking align-content: center
+PASS Checking align-content: flex-start
+PASS Checking align-content: flex-end
+PASS Checking align-content: stretch
+PASS Checking align-content: space-around
+PASS Checking align-content: space-between
+PASS Checking align-content: space-evenly
+PASS Checking align-content: baseline
+PASS Checking align-content: first baseline
+FAIL Checking align-content: last baseline assert_equals: align-content computed style is not what is should. expected "last baseline" but got "normal"
+PASS Checking align-content: safe flex-end
+PASS Checking align-content: unsafe end
+PASS Checking align-content: safe end
+PASS Checking align-content: unsafe flex-start
+PASS Checking align-content: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/parse-align-content-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/parse-align-content-003-expected.txt
new file mode 100644
index 0000000..0506093
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/parse-align-content-003-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS Checking align-content: normal
+PASS Checking align-content: start
+PASS Checking align-content: end
+PASS Checking align-content: center
+PASS Checking align-content: flex-start
+PASS Checking align-content: flex-end
+PASS Checking align-content: stretch
+PASS Checking align-content: space-around
+PASS Checking align-content: space-between
+PASS Checking align-content: space-evenly
+PASS Checking align-content: baseline
+PASS Checking align-content: first baseline
+FAIL Checking align-content: last baseline assert_equals: align-content specified value is not what it should. expected "last baseline" but got ""
+PASS Checking align-content: safe flex-end
+PASS Checking align-content: unsafe end
+PASS Checking align-content: safe end
+PASS Checking align-content: unsafe flex-start
+PASS Checking align-content: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/place-content-shorthand-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/place-content-shorthand-002-expected.txt
new file mode 100644
index 0000000..099e47fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/place-content-shorthand-002-expected.txt
@@ -0,0 +1,220 @@
+This is a testharness.js-based test.
+Found 216 tests; 204 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Checking place-content: normal normal
+PASS Checking place-content: normal left
+PASS Checking place-content: normal right
+PASS Checking place-content: normal start
+PASS Checking place-content: normal end
+PASS Checking place-content: normal center
+PASS Checking place-content: normal flex-start
+PASS Checking place-content: normal flex-end
+PASS Checking place-content: normal stretch
+PASS Checking place-content: normal space-around
+PASS Checking place-content: normal space-between
+PASS Checking place-content: normal space-evenly
+PASS Checking place-content: start normal
+PASS Checking place-content: start left
+PASS Checking place-content: start right
+PASS Checking place-content: start start
+PASS Checking place-content: start end
+PASS Checking place-content: start center
+PASS Checking place-content: start flex-start
+PASS Checking place-content: start flex-end
+PASS Checking place-content: start stretch
+PASS Checking place-content: start space-around
+PASS Checking place-content: start space-between
+PASS Checking place-content: start space-evenly
+PASS Checking place-content: end normal
+PASS Checking place-content: end left
+PASS Checking place-content: end right
+PASS Checking place-content: end start
+PASS Checking place-content: end end
+PASS Checking place-content: end center
+PASS Checking place-content: end flex-start
+PASS Checking place-content: end flex-end
+PASS Checking place-content: end stretch
+PASS Checking place-content: end space-around
+PASS Checking place-content: end space-between
+PASS Checking place-content: end space-evenly
+PASS Checking place-content: center normal
+PASS Checking place-content: center left
+PASS Checking place-content: center right
+PASS Checking place-content: center start
+PASS Checking place-content: center end
+PASS Checking place-content: center center
+PASS Checking place-content: center flex-start
+PASS Checking place-content: center flex-end
+PASS Checking place-content: center stretch
+PASS Checking place-content: center space-around
+PASS Checking place-content: center space-between
+PASS Checking place-content: center space-evenly
+PASS Checking place-content: flex-start normal
+PASS Checking place-content: flex-start left
+PASS Checking place-content: flex-start right
+PASS Checking place-content: flex-start start
+PASS Checking place-content: flex-start end
+PASS Checking place-content: flex-start center
+PASS Checking place-content: flex-start flex-start
+PASS Checking place-content: flex-start flex-end
+PASS Checking place-content: flex-start stretch
+PASS Checking place-content: flex-start space-around
+PASS Checking place-content: flex-start space-between
+PASS Checking place-content: flex-start space-evenly
+PASS Checking place-content: flex-end normal
+PASS Checking place-content: flex-end left
+PASS Checking place-content: flex-end right
+PASS Checking place-content: flex-end start
+PASS Checking place-content: flex-end end
+PASS Checking place-content: flex-end center
+PASS Checking place-content: flex-end flex-start
+PASS Checking place-content: flex-end flex-end
+PASS Checking place-content: flex-end stretch
+PASS Checking place-content: flex-end space-around
+PASS Checking place-content: flex-end space-between
+PASS Checking place-content: flex-end space-evenly
+PASS Checking place-content: stretch normal
+PASS Checking place-content: stretch left
+PASS Checking place-content: stretch right
+PASS Checking place-content: stretch start
+PASS Checking place-content: stretch end
+PASS Checking place-content: stretch center
+PASS Checking place-content: stretch flex-start
+PASS Checking place-content: stretch flex-end
+PASS Checking place-content: stretch stretch
+PASS Checking place-content: stretch space-around
+PASS Checking place-content: stretch space-between
+PASS Checking place-content: stretch space-evenly
+PASS Checking place-content: space-around normal
+PASS Checking place-content: space-around left
+PASS Checking place-content: space-around right
+PASS Checking place-content: space-around start
+PASS Checking place-content: space-around end
+PASS Checking place-content: space-around center
+PASS Checking place-content: space-around flex-start
+PASS Checking place-content: space-around flex-end
+PASS Checking place-content: space-around stretch
+PASS Checking place-content: space-around space-around
+PASS Checking place-content: space-around space-between
+PASS Checking place-content: space-around space-evenly
+PASS Checking place-content: space-between normal
+PASS Checking place-content: space-between left
+PASS Checking place-content: space-between right
+PASS Checking place-content: space-between start
+PASS Checking place-content: space-between end
+PASS Checking place-content: space-between center
+PASS Checking place-content: space-between flex-start
+PASS Checking place-content: space-between flex-end
+PASS Checking place-content: space-between stretch
+PASS Checking place-content: space-between space-around
+PASS Checking place-content: space-between space-between
+PASS Checking place-content: space-between space-evenly
+PASS Checking place-content: space-evenly normal
+PASS Checking place-content: space-evenly left
+PASS Checking place-content: space-evenly right
+PASS Checking place-content: space-evenly start
+PASS Checking place-content: space-evenly end
+PASS Checking place-content: space-evenly center
+PASS Checking place-content: space-evenly flex-start
+PASS Checking place-content: space-evenly flex-end
+PASS Checking place-content: space-evenly stretch
+PASS Checking place-content: space-evenly space-around
+PASS Checking place-content: space-evenly space-between
+PASS Checking place-content: space-evenly space-evenly
+PASS Checking place-content: baseline normal
+PASS Checking place-content: baseline left
+PASS Checking place-content: baseline right
+PASS Checking place-content: baseline start
+PASS Checking place-content: baseline end
+PASS Checking place-content: baseline center
+PASS Checking place-content: baseline flex-start
+PASS Checking place-content: baseline flex-end
+PASS Checking place-content: baseline stretch
+PASS Checking place-content: baseline space-around
+PASS Checking place-content: baseline space-between
+PASS Checking place-content: baseline space-evenly
+PASS Checking place-content: first baseline normal
+PASS Checking place-content: first baseline left
+PASS Checking place-content: first baseline right
+PASS Checking place-content: first baseline start
+PASS Checking place-content: first baseline end
+PASS Checking place-content: first baseline center
+PASS Checking place-content: first baseline flex-start
+PASS Checking place-content: first baseline flex-end
+PASS Checking place-content: first baseline stretch
+PASS Checking place-content: first baseline space-around
+PASS Checking place-content: first baseline space-between
+PASS Checking place-content: first baseline space-evenly
+FAIL Checking place-content: last baseline normal assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline left assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline right assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline start assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline end assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline center assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline flex-start assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline flex-end assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline stretch assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline space-around assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline space-between assert_equals: align-content expanded value expected "last baseline" but got ""
+FAIL Checking place-content: last baseline space-evenly assert_equals: align-content expanded value expected "last baseline" but got ""
+PASS Checking place-content: safe flex-end normal
+PASS Checking place-content: safe flex-end left
+PASS Checking place-content: safe flex-end right
+PASS Checking place-content: safe flex-end start
+PASS Checking place-content: safe flex-end end
+PASS Checking place-content: safe flex-end center
+PASS Checking place-content: safe flex-end flex-start
+PASS Checking place-content: safe flex-end flex-end
+PASS Checking place-content: safe flex-end stretch
+PASS Checking place-content: safe flex-end space-around
+PASS Checking place-content: safe flex-end space-between
+PASS Checking place-content: safe flex-end space-evenly
+PASS Checking place-content: unsafe end normal
+PASS Checking place-content: unsafe end left
+PASS Checking place-content: unsafe end right
+PASS Checking place-content: unsafe end start
+PASS Checking place-content: unsafe end end
+PASS Checking place-content: unsafe end center
+PASS Checking place-content: unsafe end flex-start
+PASS Checking place-content: unsafe end flex-end
+PASS Checking place-content: unsafe end stretch
+PASS Checking place-content: unsafe end space-around
+PASS Checking place-content: unsafe end space-between
+PASS Checking place-content: unsafe end space-evenly
+PASS Checking place-content: safe end normal
+PASS Checking place-content: safe end left
+PASS Checking place-content: safe end right
+PASS Checking place-content: safe end start
+PASS Checking place-content: safe end end
+PASS Checking place-content: safe end center
+PASS Checking place-content: safe end flex-start
+PASS Checking place-content: safe end flex-end
+PASS Checking place-content: safe end stretch
+PASS Checking place-content: safe end space-around
+PASS Checking place-content: safe end space-between
+PASS Checking place-content: safe end space-evenly
+PASS Checking place-content: unsafe flex-start normal
+PASS Checking place-content: unsafe flex-start left
+PASS Checking place-content: unsafe flex-start right
+PASS Checking place-content: unsafe flex-start start
+PASS Checking place-content: unsafe flex-start end
+PASS Checking place-content: unsafe flex-start center
+PASS Checking place-content: unsafe flex-start flex-start
+PASS Checking place-content: unsafe flex-start flex-end
+PASS Checking place-content: unsafe flex-start stretch
+PASS Checking place-content: unsafe flex-start space-around
+PASS Checking place-content: unsafe flex-start space-between
+PASS Checking place-content: unsafe flex-start space-evenly
+PASS Checking place-content: safe center normal
+PASS Checking place-content: safe center left
+PASS Checking place-content: safe center right
+PASS Checking place-content: safe center start
+PASS Checking place-content: safe center end
+PASS Checking place-content: safe center center
+PASS Checking place-content: safe center flex-start
+PASS Checking place-content: safe center flex-end
+PASS Checking place-content: safe center stretch
+PASS Checking place-content: safe center space-around
+PASS Checking place-content: safe center space-between
+PASS Checking place-content: safe center space-evenly
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/place-content-shorthand-006-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/place-content-shorthand-006-expected.txt
new file mode 100644
index 0000000..a8b0aef0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/content-distribution/place-content-shorthand-006-expected.txt
@@ -0,0 +1,160 @@
+This is a testharness.js-based test.
+Found 156 tests; 144 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Checking place-content: normal normal
+PASS Checking place-content: normal left
+PASS Checking place-content: normal right
+PASS Checking place-content: normal start
+PASS Checking place-content: normal end
+PASS Checking place-content: normal center
+PASS Checking place-content: normal flex-start
+PASS Checking place-content: normal flex-end
+PASS Checking place-content: normal stretch
+PASS Checking place-content: normal space-around
+PASS Checking place-content: normal space-between
+PASS Checking place-content: normal space-evenly
+PASS Checking place-content: start normal
+PASS Checking place-content: start left
+PASS Checking place-content: start right
+PASS Checking place-content: start start
+PASS Checking place-content: start end
+PASS Checking place-content: start center
+PASS Checking place-content: start flex-start
+PASS Checking place-content: start flex-end
+PASS Checking place-content: start stretch
+PASS Checking place-content: start space-around
+PASS Checking place-content: start space-between
+PASS Checking place-content: start space-evenly
+PASS Checking place-content: end normal
+PASS Checking place-content: end left
+PASS Checking place-content: end right
+PASS Checking place-content: end start
+PASS Checking place-content: end end
+PASS Checking place-content: end center
+PASS Checking place-content: end flex-start
+PASS Checking place-content: end flex-end
+PASS Checking place-content: end stretch
+PASS Checking place-content: end space-around
+PASS Checking place-content: end space-between
+PASS Checking place-content: end space-evenly
+PASS Checking place-content: center normal
+PASS Checking place-content: center left
+PASS Checking place-content: center right
+PASS Checking place-content: center start
+PASS Checking place-content: center end
+PASS Checking place-content: center center
+PASS Checking place-content: center flex-start
+PASS Checking place-content: center flex-end
+PASS Checking place-content: center stretch
+PASS Checking place-content: center space-around
+PASS Checking place-content: center space-between
+PASS Checking place-content: center space-evenly
+PASS Checking place-content: flex-start normal
+PASS Checking place-content: flex-start left
+PASS Checking place-content: flex-start right
+PASS Checking place-content: flex-start start
+PASS Checking place-content: flex-start end
+PASS Checking place-content: flex-start center
+PASS Checking place-content: flex-start flex-start
+PASS Checking place-content: flex-start flex-end
+PASS Checking place-content: flex-start stretch
+PASS Checking place-content: flex-start space-around
+PASS Checking place-content: flex-start space-between
+PASS Checking place-content: flex-start space-evenly
+PASS Checking place-content: flex-end normal
+PASS Checking place-content: flex-end left
+PASS Checking place-content: flex-end right
+PASS Checking place-content: flex-end start
+PASS Checking place-content: flex-end end
+PASS Checking place-content: flex-end center
+PASS Checking place-content: flex-end flex-start
+PASS Checking place-content: flex-end flex-end
+PASS Checking place-content: flex-end stretch
+PASS Checking place-content: flex-end space-around
+PASS Checking place-content: flex-end space-between
+PASS Checking place-content: flex-end space-evenly
+PASS Checking place-content: stretch normal
+PASS Checking place-content: stretch left
+PASS Checking place-content: stretch right
+PASS Checking place-content: stretch start
+PASS Checking place-content: stretch end
+PASS Checking place-content: stretch center
+PASS Checking place-content: stretch flex-start
+PASS Checking place-content: stretch flex-end
+PASS Checking place-content: stretch stretch
+PASS Checking place-content: stretch space-around
+PASS Checking place-content: stretch space-between
+PASS Checking place-content: stretch space-evenly
+PASS Checking place-content: space-around normal
+PASS Checking place-content: space-around left
+PASS Checking place-content: space-around right
+PASS Checking place-content: space-around start
+PASS Checking place-content: space-around end
+PASS Checking place-content: space-around center
+PASS Checking place-content: space-around flex-start
+PASS Checking place-content: space-around flex-end
+PASS Checking place-content: space-around stretch
+PASS Checking place-content: space-around space-around
+PASS Checking place-content: space-around space-between
+PASS Checking place-content: space-around space-evenly
+PASS Checking place-content: space-between normal
+PASS Checking place-content: space-between left
+PASS Checking place-content: space-between right
+PASS Checking place-content: space-between start
+PASS Checking place-content: space-between end
+PASS Checking place-content: space-between center
+PASS Checking place-content: space-between flex-start
+PASS Checking place-content: space-between flex-end
+PASS Checking place-content: space-between stretch
+PASS Checking place-content: space-between space-around
+PASS Checking place-content: space-between space-between
+PASS Checking place-content: space-between space-evenly
+PASS Checking place-content: space-evenly normal
+PASS Checking place-content: space-evenly left
+PASS Checking place-content: space-evenly right
+PASS Checking place-content: space-evenly start
+PASS Checking place-content: space-evenly end
+PASS Checking place-content: space-evenly center
+PASS Checking place-content: space-evenly flex-start
+PASS Checking place-content: space-evenly flex-end
+PASS Checking place-content: space-evenly stretch
+PASS Checking place-content: space-evenly space-around
+PASS Checking place-content: space-evenly space-between
+PASS Checking place-content: space-evenly space-evenly
+PASS Checking place-content: baseline normal
+PASS Checking place-content: baseline left
+PASS Checking place-content: baseline right
+PASS Checking place-content: baseline start
+PASS Checking place-content: baseline end
+PASS Checking place-content: baseline center
+PASS Checking place-content: baseline flex-start
+PASS Checking place-content: baseline flex-end
+PASS Checking place-content: baseline stretch
+PASS Checking place-content: baseline space-around
+PASS Checking place-content: baseline space-between
+PASS Checking place-content: baseline space-evenly
+PASS Checking place-content: first baseline normal
+PASS Checking place-content: first baseline left
+PASS Checking place-content: first baseline right
+PASS Checking place-content: first baseline start
+PASS Checking place-content: first baseline end
+PASS Checking place-content: first baseline center
+PASS Checking place-content: first baseline flex-start
+PASS Checking place-content: first baseline flex-end
+PASS Checking place-content: first baseline stretch
+PASS Checking place-content: first baseline space-around
+PASS Checking place-content: first baseline space-between
+PASS Checking place-content: first baseline space-evenly
+FAIL Checking place-content: last baseline normal assert_equals: last baseline normal specified value expected "last baseline normal" but got ""
+FAIL Checking place-content: last baseline left assert_equals: last baseline left specified value expected "last baseline left" but got ""
+FAIL Checking place-content: last baseline right assert_equals: last baseline right specified value expected "last baseline right" but got ""
+FAIL Checking place-content: last baseline start assert_equals: last baseline start specified value expected "last baseline start" but got ""
+FAIL Checking place-content: last baseline end assert_equals: last baseline end specified value expected "last baseline end" but got ""
+FAIL Checking place-content: last baseline center assert_equals: last baseline center specified value expected "last baseline center" but got ""
+FAIL Checking place-content: last baseline flex-start assert_equals: last baseline flex-start specified value expected "last baseline flex-start" but got ""
+FAIL Checking place-content: last baseline flex-end assert_equals: last baseline flex-end specified value expected "last baseline flex-end" but got ""
+FAIL Checking place-content: last baseline stretch assert_equals: last baseline stretch specified value expected "last baseline stretch" but got ""
+FAIL Checking place-content: last baseline space-around assert_equals: last baseline space-around specified value expected "last baseline space-around" but got ""
+FAIL Checking place-content: last baseline space-between assert_equals: last baseline space-between specified value expected "last baseline space-between" but got ""
+FAIL Checking place-content: last baseline space-evenly assert_equals: last baseline space-evenly specified value expected "last baseline space-evenly" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-align-items-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-align-items-001-expected.txt
new file mode 100644
index 0000000..62864d15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-align-items-001-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS Checking align-items: normal
+PASS Checking align-items: stretch
+PASS Checking align-items: start
+PASS Checking align-items: end
+PASS Checking align-items: self-start
+PASS Checking align-items: self-end
+PASS Checking align-items: center
+PASS Checking align-items: flex-start
+PASS Checking align-items: flex-end
+PASS Checking align-items: baseline
+PASS Checking align-items: first baseline
+FAIL Checking align-items: last baseline assert_equals: align-items computed style is not what is should. expected "last baseline" but got "normal"
+PASS Checking align-items: safe flex-end
+PASS Checking align-items: unsafe end
+PASS Checking align-items: safe end
+PASS Checking align-items: unsafe flex-start
+PASS Checking align-items: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-align-items-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-align-items-003-expected.txt
new file mode 100644
index 0000000..e541f7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-align-items-003-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS Checking align-items: normal
+PASS Checking align-items: stretch
+PASS Checking align-items: start
+PASS Checking align-items: end
+PASS Checking align-items: self-start
+PASS Checking align-items: self-end
+PASS Checking align-items: center
+PASS Checking align-items: flex-start
+PASS Checking align-items: flex-end
+PASS Checking align-items: baseline
+PASS Checking align-items: first baseline
+FAIL Checking align-items: last baseline assert_equals: align-items specified value is not what it should. expected "last baseline" but got ""
+PASS Checking align-items: safe flex-end
+PASS Checking align-items: unsafe end
+PASS Checking align-items: safe end
+PASS Checking align-items: unsafe flex-start
+PASS Checking align-items: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-justify-items-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-justify-items-001-expected.txt
new file mode 100644
index 0000000..f88a832
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-justify-items-001-expected.txt
@@ -0,0 +1,25 @@
+This is a testharness.js-based test.
+PASS Checking justify-items: normal
+PASS Checking justify-items: stretch
+PASS Checking justify-items: left
+PASS Checking justify-items: right
+PASS Checking justify-items: start
+PASS Checking justify-items: end
+PASS Checking justify-items: self-start
+PASS Checking justify-items: self-end
+PASS Checking justify-items: center
+PASS Checking justify-items: flex-start
+PASS Checking justify-items: flex-end
+PASS Checking justify-items: baseline
+PASS Checking justify-items: first baseline
+FAIL Checking justify-items: last baseline assert_equals: justify-items computed style is not what is should. expected "last baseline" but got "normal"
+PASS Checking justify-items: safe flex-end
+PASS Checking justify-items: unsafe end
+PASS Checking justify-items: safe end
+PASS Checking justify-items: unsafe flex-start
+PASS Checking justify-items: safe center
+PASS Checking justify-items: legacy left
+PASS Checking justify-items: legacy center
+PASS Checking justify-items: legacy right
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-justify-items-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-justify-items-003-expected.txt
new file mode 100644
index 0000000..281d2c0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/parse-justify-items-003-expected.txt
@@ -0,0 +1,25 @@
+This is a testharness.js-based test.
+PASS Checking justify-items: normal
+PASS Checking justify-items: stretch
+PASS Checking justify-items: left
+PASS Checking justify-items: right
+PASS Checking justify-items: start
+PASS Checking justify-items: end
+PASS Checking justify-items: self-start
+PASS Checking justify-items: self-end
+PASS Checking justify-items: center
+PASS Checking justify-items: flex-start
+PASS Checking justify-items: flex-end
+PASS Checking justify-items: baseline
+PASS Checking justify-items: first baseline
+FAIL Checking justify-items: last baseline assert_equals: justify-items specified value is not what it should. expected "last baseline" but got ""
+PASS Checking justify-items: safe flex-end
+PASS Checking justify-items: unsafe end
+PASS Checking justify-items: safe end
+PASS Checking justify-items: unsafe flex-start
+PASS Checking justify-items: safe center
+PASS Checking justify-items: legacy left
+PASS Checking justify-items: legacy center
+PASS Checking justify-items: legacy right
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-001-expected.txt
new file mode 100644
index 0000000..65f86047
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-001-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS Checking place-items: normal
+PASS Checking place-items: stretch
+PASS Checking place-items: start
+PASS Checking place-items: end
+PASS Checking place-items: self-start
+PASS Checking place-items: self-end
+PASS Checking place-items: center
+PASS Checking place-items: flex-start
+PASS Checking place-items: flex-end
+PASS Checking place-items: baseline
+PASS Checking place-items: first baseline
+FAIL Checking place-items: last baseline assert_equals: align-items expanded value expected "last baseline" but got ""
+PASS Checking place-items: safe flex-end
+PASS Checking place-items: unsafe end
+PASS Checking place-items: safe end
+PASS Checking place-items: unsafe flex-start
+PASS Checking place-items: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-002-expected.txt
new file mode 100644
index 0000000..05eeda9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-002-expected.txt
@@ -0,0 +1,378 @@
+This is a testharness.js-based test.
+Found 374 tests; 336 PASS, 38 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Checking place-items: normal left
+PASS Checking place-items: normal right
+PASS Checking place-items: normal legacy left
+PASS Checking place-items: normal legacy center
+PASS Checking place-items: normal legacy right
+PASS Checking place-items: normal normal
+PASS Checking place-items: normal stretch
+PASS Checking place-items: normal start
+PASS Checking place-items: normal end
+PASS Checking place-items: normal self-start
+PASS Checking place-items: normal self-end
+PASS Checking place-items: normal center
+PASS Checking place-items: normal flex-start
+PASS Checking place-items: normal flex-end
+PASS Checking place-items: normal baseline
+PASS Checking place-items: normal first baseline
+FAIL Checking place-items: normal last baseline assert_equals: align-items expanded value expected "normal" but got ""
+PASS Checking place-items: normal safe flex-end
+PASS Checking place-items: normal unsafe end
+PASS Checking place-items: normal safe end
+PASS Checking place-items: normal unsafe flex-start
+PASS Checking place-items: normal safe center
+PASS Checking place-items: stretch left
+PASS Checking place-items: stretch right
+PASS Checking place-items: stretch legacy left
+PASS Checking place-items: stretch legacy center
+PASS Checking place-items: stretch legacy right
+PASS Checking place-items: stretch normal
+PASS Checking place-items: stretch stretch
+PASS Checking place-items: stretch start
+PASS Checking place-items: stretch end
+PASS Checking place-items: stretch self-start
+PASS Checking place-items: stretch self-end
+PASS Checking place-items: stretch center
+PASS Checking place-items: stretch flex-start
+PASS Checking place-items: stretch flex-end
+PASS Checking place-items: stretch baseline
+PASS Checking place-items: stretch first baseline
+FAIL Checking place-items: stretch last baseline assert_equals: align-items expanded value expected "stretch" but got ""
+PASS Checking place-items: stretch safe flex-end
+PASS Checking place-items: stretch unsafe end
+PASS Checking place-items: stretch safe end
+PASS Checking place-items: stretch unsafe flex-start
+PASS Checking place-items: stretch safe center
+PASS Checking place-items: start left
+PASS Checking place-items: start right
+PASS Checking place-items: start legacy left
+PASS Checking place-items: start legacy center
+PASS Checking place-items: start legacy right
+PASS Checking place-items: start normal
+PASS Checking place-items: start stretch
+PASS Checking place-items: start start
+PASS Checking place-items: start end
+PASS Checking place-items: start self-start
+PASS Checking place-items: start self-end
+PASS Checking place-items: start center
+PASS Checking place-items: start flex-start
+PASS Checking place-items: start flex-end
+PASS Checking place-items: start baseline
+PASS Checking place-items: start first baseline
+FAIL Checking place-items: start last baseline assert_equals: align-items expanded value expected "start" but got ""
+PASS Checking place-items: start safe flex-end
+PASS Checking place-items: start unsafe end
+PASS Checking place-items: start safe end
+PASS Checking place-items: start unsafe flex-start
+PASS Checking place-items: start safe center
+PASS Checking place-items: end left
+PASS Checking place-items: end right
+PASS Checking place-items: end legacy left
+PASS Checking place-items: end legacy center
+PASS Checking place-items: end legacy right
+PASS Checking place-items: end normal
+PASS Checking place-items: end stretch
+PASS Checking place-items: end start
+PASS Checking place-items: end end
+PASS Checking place-items: end self-start
+PASS Checking place-items: end self-end
+PASS Checking place-items: end center
+PASS Checking place-items: end flex-start
+PASS Checking place-items: end flex-end
+PASS Checking place-items: end baseline
+PASS Checking place-items: end first baseline
+FAIL Checking place-items: end last baseline assert_equals: align-items expanded value expected "end" but got ""
+PASS Checking place-items: end safe flex-end
+PASS Checking place-items: end unsafe end
+PASS Checking place-items: end safe end
+PASS Checking place-items: end unsafe flex-start
+PASS Checking place-items: end safe center
+PASS Checking place-items: self-start left
+PASS Checking place-items: self-start right
+PASS Checking place-items: self-start legacy left
+PASS Checking place-items: self-start legacy center
+PASS Checking place-items: self-start legacy right
+PASS Checking place-items: self-start normal
+PASS Checking place-items: self-start stretch
+PASS Checking place-items: self-start start
+PASS Checking place-items: self-start end
+PASS Checking place-items: self-start self-start
+PASS Checking place-items: self-start self-end
+PASS Checking place-items: self-start center
+PASS Checking place-items: self-start flex-start
+PASS Checking place-items: self-start flex-end
+PASS Checking place-items: self-start baseline
+PASS Checking place-items: self-start first baseline
+FAIL Checking place-items: self-start last baseline assert_equals: align-items expanded value expected "self-start" but got ""
+PASS Checking place-items: self-start safe flex-end
+PASS Checking place-items: self-start unsafe end
+PASS Checking place-items: self-start safe end
+PASS Checking place-items: self-start unsafe flex-start
+PASS Checking place-items: self-start safe center
+PASS Checking place-items: self-end left
+PASS Checking place-items: self-end right
+PASS Checking place-items: self-end legacy left
+PASS Checking place-items: self-end legacy center
+PASS Checking place-items: self-end legacy right
+PASS Checking place-items: self-end normal
+PASS Checking place-items: self-end stretch
+PASS Checking place-items: self-end start
+PASS Checking place-items: self-end end
+PASS Checking place-items: self-end self-start
+PASS Checking place-items: self-end self-end
+PASS Checking place-items: self-end center
+PASS Checking place-items: self-end flex-start
+PASS Checking place-items: self-end flex-end
+PASS Checking place-items: self-end baseline
+PASS Checking place-items: self-end first baseline
+FAIL Checking place-items: self-end last baseline assert_equals: align-items expanded value expected "self-end" but got ""
+PASS Checking place-items: self-end safe flex-end
+PASS Checking place-items: self-end unsafe end
+PASS Checking place-items: self-end safe end
+PASS Checking place-items: self-end unsafe flex-start
+PASS Checking place-items: self-end safe center
+PASS Checking place-items: center left
+PASS Checking place-items: center right
+PASS Checking place-items: center legacy left
+PASS Checking place-items: center legacy center
+PASS Checking place-items: center legacy right
+PASS Checking place-items: center normal
+PASS Checking place-items: center stretch
+PASS Checking place-items: center start
+PASS Checking place-items: center end
+PASS Checking place-items: center self-start
+PASS Checking place-items: center self-end
+PASS Checking place-items: center center
+PASS Checking place-items: center flex-start
+PASS Checking place-items: center flex-end
+PASS Checking place-items: center baseline
+PASS Checking place-items: center first baseline
+FAIL Checking place-items: center last baseline assert_equals: align-items expanded value expected "center" but got ""
+PASS Checking place-items: center safe flex-end
+PASS Checking place-items: center unsafe end
+PASS Checking place-items: center safe end
+PASS Checking place-items: center unsafe flex-start
+PASS Checking place-items: center safe center
+PASS Checking place-items: flex-start left
+PASS Checking place-items: flex-start right
+PASS Checking place-items: flex-start legacy left
+PASS Checking place-items: flex-start legacy center
+PASS Checking place-items: flex-start legacy right
+PASS Checking place-items: flex-start normal
+PASS Checking place-items: flex-start stretch
+PASS Checking place-items: flex-start start
+PASS Checking place-items: flex-start end
+PASS Checking place-items: flex-start self-start
+PASS Checking place-items: flex-start self-end
+PASS Checking place-items: flex-start center
+PASS Checking place-items: flex-start flex-start
+PASS Checking place-items: flex-start flex-end
+PASS Checking place-items: flex-start baseline
+PASS Checking place-items: flex-start first baseline
+FAIL Checking place-items: flex-start last baseline assert_equals: align-items expanded value expected "flex-start" but got ""
+PASS Checking place-items: flex-start safe flex-end
+PASS Checking place-items: flex-start unsafe end
+PASS Checking place-items: flex-start safe end
+PASS Checking place-items: flex-start unsafe flex-start
+PASS Checking place-items: flex-start safe center
+PASS Checking place-items: flex-end left
+PASS Checking place-items: flex-end right
+PASS Checking place-items: flex-end legacy left
+PASS Checking place-items: flex-end legacy center
+PASS Checking place-items: flex-end legacy right
+PASS Checking place-items: flex-end normal
+PASS Checking place-items: flex-end stretch
+PASS Checking place-items: flex-end start
+PASS Checking place-items: flex-end end
+PASS Checking place-items: flex-end self-start
+PASS Checking place-items: flex-end self-end
+PASS Checking place-items: flex-end center
+PASS Checking place-items: flex-end flex-start
+PASS Checking place-items: flex-end flex-end
+PASS Checking place-items: flex-end baseline
+PASS Checking place-items: flex-end first baseline
+FAIL Checking place-items: flex-end last baseline assert_equals: align-items expanded value expected "flex-end" but got ""
+PASS Checking place-items: flex-end safe flex-end
+PASS Checking place-items: flex-end unsafe end
+PASS Checking place-items: flex-end safe end
+PASS Checking place-items: flex-end unsafe flex-start
+PASS Checking place-items: flex-end safe center
+PASS Checking place-items: baseline left
+PASS Checking place-items: baseline right
+PASS Checking place-items: baseline legacy left
+PASS Checking place-items: baseline legacy center
+PASS Checking place-items: baseline legacy right
+PASS Checking place-items: baseline normal
+PASS Checking place-items: baseline stretch
+PASS Checking place-items: baseline start
+PASS Checking place-items: baseline end
+PASS Checking place-items: baseline self-start
+PASS Checking place-items: baseline self-end
+PASS Checking place-items: baseline center
+PASS Checking place-items: baseline flex-start
+PASS Checking place-items: baseline flex-end
+PASS Checking place-items: baseline baseline
+PASS Checking place-items: baseline first baseline
+FAIL Checking place-items: baseline last baseline assert_equals: align-items expanded value expected "baseline" but got ""
+PASS Checking place-items: baseline safe flex-end
+PASS Checking place-items: baseline unsafe end
+PASS Checking place-items: baseline safe end
+PASS Checking place-items: baseline unsafe flex-start
+PASS Checking place-items: baseline safe center
+PASS Checking place-items: first baseline left
+PASS Checking place-items: first baseline right
+PASS Checking place-items: first baseline legacy left
+PASS Checking place-items: first baseline legacy center
+PASS Checking place-items: first baseline legacy right
+PASS Checking place-items: first baseline normal
+PASS Checking place-items: first baseline stretch
+PASS Checking place-items: first baseline start
+PASS Checking place-items: first baseline end
+PASS Checking place-items: first baseline self-start
+PASS Checking place-items: first baseline self-end
+PASS Checking place-items: first baseline center
+PASS Checking place-items: first baseline flex-start
+PASS Checking place-items: first baseline flex-end
+PASS Checking place-items: first baseline baseline
+PASS Checking place-items: first baseline first baseline
+FAIL Checking place-items: first baseline last baseline assert_equals: align-items expanded value expected "baseline" but got ""
+PASS Checking place-items: first baseline safe flex-end
+PASS Checking place-items: first baseline unsafe end
+PASS Checking place-items: first baseline safe end
+PASS Checking place-items: first baseline unsafe flex-start
+PASS Checking place-items: first baseline safe center
+FAIL Checking place-items: last baseline left assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline right assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline legacy left assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline legacy center assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline legacy right assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline normal assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline stretch assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline start assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline end assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline self-start assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline self-end assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline center assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline flex-start assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline flex-end assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline baseline assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline first baseline assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline last baseline assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline safe flex-end assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline unsafe end assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline safe end assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline unsafe flex-start assert_equals: align-items expanded value expected "last baseline" but got ""
+FAIL Checking place-items: last baseline safe center assert_equals: align-items expanded value expected "last baseline" but got ""
+PASS Checking place-items: safe flex-end left
+PASS Checking place-items: safe flex-end right
+PASS Checking place-items: safe flex-end legacy left
+PASS Checking place-items: safe flex-end legacy center
+PASS Checking place-items: safe flex-end legacy right
+PASS Checking place-items: safe flex-end normal
+PASS Checking place-items: safe flex-end stretch
+PASS Checking place-items: safe flex-end start
+PASS Checking place-items: safe flex-end end
+PASS Checking place-items: safe flex-end self-start
+PASS Checking place-items: safe flex-end self-end
+PASS Checking place-items: safe flex-end center
+PASS Checking place-items: safe flex-end flex-start
+PASS Checking place-items: safe flex-end flex-end
+PASS Checking place-items: safe flex-end baseline
+PASS Checking place-items: safe flex-end first baseline
+FAIL Checking place-items: safe flex-end last baseline assert_equals: align-items expanded value expected "safe flex-end" but got ""
+PASS Checking place-items: safe flex-end safe flex-end
+PASS Checking place-items: safe flex-end unsafe end
+PASS Checking place-items: safe flex-end safe end
+PASS Checking place-items: safe flex-end unsafe flex-start
+PASS Checking place-items: safe flex-end safe center
+PASS Checking place-items: unsafe end left
+PASS Checking place-items: unsafe end right
+PASS Checking place-items: unsafe end legacy left
+PASS Checking place-items: unsafe end legacy center
+PASS Checking place-items: unsafe end legacy right
+PASS Checking place-items: unsafe end normal
+PASS Checking place-items: unsafe end stretch
+PASS Checking place-items: unsafe end start
+PASS Checking place-items: unsafe end end
+PASS Checking place-items: unsafe end self-start
+PASS Checking place-items: unsafe end self-end
+PASS Checking place-items: unsafe end center
+PASS Checking place-items: unsafe end flex-start
+PASS Checking place-items: unsafe end flex-end
+PASS Checking place-items: unsafe end baseline
+PASS Checking place-items: unsafe end first baseline
+FAIL Checking place-items: unsafe end last baseline assert_equals: align-items expanded value expected "unsafe end" but got ""
+PASS Checking place-items: unsafe end safe flex-end
+PASS Checking place-items: unsafe end unsafe end
+PASS Checking place-items: unsafe end safe end
+PASS Checking place-items: unsafe end unsafe flex-start
+PASS Checking place-items: unsafe end safe center
+PASS Checking place-items: safe end left
+PASS Checking place-items: safe end right
+PASS Checking place-items: safe end legacy left
+PASS Checking place-items: safe end legacy center
+PASS Checking place-items: safe end legacy right
+PASS Checking place-items: safe end normal
+PASS Checking place-items: safe end stretch
+PASS Checking place-items: safe end start
+PASS Checking place-items: safe end end
+PASS Checking place-items: safe end self-start
+PASS Checking place-items: safe end self-end
+PASS Checking place-items: safe end center
+PASS Checking place-items: safe end flex-start
+PASS Checking place-items: safe end flex-end
+PASS Checking place-items: safe end baseline
+PASS Checking place-items: safe end first baseline
+FAIL Checking place-items: safe end last baseline assert_equals: align-items expanded value expected "safe end" but got ""
+PASS Checking place-items: safe end safe flex-end
+PASS Checking place-items: safe end unsafe end
+PASS Checking place-items: safe end safe end
+PASS Checking place-items: safe end unsafe flex-start
+PASS Checking place-items: safe end safe center
+PASS Checking place-items: unsafe flex-start left
+PASS Checking place-items: unsafe flex-start right
+PASS Checking place-items: unsafe flex-start legacy left
+PASS Checking place-items: unsafe flex-start legacy center
+PASS Checking place-items: unsafe flex-start legacy right
+PASS Checking place-items: unsafe flex-start normal
+PASS Checking place-items: unsafe flex-start stretch
+PASS Checking place-items: unsafe flex-start start
+PASS Checking place-items: unsafe flex-start end
+PASS Checking place-items: unsafe flex-start self-start
+PASS Checking place-items: unsafe flex-start self-end
+PASS Checking place-items: unsafe flex-start center
+PASS Checking place-items: unsafe flex-start flex-start
+PASS Checking place-items: unsafe flex-start flex-end
+PASS Checking place-items: unsafe flex-start baseline
+PASS Checking place-items: unsafe flex-start first baseline
+FAIL Checking place-items: unsafe flex-start last baseline assert_equals: align-items expanded value expected "unsafe flex-start" but got ""
+PASS Checking place-items: unsafe flex-start safe flex-end
+PASS Checking place-items: unsafe flex-start unsafe end
+PASS Checking place-items: unsafe flex-start safe end
+PASS Checking place-items: unsafe flex-start unsafe flex-start
+PASS Checking place-items: unsafe flex-start safe center
+PASS Checking place-items: safe center left
+PASS Checking place-items: safe center right
+PASS Checking place-items: safe center legacy left
+PASS Checking place-items: safe center legacy center
+PASS Checking place-items: safe center legacy right
+PASS Checking place-items: safe center normal
+PASS Checking place-items: safe center stretch
+PASS Checking place-items: safe center start
+PASS Checking place-items: safe center end
+PASS Checking place-items: safe center self-start
+PASS Checking place-items: safe center self-end
+PASS Checking place-items: safe center center
+PASS Checking place-items: safe center flex-start
+PASS Checking place-items: safe center flex-end
+PASS Checking place-items: safe center baseline
+PASS Checking place-items: safe center first baseline
+FAIL Checking place-items: safe center last baseline assert_equals: align-items expanded value expected "safe center" but got ""
+PASS Checking place-items: safe center safe flex-end
+PASS Checking place-items: safe center unsafe end
+PASS Checking place-items: safe center safe end
+PASS Checking place-items: safe center unsafe flex-start
+PASS Checking place-items: safe center safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-006-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-006-expected.txt
new file mode 100644
index 0000000..828e683c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/place-items-shorthand-006-expected.txt
@@ -0,0 +1,172 @@
+This is a testharness.js-based test.
+Found 168 tests; 143 PASS, 25 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Checking place-items: normal left
+PASS Checking place-items: normal right
+PASS Checking place-items: normal normal
+PASS Checking place-items: normal stretch
+PASS Checking place-items: normal start
+PASS Checking place-items: normal end
+PASS Checking place-items: normal self-start
+PASS Checking place-items: normal self-end
+PASS Checking place-items: normal center
+PASS Checking place-items: normal flex-start
+PASS Checking place-items: normal flex-end
+PASS Checking place-items: normal baseline
+PASS Checking place-items: normal first baseline
+FAIL Checking place-items: normal last baseline assert_equals: normal last baseline specified value expected "normal last baseline" but got ""
+PASS Checking place-items: stretch left
+PASS Checking place-items: stretch right
+PASS Checking place-items: stretch normal
+PASS Checking place-items: stretch stretch
+PASS Checking place-items: stretch start
+PASS Checking place-items: stretch end
+PASS Checking place-items: stretch self-start
+PASS Checking place-items: stretch self-end
+PASS Checking place-items: stretch center
+PASS Checking place-items: stretch flex-start
+PASS Checking place-items: stretch flex-end
+PASS Checking place-items: stretch baseline
+PASS Checking place-items: stretch first baseline
+FAIL Checking place-items: stretch last baseline assert_equals: stretch last baseline specified value expected "stretch last baseline" but got ""
+PASS Checking place-items: start left
+PASS Checking place-items: start right
+PASS Checking place-items: start normal
+PASS Checking place-items: start stretch
+PASS Checking place-items: start start
+PASS Checking place-items: start end
+PASS Checking place-items: start self-start
+PASS Checking place-items: start self-end
+PASS Checking place-items: start center
+PASS Checking place-items: start flex-start
+PASS Checking place-items: start flex-end
+PASS Checking place-items: start baseline
+PASS Checking place-items: start first baseline
+FAIL Checking place-items: start last baseline assert_equals: start last baseline specified value expected "start last baseline" but got ""
+PASS Checking place-items: end left
+PASS Checking place-items: end right
+PASS Checking place-items: end normal
+PASS Checking place-items: end stretch
+PASS Checking place-items: end start
+PASS Checking place-items: end end
+PASS Checking place-items: end self-start
+PASS Checking place-items: end self-end
+PASS Checking place-items: end center
+PASS Checking place-items: end flex-start
+PASS Checking place-items: end flex-end
+PASS Checking place-items: end baseline
+PASS Checking place-items: end first baseline
+FAIL Checking place-items: end last baseline assert_equals: end last baseline specified value expected "end last baseline" but got ""
+PASS Checking place-items: self-start left
+PASS Checking place-items: self-start right
+PASS Checking place-items: self-start normal
+PASS Checking place-items: self-start stretch
+PASS Checking place-items: self-start start
+PASS Checking place-items: self-start end
+PASS Checking place-items: self-start self-start
+PASS Checking place-items: self-start self-end
+PASS Checking place-items: self-start center
+PASS Checking place-items: self-start flex-start
+PASS Checking place-items: self-start flex-end
+PASS Checking place-items: self-start baseline
+PASS Checking place-items: self-start first baseline
+FAIL Checking place-items: self-start last baseline assert_equals: self-start last baseline specified value expected "self-start last baseline" but got ""
+PASS Checking place-items: self-end left
+PASS Checking place-items: self-end right
+PASS Checking place-items: self-end normal
+PASS Checking place-items: self-end stretch
+PASS Checking place-items: self-end start
+PASS Checking place-items: self-end end
+PASS Checking place-items: self-end self-start
+PASS Checking place-items: self-end self-end
+PASS Checking place-items: self-end center
+PASS Checking place-items: self-end flex-start
+PASS Checking place-items: self-end flex-end
+PASS Checking place-items: self-end baseline
+PASS Checking place-items: self-end first baseline
+FAIL Checking place-items: self-end last baseline assert_equals: self-end last baseline specified value expected "self-end last baseline" but got ""
+PASS Checking place-items: center left
+PASS Checking place-items: center right
+PASS Checking place-items: center normal
+PASS Checking place-items: center stretch
+PASS Checking place-items: center start
+PASS Checking place-items: center end
+PASS Checking place-items: center self-start
+PASS Checking place-items: center self-end
+PASS Checking place-items: center center
+PASS Checking place-items: center flex-start
+PASS Checking place-items: center flex-end
+PASS Checking place-items: center baseline
+PASS Checking place-items: center first baseline
+FAIL Checking place-items: center last baseline assert_equals: center last baseline specified value expected "center last baseline" but got ""
+PASS Checking place-items: flex-start left
+PASS Checking place-items: flex-start right
+PASS Checking place-items: flex-start normal
+PASS Checking place-items: flex-start stretch
+PASS Checking place-items: flex-start start
+PASS Checking place-items: flex-start end
+PASS Checking place-items: flex-start self-start
+PASS Checking place-items: flex-start self-end
+PASS Checking place-items: flex-start center
+PASS Checking place-items: flex-start flex-start
+PASS Checking place-items: flex-start flex-end
+PASS Checking place-items: flex-start baseline
+PASS Checking place-items: flex-start first baseline
+FAIL Checking place-items: flex-start last baseline assert_equals: flex-start last baseline specified value expected "flex-start last baseline" but got ""
+PASS Checking place-items: flex-end left
+PASS Checking place-items: flex-end right
+PASS Checking place-items: flex-end normal
+PASS Checking place-items: flex-end stretch
+PASS Checking place-items: flex-end start
+PASS Checking place-items: flex-end end
+PASS Checking place-items: flex-end self-start
+PASS Checking place-items: flex-end self-end
+PASS Checking place-items: flex-end center
+PASS Checking place-items: flex-end flex-start
+PASS Checking place-items: flex-end flex-end
+PASS Checking place-items: flex-end baseline
+PASS Checking place-items: flex-end first baseline
+FAIL Checking place-items: flex-end last baseline assert_equals: flex-end last baseline specified value expected "flex-end last baseline" but got ""
+PASS Checking place-items: baseline left
+PASS Checking place-items: baseline right
+PASS Checking place-items: baseline normal
+PASS Checking place-items: baseline stretch
+PASS Checking place-items: baseline start
+PASS Checking place-items: baseline end
+PASS Checking place-items: baseline self-start
+PASS Checking place-items: baseline self-end
+PASS Checking place-items: baseline center
+PASS Checking place-items: baseline flex-start
+PASS Checking place-items: baseline flex-end
+PASS Checking place-items: baseline baseline
+PASS Checking place-items: baseline first baseline
+FAIL Checking place-items: baseline last baseline assert_equals: baseline last baseline specified value expected "baseline last baseline" but got ""
+PASS Checking place-items: first baseline left
+PASS Checking place-items: first baseline right
+PASS Checking place-items: first baseline normal
+PASS Checking place-items: first baseline stretch
+PASS Checking place-items: first baseline start
+PASS Checking place-items: first baseline end
+PASS Checking place-items: first baseline self-start
+PASS Checking place-items: first baseline self-end
+PASS Checking place-items: first baseline center
+PASS Checking place-items: first baseline flex-start
+PASS Checking place-items: first baseline flex-end
+PASS Checking place-items: first baseline baseline
+PASS Checking place-items: first baseline first baseline
+FAIL Checking place-items: first baseline last baseline assert_equals: first baseline last baseline specified value expected "baseline last baseline" but got ""
+FAIL Checking place-items: last baseline left assert_equals: last baseline left specified value expected "last baseline left" but got ""
+FAIL Checking place-items: last baseline right assert_equals: last baseline right specified value expected "last baseline right" but got ""
+FAIL Checking place-items: last baseline normal assert_equals: last baseline normal specified value expected "last baseline normal" but got ""
+FAIL Checking place-items: last baseline stretch assert_equals: last baseline stretch specified value expected "last baseline stretch" but got ""
+FAIL Checking place-items: last baseline start assert_equals: last baseline start specified value expected "last baseline start" but got ""
+FAIL Checking place-items: last baseline end assert_equals: last baseline end specified value expected "last baseline end" but got ""
+FAIL Checking place-items: last baseline self-start assert_equals: last baseline self-start specified value expected "last baseline self-start" but got ""
+FAIL Checking place-items: last baseline self-end assert_equals: last baseline self-end specified value expected "last baseline self-end" but got ""
+FAIL Checking place-items: last baseline center assert_equals: last baseline center specified value expected "last baseline center" but got ""
+FAIL Checking place-items: last baseline flex-start assert_equals: last baseline flex-start specified value expected "last baseline flex-start" but got ""
+FAIL Checking place-items: last baseline flex-end assert_equals: last baseline flex-end specified value expected "last baseline flex-end" but got ""
+FAIL Checking place-items: last baseline baseline assert_equals: last baseline baseline specified value expected "last baseline baseline" but got ""
+FAIL Checking place-items: last baseline first baseline assert_equals: last baseline first baseline specified value expected "last baseline baseline" but got ""
+FAIL Checking place-items: last baseline last baseline assert_equals: last baseline last baseline specified value expected "last baseline" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/shorthand-serialization-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/shorthand-serialization-001-expected.txt
new file mode 100644
index 0000000..514f941
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/default-alignment/shorthand-serialization-001-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS test shorthand serialization {"alignContent":"center","shorthand":"center normal"}
+PASS test shorthand serialization {"alignContent":"baseline safe right","shorthand":""}
+PASS test shorthand serialization {"justifyContent":"safe start","shorthand":"normal safe start"}
+PASS test shorthand serialization {"justifyContent":"unsafe start","shorthand":["normal unsafe start"]}
+PASS test shorthand serialization {"justifyContent":"space-evenly start","shorthand":""}
+PASS test shorthand serialization {"alignContent":"start","justifyContent":"end","shorthand":"start end"}
+PASS test shorthand serialization {"alignItems":"center","shorthand":"center legacy"}
+PASS test shorthand serialization {"alignItems":"baseline","shorthand":"baseline legacy"}
+PASS test shorthand serialization {"justifyItems":"safe start","shorthand":"normal safe start"}
+PASS test shorthand serialization {"justifyItems":"unsafe start","shorthand":["normal unsafe start"]}
+PASS test shorthand serialization {"justifyItems":"stretch","shorthand":"normal stretch"}
+PASS test shorthand serialization {"justifyItems":"left legacy","shorthand":"normal legacy left"}
+PASS test shorthand serialization {"alignItems":"stretch","justifyItems":"end","shorthand":"stretch end"}
+PASS test shorthand serialization {"alignSelf":"self-end safe","shorthand":""}
+PASS test shorthand serialization {"justifySelf":"unsafe start","shorthand":["auto start","auto unsafe start"]}
+PASS test shorthand serialization {"justifySelf":"last baseline start","shorthand":""}
+FAIL test shorthand serialization {"alignSelf":"baseline","justifySelf":"last baseline","shorthand":"baseline last baseline"} assert_equals: expected "baseline last baseline" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/inheritance-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/inheritance-expected.txt
new file mode 100644
index 0000000..70099ac2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/inheritance-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS Property align-content has initial value normal
+FAIL Property align-content does not inherit assert_equals: expected "last baseline" but got "normal"
+PASS Property align-items has initial value normal
+FAIL Property align-items does not inherit assert_equals: expected "last baseline" but got "normal"
+PASS Property align-self has initial value auto
+FAIL Property align-self does not inherit assert_equals: expected "last baseline" but got "auto"
+PASS Property column-gap has initial value normal
+PASS Property column-gap does not inherit
+PASS Property justify-content has initial value normal
+PASS Property justify-content does not inherit
+PASS Property justify-items has initial value legacy center
+FAIL Property justify-items does not inherit assert_equals: expected "last baseline" but got "normal"
+PASS Property justify-self has initial value auto
+FAIL Property justify-self does not inherit assert_equals: expected "last baseline" but got "auto"
+PASS Property row-gap has initial value normal
+PASS Property row-gap does not inherit
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-computed-expected.txt
new file mode 100644
index 0000000..10c90ab
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-computed-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS Property align-content value 'normal'
+PASS Property align-content value 'baseline'
+FAIL Property align-content value 'last baseline' assert_true: 'last baseline' is a supported value for align-content. expected true got false
+PASS Property align-content value 'space-between'
+PASS Property align-content value 'space-around'
+PASS Property align-content value 'space-evenly'
+PASS Property align-content value 'stretch'
+PASS Property align-content value 'center'
+PASS Property align-content value 'start'
+PASS Property align-content value 'end'
+PASS Property align-content value 'flex-start'
+PASS Property align-content value 'flex-end'
+PASS Property align-content value 'unsafe end'
+PASS Property align-content value 'safe flex-start'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-valid-expected.txt
new file mode 100644
index 0000000..b53e2fb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-content-valid-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS e.style['align-content'] = "normal" should set the property value
+PASS e.style['align-content'] = "baseline" should set the property value
+PASS e.style['align-content'] = "first baseline" should set the property value
+FAIL e.style['align-content'] = "last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['align-content'] = "space-between" should set the property value
+PASS e.style['align-content'] = "space-around" should set the property value
+PASS e.style['align-content'] = "space-evenly" should set the property value
+PASS e.style['align-content'] = "stretch" should set the property value
+PASS e.style['align-content'] = "center" should set the property value
+PASS e.style['align-content'] = "start" should set the property value
+PASS e.style['align-content'] = "end" should set the property value
+PASS e.style['align-content'] = "flex-start" should set the property value
+PASS e.style['align-content'] = "flex-end" should set the property value
+PASS e.style['align-content'] = "unsafe end" should set the property value
+PASS e.style['align-content'] = "safe flex-start" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-computed-expected.txt
new file mode 100644
index 0000000..68fb768
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-computed-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS Property align-items value 'normal'
+PASS Property align-items value 'stretch'
+PASS Property align-items value 'baseline'
+FAIL Property align-items value 'last baseline' assert_true: 'last baseline' is a supported value for align-items. expected true got false
+PASS Property align-items value 'center'
+PASS Property align-items value 'start'
+PASS Property align-items value 'end'
+PASS Property align-items value 'self-start'
+PASS Property align-items value 'self-end'
+PASS Property align-items value 'flex-start'
+PASS Property align-items value 'flex-end'
+PASS Property align-items value 'unsafe center'
+PASS Property align-items value 'safe self-end'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-valid-expected.txt
new file mode 100644
index 0000000..87863b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-items-valid-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS e.style['align-items'] = "normal" should set the property value
+PASS e.style['align-items'] = "stretch" should set the property value
+PASS e.style['align-items'] = "baseline" should set the property value
+PASS e.style['align-items'] = "first baseline" should set the property value
+FAIL e.style['align-items'] = "last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['align-items'] = "center" should set the property value
+PASS e.style['align-items'] = "start" should set the property value
+PASS e.style['align-items'] = "end" should set the property value
+PASS e.style['align-items'] = "self-start" should set the property value
+PASS e.style['align-items'] = "self-end" should set the property value
+PASS e.style['align-items'] = "flex-start" should set the property value
+PASS e.style['align-items'] = "flex-end" should set the property value
+PASS e.style['align-items'] = "unsafe center" should set the property value
+PASS e.style['align-items'] = "safe self-end" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-computed-expected.txt
new file mode 100644
index 0000000..05c1a70e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-computed-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS Property align-self value 'auto'
+PASS Property align-self value 'normal'
+PASS Property align-self value 'stretch'
+PASS Property align-self value 'baseline'
+FAIL Property align-self value 'last baseline' assert_true: 'last baseline' is a supported value for align-self. expected true got false
+PASS Property align-self value 'center'
+PASS Property align-self value 'start'
+PASS Property align-self value 'end'
+PASS Property align-self value 'self-start'
+PASS Property align-self value 'self-end'
+PASS Property align-self value 'flex-start'
+PASS Property align-self value 'flex-end'
+PASS Property align-self value 'unsafe center'
+PASS Property align-self value 'safe self-end'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-valid-expected.txt
new file mode 100644
index 0000000..e77367f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/align-self-valid-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS e.style['align-self'] = "auto" should set the property value
+PASS e.style['align-self'] = "normal" should set the property value
+PASS e.style['align-self'] = "stretch" should set the property value
+PASS e.style['align-self'] = "baseline" should set the property value
+PASS e.style['align-self'] = "first baseline" should set the property value
+FAIL e.style['align-self'] = "last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['align-self'] = "center" should set the property value
+PASS e.style['align-self'] = "start" should set the property value
+PASS e.style['align-self'] = "end" should set the property value
+PASS e.style['align-self'] = "self-start" should set the property value
+PASS e.style['align-self'] = "self-end" should set the property value
+PASS e.style['align-self'] = "flex-start" should set the property value
+PASS e.style['align-self'] = "flex-end" should set the property value
+PASS e.style['align-self'] = "unsafe center" should set the property value
+PASS e.style['align-self'] = "safe self-end" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-computed-expected.txt
new file mode 100644
index 0000000..3cef764
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-computed-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS Property justify-items value 'normal'
+PASS Property justify-items value 'stretch'
+PASS Property justify-items value 'baseline'
+FAIL Property justify-items value 'last baseline' assert_true: 'last baseline' is a supported value for justify-items. expected true got false
+PASS Property justify-items value 'center'
+PASS Property justify-items value 'start'
+PASS Property justify-items value 'end'
+PASS Property justify-items value 'self-start'
+PASS Property justify-items value 'self-end'
+PASS Property justify-items value 'flex-start'
+PASS Property justify-items value 'flex-end'
+PASS Property justify-items value 'unsafe center'
+PASS Property justify-items value 'safe self-end'
+PASS Property justify-items value 'right'
+PASS Property justify-items value 'safe left'
+PASS Property justify-items value 'legacy'
+PASS Property justify-items value 'legacy left'
+PASS Property justify-items value 'legacy right'
+PASS Property justify-items value 'legacy center'
+PASS justify-items legacy depends on inherited value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid-expected.txt
new file mode 100644
index 0000000..74010d2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-items-valid-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS e.style['justify-items'] = "normal" should set the property value
+PASS e.style['justify-items'] = "stretch" should set the property value
+PASS e.style['justify-items'] = "baseline" should set the property value
+PASS e.style['justify-items'] = "first baseline" should set the property value
+FAIL e.style['justify-items'] = "last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['justify-items'] = "center" should set the property value
+PASS e.style['justify-items'] = "start" should set the property value
+PASS e.style['justify-items'] = "end" should set the property value
+PASS e.style['justify-items'] = "self-start" should set the property value
+PASS e.style['justify-items'] = "self-end" should set the property value
+PASS e.style['justify-items'] = "flex-start" should set the property value
+PASS e.style['justify-items'] = "flex-end" should set the property value
+PASS e.style['justify-items'] = "unsafe center" should set the property value
+PASS e.style['justify-items'] = "safe self-end" should set the property value
+PASS e.style['justify-items'] = "right" should set the property value
+PASS e.style['justify-items'] = "safe left" should set the property value
+PASS e.style['justify-items'] = "legacy" should set the property value
+PASS e.style['justify-items'] = "left legacy" should set the property value
+PASS e.style['justify-items'] = "right legacy" should set the property value
+PASS e.style['justify-items'] = "center legacy" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-computed-expected.txt
new file mode 100644
index 0000000..0d104e2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-computed-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS Property justify-self value 'auto'
+PASS Property justify-self value 'normal'
+PASS Property justify-self value 'stretch'
+PASS Property justify-self value 'baseline'
+FAIL Property justify-self value 'last baseline' assert_true: 'last baseline' is a supported value for justify-self. expected true got false
+PASS Property justify-self value 'center'
+PASS Property justify-self value 'start'
+PASS Property justify-self value 'end'
+PASS Property justify-self value 'self-start'
+PASS Property justify-self value 'self-end'
+PASS Property justify-self value 'flex-start'
+PASS Property justify-self value 'flex-end'
+PASS Property justify-self value 'unsafe center'
+PASS Property justify-self value 'safe self-end'
+PASS Property justify-self value 'left'
+PASS Property justify-self value 'unsafe right'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid-expected.txt
new file mode 100644
index 0000000..6e169140
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/justify-self-valid-expected.txt
@@ -0,0 +1,20 @@
+This is a testharness.js-based test.
+PASS e.style['justify-self'] = "auto" should set the property value
+PASS e.style['justify-self'] = "normal" should set the property value
+PASS e.style['justify-self'] = "stretch" should set the property value
+PASS e.style['justify-self'] = "baseline" should set the property value
+PASS e.style['justify-self'] = "first baseline" should set the property value
+FAIL e.style['justify-self'] = "last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['justify-self'] = "center" should set the property value
+PASS e.style['justify-self'] = "start" should set the property value
+PASS e.style['justify-self'] = "end" should set the property value
+PASS e.style['justify-self'] = "self-start" should set the property value
+PASS e.style['justify-self'] = "self-end" should set the property value
+PASS e.style['justify-self'] = "flex-start" should set the property value
+PASS e.style['justify-self'] = "flex-end" should set the property value
+PASS e.style['justify-self'] = "unsafe center" should set the property value
+PASS e.style['justify-self'] = "safe self-end" should set the property value
+PASS e.style['justify-self'] = "left" should set the property value
+PASS e.style['justify-self'] = "unsafe right" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-computed-expected.txt
new file mode 100644
index 0000000..64ce3d07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-computed-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS Property place-content value 'normal normal'
+PASS Property place-content value 'first baseline'
+PASS Property place-content value 'baseline'
+PASS Property place-content value 'first baseline start'
+FAIL Property place-content value 'last baseline' assert_true: 'last baseline' is a supported value for place-content. expected true got false
+PASS Property place-content value 'first baseline stretch'
+FAIL Property place-content value 'last baseline flex-start' assert_true: 'last baseline flex-start' is a supported value for place-content. expected true got false
+PASS Property place-content value 'baseline stretch'
+PASS Property place-content value 'space-between'
+PASS Property place-content value 'space-around'
+PASS Property place-content value 'space-evenly'
+PASS Property place-content value 'stretch'
+PASS Property place-content value 'center'
+PASS Property place-content value 'end'
+PASS Property place-content value 'flex-start flex-start'
+PASS Property place-content value 'unsafe end unsafe end'
+PASS Property place-content value 'safe flex-start'
+PASS Property place-content value 'normal stretch'
+PASS Property place-content value 'baseline space-around'
+PASS Property place-content value 'space-evenly unsafe end'
+PASS Property place-content value 'center normal'
+PASS Property place-content value 'normal right'
+PASS Property place-content value 'baseline unsafe left'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-shorthand-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-shorthand-expected.txt
new file mode 100644
index 0000000..081e3ae2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-shorthand-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS e.style['place-content'] = "normal" should set align-content
+PASS e.style['place-content'] = "normal" should set justify-content
+PASS e.style['place-content'] = "normal" should not set unrelated longhands
+PASS e.style['place-content'] = "first baseline" should set align-content
+PASS e.style['place-content'] = "first baseline" should set justify-content
+PASS e.style['place-content'] = "first baseline" should not set unrelated longhands
+FAIL e.style['place-content'] = "last baseline flex-start" should set align-content assert_equals: align-content should be canonical expected "last baseline" but got ""
+FAIL e.style['place-content'] = "last baseline flex-start" should set justify-content assert_equals: justify-content should be canonical expected "flex-start" but got ""
+FAIL e.style['place-content'] = "last baseline flex-start" should not set unrelated longhands assert_true: expected true got false
+PASS e.style['place-content'] = "space-around" should set align-content
+PASS e.style['place-content'] = "space-around" should set justify-content
+PASS e.style['place-content'] = "space-around" should not set unrelated longhands
+PASS e.style['place-content'] = "space-evenly unsafe end" should set align-content
+PASS e.style['place-content'] = "space-evenly unsafe end" should set justify-content
+PASS e.style['place-content'] = "space-evenly unsafe end" should not set unrelated longhands
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-valid-expected.txt
new file mode 100644
index 0000000..a274937
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-content-valid-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS e.style['place-content'] = "normal normal" should set the property value
+PASS e.style['place-content'] = "first baseline" should set the property value
+PASS e.style['place-content'] = "baseline" should set the property value
+PASS e.style['place-content'] = "first baseline start" should set the property value
+FAIL e.style['place-content'] = "last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['place-content'] = "first baseline stretch" should set the property value
+FAIL e.style['place-content'] = "last baseline flex-start" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['place-content'] = "baseline stretch" should set the property value
+PASS e.style['place-content'] = "space-between" should set the property value
+PASS e.style['place-content'] = "space-around" should set the property value
+PASS e.style['place-content'] = "space-evenly" should set the property value
+PASS e.style['place-content'] = "stretch" should set the property value
+PASS e.style['place-content'] = "center" should set the property value
+PASS e.style['place-content'] = "end" should set the property value
+PASS e.style['place-content'] = "flex-start flex-start" should set the property value
+PASS e.style['place-content'] = "unsafe end unsafe end" should set the property value
+PASS e.style['place-content'] = "safe flex-start" should set the property value
+PASS e.style['place-content'] = "normal stretch" should set the property value
+PASS e.style['place-content'] = "baseline space-around" should set the property value
+PASS e.style['place-content'] = "space-evenly unsafe end" should set the property value
+PASS e.style['place-content'] = "center normal" should set the property value
+PASS e.style['place-content'] = "normal right" should set the property value
+PASS e.style['place-content'] = "baseline unsafe left" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed-expected.txt
new file mode 100644
index 0000000..866e77b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS Property place-items value 'normal'
+PASS Property place-items value 'stretch stretch'
+PASS Property place-items value 'first baseline'
+FAIL Property place-items value 'last baseline last baseline' assert_true: 'last baseline last baseline' is a supported value for place-items. expected true got false
+PASS Property place-items value 'center'
+PASS Property place-items value 'end end'
+PASS Property place-items value 'self-start'
+PASS Property place-items value 'flex-end'
+PASS Property place-items value 'unsafe center unsafe center'
+PASS Property place-items value 'safe self-end'
+PASS Property place-items value 'stretch baseline'
+FAIL Property place-items value 'last baseline center' assert_true: 'last baseline center' is a supported value for place-items. expected true got false
+PASS Property place-items value 'safe self-end normal'
+PASS Property place-items value 'normal right'
+PASS Property place-items value 'baseline unsafe left'
+PASS Property place-items value 'flex-end legacy'
+PASS Property place-items value 'stretch legacy left'
+PASS Property place-items value 'first baseline right legacy'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-shorthand-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-shorthand-expected.txt
new file mode 100644
index 0000000..1200e3256
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-shorthand-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS e.style['place-items'] = "normal" should set align-items
+PASS e.style['place-items'] = "normal" should set justify-items
+PASS e.style['place-items'] = "normal" should not set unrelated longhands
+PASS e.style['place-items'] = "first baseline" should set align-items
+PASS e.style['place-items'] = "first baseline" should set justify-items
+PASS e.style['place-items'] = "first baseline" should not set unrelated longhands
+FAIL e.style['place-items'] = "last baseline flex-start" should set align-items assert_equals: align-items should be canonical expected "last baseline" but got ""
+FAIL e.style['place-items'] = "last baseline flex-start" should set justify-items assert_equals: justify-items should be canonical expected "flex-start" but got ""
+FAIL e.style['place-items'] = "last baseline flex-start" should not set unrelated longhands assert_true: expected true got false
+PASS e.style['place-items'] = "stretch right legacy" should set align-items
+PASS e.style['place-items'] = "stretch right legacy" should set justify-items
+PASS e.style['place-items'] = "stretch right legacy" should not set unrelated longhands
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-valid-expected.txt
new file mode 100644
index 0000000..bd31be6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-valid-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS e.style['place-items'] = "normal" should set the property value
+PASS e.style['place-items'] = "stretch stretch" should set the property value
+PASS e.style['place-items'] = "first baseline" should set the property value
+FAIL e.style['place-items'] = "last baseline last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['place-items'] = "center" should set the property value
+PASS e.style['place-items'] = "end end" should set the property value
+PASS e.style['place-items'] = "self-start" should set the property value
+PASS e.style['place-items'] = "flex-end" should set the property value
+PASS e.style['place-items'] = "unsafe center unsafe center" should set the property value
+PASS e.style['place-items'] = "safe self-end" should set the property value
+PASS e.style['place-items'] = "stretch baseline" should set the property value
+FAIL e.style['place-items'] = "last baseline center" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['place-items'] = "safe self-end normal" should set the property value
+PASS e.style['place-items'] = "normal right" should set the property value
+PASS e.style['place-items'] = "baseline unsafe left" should set the property value
+PASS e.style['place-items'] = "flex-end legacy" should set the property value
+PASS e.style['place-items'] = "stretch legacy left" should set the property value
+PASS e.style['place-items'] = "first baseline right legacy" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-computed-expected.txt
new file mode 100644
index 0000000..b3fd233
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-computed-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS Property place-self value 'auto auto'
+PASS Property place-self value 'normal'
+PASS Property place-self value 'stretch'
+PASS Property place-self value 'first baseline'
+FAIL Property place-self value 'last baseline last baseline' assert_true: 'last baseline last baseline' is a supported value for place-self. expected true got false
+PASS Property place-self value 'center center'
+PASS Property place-self value 'start'
+PASS Property place-self value 'self-start'
+PASS Property place-self value 'flex-end'
+PASS Property place-self value 'unsafe center'
+PASS Property place-self value 'safe self-end safe self-end'
+FAIL Property place-self value 'auto last baseline' assert_true: 'auto last baseline' is a supported value for place-self. expected true got false
+PASS Property place-self value 'baseline flex-end'
+PASS Property place-self value 'unsafe center stretch'
+PASS Property place-self value 'normal right'
+PASS Property place-self value 'baseline unsafe left'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-shorthand-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-shorthand-expected.txt
new file mode 100644
index 0000000..efc5c8ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-shorthand-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS e.style['place-self'] = "normal" should set align-self
+PASS e.style['place-self'] = "normal" should set justify-self
+PASS e.style['place-self'] = "normal" should not set unrelated longhands
+PASS e.style['place-self'] = "first baseline" should set align-self
+PASS e.style['place-self'] = "first baseline" should set justify-self
+PASS e.style['place-self'] = "first baseline" should not set unrelated longhands
+FAIL e.style['place-self'] = "last baseline flex-start" should set align-self assert_equals: align-self should be canonical expected "last baseline" but got ""
+FAIL e.style['place-self'] = "last baseline flex-start" should set justify-self assert_equals: justify-self should be canonical expected "flex-start" but got ""
+FAIL e.style['place-self'] = "last baseline flex-start" should not set unrelated longhands assert_true: expected true got false
+PASS e.style['place-self'] = "unsafe self-start stretch" should set align-self
+PASS e.style['place-self'] = "unsafe self-start stretch" should set justify-self
+PASS e.style['place-self'] = "unsafe self-start stretch" should not set unrelated longhands
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-valid-expected.txt
new file mode 100644
index 0000000..a679d87
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-self-valid-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+PASS e.style['place-self'] = "auto auto" should set the property value
+PASS e.style['place-self'] = "normal" should set the property value
+PASS e.style['place-self'] = "stretch" should set the property value
+PASS e.style['place-self'] = "first baseline" should set the property value
+FAIL e.style['place-self'] = "last baseline last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['place-self'] = "center center" should set the property value
+PASS e.style['place-self'] = "start" should set the property value
+PASS e.style['place-self'] = "self-start" should set the property value
+PASS e.style['place-self'] = "flex-end" should set the property value
+PASS e.style['place-self'] = "unsafe center" should set the property value
+PASS e.style['place-self'] = "safe self-end safe self-end" should set the property value
+FAIL e.style['place-self'] = "auto last baseline" should set the property value assert_not_equals: property should be set got disallowed value ""
+PASS e.style['place-self'] = "baseline flex-end" should set the property value
+PASS e.style['place-self'] = "unsafe center stretch" should set the property value
+PASS e.style['place-self'] = "normal right" should set the property value
+PASS e.style['place-self'] = "baseline unsafe left" should set the property value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-align-self-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-align-self-001-expected.txt
new file mode 100644
index 0000000..8d91787
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-align-self-001-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS Checking align-self: auto
+PASS Checking align-self: normal
+PASS Checking align-self: stretch
+PASS Checking align-self: start
+PASS Checking align-self: end
+PASS Checking align-self: self-start
+PASS Checking align-self: self-end
+PASS Checking align-self: center
+PASS Checking align-self: flex-start
+PASS Checking align-self: flex-end
+PASS Checking align-self: baseline
+PASS Checking align-self: first baseline
+FAIL Checking align-self: last baseline assert_equals: align-self computed style is not what is should. expected "last baseline" but got "auto"
+PASS Checking align-self: safe flex-end
+PASS Checking align-self: unsafe end
+PASS Checking align-self: safe end
+PASS Checking align-self: unsafe flex-start
+PASS Checking align-self: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-align-self-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-align-self-003-expected.txt
new file mode 100644
index 0000000..fae46cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-align-self-003-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS Checking align-self: auto
+PASS Checking align-self: normal
+PASS Checking align-self: stretch
+PASS Checking align-self: start
+PASS Checking align-self: end
+PASS Checking align-self: self-start
+PASS Checking align-self: self-end
+PASS Checking align-self: center
+PASS Checking align-self: flex-start
+PASS Checking align-self: flex-end
+PASS Checking align-self: baseline
+PASS Checking align-self: first baseline
+FAIL Checking align-self: last baseline assert_equals: align-self specified value is not what it should. expected "last baseline" but got ""
+PASS Checking align-self: safe flex-end
+PASS Checking align-self: unsafe end
+PASS Checking align-self: safe end
+PASS Checking align-self: unsafe flex-start
+PASS Checking align-self: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-justify-self-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-justify-self-001-expected.txt
new file mode 100644
index 0000000..7b7c713
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-justify-self-001-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS Checking justify-self: auto
+PASS Checking justify-self: normal
+PASS Checking justify-self: stretch
+PASS Checking justify-self: left
+PASS Checking justify-self: right
+PASS Checking justify-self: start
+PASS Checking justify-self: end
+PASS Checking justify-self: self-start
+PASS Checking justify-self: self-end
+PASS Checking justify-self: center
+PASS Checking justify-self: flex-start
+PASS Checking justify-self: flex-end
+PASS Checking justify-self: baseline
+PASS Checking justify-self: first baseline
+FAIL Checking justify-self: last baseline assert_equals: justify-self computed style is not what is should. expected "last baseline" but got "auto"
+PASS Checking justify-self: safe flex-end
+PASS Checking justify-self: unsafe end
+PASS Checking justify-self: safe end
+PASS Checking justify-self: unsafe flex-start
+PASS Checking justify-self: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-justify-self-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-justify-self-003-expected.txt
new file mode 100644
index 0000000..f96fc0f48
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/parse-justify-self-003-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS Checking justify-self: auto
+PASS Checking justify-self: normal
+PASS Checking justify-self: stretch
+PASS Checking justify-self: left
+PASS Checking justify-self: right
+PASS Checking justify-self: start
+PASS Checking justify-self: end
+PASS Checking justify-self: self-start
+PASS Checking justify-self: self-end
+PASS Checking justify-self: center
+PASS Checking justify-self: flex-start
+PASS Checking justify-self: flex-end
+PASS Checking justify-self: baseline
+PASS Checking justify-self: first baseline
+FAIL Checking justify-self: last baseline assert_equals: justify-self specified value is not what it should. expected "last baseline" but got ""
+PASS Checking justify-self: safe flex-end
+PASS Checking justify-self: unsafe end
+PASS Checking justify-self: safe end
+PASS Checking justify-self: unsafe flex-start
+PASS Checking justify-self: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-001-expected.txt
new file mode 100644
index 0000000..2072934
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-001-expected.txt
@@ -0,0 +1,21 @@
+This is a testharness.js-based test.
+PASS Checking place-self: auto
+PASS Checking place-self: normal
+PASS Checking place-self: stretch
+PASS Checking place-self: start
+PASS Checking place-self: end
+PASS Checking place-self: self-start
+PASS Checking place-self: self-end
+PASS Checking place-self: center
+PASS Checking place-self: flex-start
+PASS Checking place-self: flex-end
+PASS Checking place-self: baseline
+PASS Checking place-self: first baseline
+FAIL Checking place-self: last baseline assert_equals: align-self expanded value expected "last baseline" but got ""
+PASS Checking place-self: safe flex-end
+PASS Checking place-self: unsafe end
+PASS Checking place-self: safe end
+PASS Checking place-self: unsafe flex-start
+PASS Checking place-self: safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-002-expected.txt
new file mode 100644
index 0000000..72605c4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-002-expected.txt
@@ -0,0 +1,364 @@
+This is a testharness.js-based test.
+Found 360 tests; 323 PASS, 37 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Checking place-self: auto left
+PASS Checking place-self: auto right
+PASS Checking place-self: auto auto
+PASS Checking place-self: auto normal
+PASS Checking place-self: auto stretch
+PASS Checking place-self: auto start
+PASS Checking place-self: auto end
+PASS Checking place-self: auto self-start
+PASS Checking place-self: auto self-end
+PASS Checking place-self: auto center
+PASS Checking place-self: auto flex-start
+PASS Checking place-self: auto flex-end
+PASS Checking place-self: auto baseline
+PASS Checking place-self: auto first baseline
+FAIL Checking place-self: auto last baseline assert_equals: align-self expanded value expected "auto" but got ""
+PASS Checking place-self: auto safe flex-end
+PASS Checking place-self: auto unsafe end
+PASS Checking place-self: auto safe end
+PASS Checking place-self: auto unsafe flex-start
+PASS Checking place-self: auto safe center
+PASS Checking place-self: normal left
+PASS Checking place-self: normal right
+PASS Checking place-self: normal auto
+PASS Checking place-self: normal normal
+PASS Checking place-self: normal stretch
+PASS Checking place-self: normal start
+PASS Checking place-self: normal end
+PASS Checking place-self: normal self-start
+PASS Checking place-self: normal self-end
+PASS Checking place-self: normal center
+PASS Checking place-self: normal flex-start
+PASS Checking place-self: normal flex-end
+PASS Checking place-self: normal baseline
+PASS Checking place-self: normal first baseline
+FAIL Checking place-self: normal last baseline assert_equals: align-self expanded value expected "normal" but got ""
+PASS Checking place-self: normal safe flex-end
+PASS Checking place-self: normal unsafe end
+PASS Checking place-self: normal safe end
+PASS Checking place-self: normal unsafe flex-start
+PASS Checking place-self: normal safe center
+PASS Checking place-self: stretch left
+PASS Checking place-self: stretch right
+PASS Checking place-self: stretch auto
+PASS Checking place-self: stretch normal
+PASS Checking place-self: stretch stretch
+PASS Checking place-self: stretch start
+PASS Checking place-self: stretch end
+PASS Checking place-self: stretch self-start
+PASS Checking place-self: stretch self-end
+PASS Checking place-self: stretch center
+PASS Checking place-self: stretch flex-start
+PASS Checking place-self: stretch flex-end
+PASS Checking place-self: stretch baseline
+PASS Checking place-self: stretch first baseline
+FAIL Checking place-self: stretch last baseline assert_equals: align-self expanded value expected "stretch" but got ""
+PASS Checking place-self: stretch safe flex-end
+PASS Checking place-self: stretch unsafe end
+PASS Checking place-self: stretch safe end
+PASS Checking place-self: stretch unsafe flex-start
+PASS Checking place-self: stretch safe center
+PASS Checking place-self: start left
+PASS Checking place-self: start right
+PASS Checking place-self: start auto
+PASS Checking place-self: start normal
+PASS Checking place-self: start stretch
+PASS Checking place-self: start start
+PASS Checking place-self: start end
+PASS Checking place-self: start self-start
+PASS Checking place-self: start self-end
+PASS Checking place-self: start center
+PASS Checking place-self: start flex-start
+PASS Checking place-self: start flex-end
+PASS Checking place-self: start baseline
+PASS Checking place-self: start first baseline
+FAIL Checking place-self: start last baseline assert_equals: align-self expanded value expected "start" but got ""
+PASS Checking place-self: start safe flex-end
+PASS Checking place-self: start unsafe end
+PASS Checking place-self: start safe end
+PASS Checking place-self: start unsafe flex-start
+PASS Checking place-self: start safe center
+PASS Checking place-self: end left
+PASS Checking place-self: end right
+PASS Checking place-self: end auto
+PASS Checking place-self: end normal
+PASS Checking place-self: end stretch
+PASS Checking place-self: end start
+PASS Checking place-self: end end
+PASS Checking place-self: end self-start
+PASS Checking place-self: end self-end
+PASS Checking place-self: end center
+PASS Checking place-self: end flex-start
+PASS Checking place-self: end flex-end
+PASS Checking place-self: end baseline
+PASS Checking place-self: end first baseline
+FAIL Checking place-self: end last baseline assert_equals: align-self expanded value expected "end" but got ""
+PASS Checking place-self: end safe flex-end
+PASS Checking place-self: end unsafe end
+PASS Checking place-self: end safe end
+PASS Checking place-self: end unsafe flex-start
+PASS Checking place-self: end safe center
+PASS Checking place-self: self-start left
+PASS Checking place-self: self-start right
+PASS Checking place-self: self-start auto
+PASS Checking place-self: self-start normal
+PASS Checking place-self: self-start stretch
+PASS Checking place-self: self-start start
+PASS Checking place-self: self-start end
+PASS Checking place-self: self-start self-start
+PASS Checking place-self: self-start self-end
+PASS Checking place-self: self-start center
+PASS Checking place-self: self-start flex-start
+PASS Checking place-self: self-start flex-end
+PASS Checking place-self: self-start baseline
+PASS Checking place-self: self-start first baseline
+FAIL Checking place-self: self-start last baseline assert_equals: align-self expanded value expected "self-start" but got ""
+PASS Checking place-self: self-start safe flex-end
+PASS Checking place-self: self-start unsafe end
+PASS Checking place-self: self-start safe end
+PASS Checking place-self: self-start unsafe flex-start
+PASS Checking place-self: self-start safe center
+PASS Checking place-self: self-end left
+PASS Checking place-self: self-end right
+PASS Checking place-self: self-end auto
+PASS Checking place-self: self-end normal
+PASS Checking place-self: self-end stretch
+PASS Checking place-self: self-end start
+PASS Checking place-self: self-end end
+PASS Checking place-self: self-end self-start
+PASS Checking place-self: self-end self-end
+PASS Checking place-self: self-end center
+PASS Checking place-self: self-end flex-start
+PASS Checking place-self: self-end flex-end
+PASS Checking place-self: self-end baseline
+PASS Checking place-self: self-end first baseline
+FAIL Checking place-self: self-end last baseline assert_equals: align-self expanded value expected "self-end" but got ""
+PASS Checking place-self: self-end safe flex-end
+PASS Checking place-self: self-end unsafe end
+PASS Checking place-self: self-end safe end
+PASS Checking place-self: self-end unsafe flex-start
+PASS Checking place-self: self-end safe center
+PASS Checking place-self: center left
+PASS Checking place-self: center right
+PASS Checking place-self: center auto
+PASS Checking place-self: center normal
+PASS Checking place-self: center stretch
+PASS Checking place-self: center start
+PASS Checking place-self: center end
+PASS Checking place-self: center self-start
+PASS Checking place-self: center self-end
+PASS Checking place-self: center center
+PASS Checking place-self: center flex-start
+PASS Checking place-self: center flex-end
+PASS Checking place-self: center baseline
+PASS Checking place-self: center first baseline
+FAIL Checking place-self: center last baseline assert_equals: align-self expanded value expected "center" but got ""
+PASS Checking place-self: center safe flex-end
+PASS Checking place-self: center unsafe end
+PASS Checking place-self: center safe end
+PASS Checking place-self: center unsafe flex-start
+PASS Checking place-self: center safe center
+PASS Checking place-self: flex-start left
+PASS Checking place-self: flex-start right
+PASS Checking place-self: flex-start auto
+PASS Checking place-self: flex-start normal
+PASS Checking place-self: flex-start stretch
+PASS Checking place-self: flex-start start
+PASS Checking place-self: flex-start end
+PASS Checking place-self: flex-start self-start
+PASS Checking place-self: flex-start self-end
+PASS Checking place-self: flex-start center
+PASS Checking place-self: flex-start flex-start
+PASS Checking place-self: flex-start flex-end
+PASS Checking place-self: flex-start baseline
+PASS Checking place-self: flex-start first baseline
+FAIL Checking place-self: flex-start last baseline assert_equals: align-self expanded value expected "flex-start" but got ""
+PASS Checking place-self: flex-start safe flex-end
+PASS Checking place-self: flex-start unsafe end
+PASS Checking place-self: flex-start safe end
+PASS Checking place-self: flex-start unsafe flex-start
+PASS Checking place-self: flex-start safe center
+PASS Checking place-self: flex-end left
+PASS Checking place-self: flex-end right
+PASS Checking place-self: flex-end auto
+PASS Checking place-self: flex-end normal
+PASS Checking place-self: flex-end stretch
+PASS Checking place-self: flex-end start
+PASS Checking place-self: flex-end end
+PASS Checking place-self: flex-end self-start
+PASS Checking place-self: flex-end self-end
+PASS Checking place-self: flex-end center
+PASS Checking place-self: flex-end flex-start
+PASS Checking place-self: flex-end flex-end
+PASS Checking place-self: flex-end baseline
+PASS Checking place-self: flex-end first baseline
+FAIL Checking place-self: flex-end last baseline assert_equals: align-self expanded value expected "flex-end" but got ""
+PASS Checking place-self: flex-end safe flex-end
+PASS Checking place-self: flex-end unsafe end
+PASS Checking place-self: flex-end safe end
+PASS Checking place-self: flex-end unsafe flex-start
+PASS Checking place-self: flex-end safe center
+PASS Checking place-self: baseline left
+PASS Checking place-self: baseline right
+PASS Checking place-self: baseline auto
+PASS Checking place-self: baseline normal
+PASS Checking place-self: baseline stretch
+PASS Checking place-self: baseline start
+PASS Checking place-self: baseline end
+PASS Checking place-self: baseline self-start
+PASS Checking place-self: baseline self-end
+PASS Checking place-self: baseline center
+PASS Checking place-self: baseline flex-start
+PASS Checking place-self: baseline flex-end
+PASS Checking place-self: baseline baseline
+PASS Checking place-self: baseline first baseline
+FAIL Checking place-self: baseline last baseline assert_equals: align-self expanded value expected "baseline" but got ""
+PASS Checking place-self: baseline safe flex-end
+PASS Checking place-self: baseline unsafe end
+PASS Checking place-self: baseline safe end
+PASS Checking place-self: baseline unsafe flex-start
+PASS Checking place-self: baseline safe center
+PASS Checking place-self: first baseline left
+PASS Checking place-self: first baseline right
+PASS Checking place-self: first baseline auto
+PASS Checking place-self: first baseline normal
+PASS Checking place-self: first baseline stretch
+PASS Checking place-self: first baseline start
+PASS Checking place-self: first baseline end
+PASS Checking place-self: first baseline self-start
+PASS Checking place-self: first baseline self-end
+PASS Checking place-self: first baseline center
+PASS Checking place-self: first baseline flex-start
+PASS Checking place-self: first baseline flex-end
+PASS Checking place-self: first baseline baseline
+PASS Checking place-self: first baseline first baseline
+FAIL Checking place-self: first baseline last baseline assert_equals: align-self expanded value expected "baseline" but got ""
+PASS Checking place-self: first baseline safe flex-end
+PASS Checking place-self: first baseline unsafe end
+PASS Checking place-self: first baseline safe end
+PASS Checking place-self: first baseline unsafe flex-start
+PASS Checking place-self: first baseline safe center
+FAIL Checking place-self: last baseline left assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline right assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline auto assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline normal assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline stretch assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline start assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline end assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline self-start assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline self-end assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline center assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline flex-start assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline flex-end assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline baseline assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline first baseline assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline last baseline assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline safe flex-end assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline unsafe end assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline safe end assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline unsafe flex-start assert_equals: align-self expanded value expected "last baseline" but got ""
+FAIL Checking place-self: last baseline safe center assert_equals: align-self expanded value expected "last baseline" but got ""
+PASS Checking place-self: safe flex-end left
+PASS Checking place-self: safe flex-end right
+PASS Checking place-self: safe flex-end auto
+PASS Checking place-self: safe flex-end normal
+PASS Checking place-self: safe flex-end stretch
+PASS Checking place-self: safe flex-end start
+PASS Checking place-self: safe flex-end end
+PASS Checking place-self: safe flex-end self-start
+PASS Checking place-self: safe flex-end self-end
+PASS Checking place-self: safe flex-end center
+PASS Checking place-self: safe flex-end flex-start
+PASS Checking place-self: safe flex-end flex-end
+PASS Checking place-self: safe flex-end baseline
+PASS Checking place-self: safe flex-end first baseline
+FAIL Checking place-self: safe flex-end last baseline assert_equals: align-self expanded value expected "safe flex-end" but got ""
+PASS Checking place-self: safe flex-end safe flex-end
+PASS Checking place-self: safe flex-end unsafe end
+PASS Checking place-self: safe flex-end safe end
+PASS Checking place-self: safe flex-end unsafe flex-start
+PASS Checking place-self: safe flex-end safe center
+PASS Checking place-self: unsafe end left
+PASS Checking place-self: unsafe end right
+PASS Checking place-self: unsafe end auto
+PASS Checking place-self: unsafe end normal
+PASS Checking place-self: unsafe end stretch
+PASS Checking place-self: unsafe end start
+PASS Checking place-self: unsafe end end
+PASS Checking place-self: unsafe end self-start
+PASS Checking place-self: unsafe end self-end
+PASS Checking place-self: unsafe end center
+PASS Checking place-self: unsafe end flex-start
+PASS Checking place-self: unsafe end flex-end
+PASS Checking place-self: unsafe end baseline
+PASS Checking place-self: unsafe end first baseline
+FAIL Checking place-self: unsafe end last baseline assert_equals: align-self expanded value expected "unsafe end" but got ""
+PASS Checking place-self: unsafe end safe flex-end
+PASS Checking place-self: unsafe end unsafe end
+PASS Checking place-self: unsafe end safe end
+PASS Checking place-self: unsafe end unsafe flex-start
+PASS Checking place-self: unsafe end safe center
+PASS Checking place-self: safe end left
+PASS Checking place-self: safe end right
+PASS Checking place-self: safe end auto
+PASS Checking place-self: safe end normal
+PASS Checking place-self: safe end stretch
+PASS Checking place-self: safe end start
+PASS Checking place-self: safe end end
+PASS Checking place-self: safe end self-start
+PASS Checking place-self: safe end self-end
+PASS Checking place-self: safe end center
+PASS Checking place-self: safe end flex-start
+PASS Checking place-self: safe end flex-end
+PASS Checking place-self: safe end baseline
+PASS Checking place-self: safe end first baseline
+FAIL Checking place-self: safe end last baseline assert_equals: align-self expanded value expected "safe end" but got ""
+PASS Checking place-self: safe end safe flex-end
+PASS Checking place-self: safe end unsafe end
+PASS Checking place-self: safe end safe end
+PASS Checking place-self: safe end unsafe flex-start
+PASS Checking place-self: safe end safe center
+PASS Checking place-self: unsafe flex-start left
+PASS Checking place-self: unsafe flex-start right
+PASS Checking place-self: unsafe flex-start auto
+PASS Checking place-self: unsafe flex-start normal
+PASS Checking place-self: unsafe flex-start stretch
+PASS Checking place-self: unsafe flex-start start
+PASS Checking place-self: unsafe flex-start end
+PASS Checking place-self: unsafe flex-start self-start
+PASS Checking place-self: unsafe flex-start self-end
+PASS Checking place-self: unsafe flex-start center
+PASS Checking place-self: unsafe flex-start flex-start
+PASS Checking place-self: unsafe flex-start flex-end
+PASS Checking place-self: unsafe flex-start baseline
+PASS Checking place-self: unsafe flex-start first baseline
+FAIL Checking place-self: unsafe flex-start last baseline assert_equals: align-self expanded value expected "unsafe flex-start" but got ""
+PASS Checking place-self: unsafe flex-start safe flex-end
+PASS Checking place-self: unsafe flex-start unsafe end
+PASS Checking place-self: unsafe flex-start safe end
+PASS Checking place-self: unsafe flex-start unsafe flex-start
+PASS Checking place-self: unsafe flex-start safe center
+PASS Checking place-self: safe center left
+PASS Checking place-self: safe center right
+PASS Checking place-self: safe center auto
+PASS Checking place-self: safe center normal
+PASS Checking place-self: safe center stretch
+PASS Checking place-self: safe center start
+PASS Checking place-self: safe center end
+PASS Checking place-self: safe center self-start
+PASS Checking place-self: safe center self-end
+PASS Checking place-self: safe center center
+PASS Checking place-self: safe center flex-start
+PASS Checking place-self: safe center flex-end
+PASS Checking place-self: safe center baseline
+PASS Checking place-self: safe center first baseline
+FAIL Checking place-self: safe center last baseline assert_equals: align-self expanded value expected "safe center" but got ""
+PASS Checking place-self: safe center safe flex-end
+PASS Checking place-self: safe center unsafe end
+PASS Checking place-self: safe center safe end
+PASS Checking place-self: safe center unsafe flex-start
+PASS Checking place-self: safe center safe center
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-006-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-006-expected.txt
new file mode 100644
index 0000000..321e2557
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/self-alignment/place-self-shorthand-006-expected.txt
@@ -0,0 +1,199 @@
+This is a testharness.js-based test.
+Found 195 tests; 168 PASS, 27 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Checking place-self: auto left
+PASS Checking place-self: auto right
+PASS Checking place-self: auto auto
+PASS Checking place-self: auto normal
+PASS Checking place-self: auto stretch
+PASS Checking place-self: auto start
+PASS Checking place-self: auto end
+PASS Checking place-self: auto self-start
+PASS Checking place-self: auto self-end
+PASS Checking place-self: auto center
+PASS Checking place-self: auto flex-start
+PASS Checking place-self: auto flex-end
+PASS Checking place-self: auto baseline
+PASS Checking place-self: auto first baseline
+FAIL Checking place-self: auto last baseline assert_equals: auto last baseline specified value expected "auto last baseline" but got ""
+PASS Checking place-self: normal left
+PASS Checking place-self: normal right
+PASS Checking place-self: normal auto
+PASS Checking place-self: normal normal
+PASS Checking place-self: normal stretch
+PASS Checking place-self: normal start
+PASS Checking place-self: normal end
+PASS Checking place-self: normal self-start
+PASS Checking place-self: normal self-end
+PASS Checking place-self: normal center
+PASS Checking place-self: normal flex-start
+PASS Checking place-self: normal flex-end
+PASS Checking place-self: normal baseline
+PASS Checking place-self: normal first baseline
+FAIL Checking place-self: normal last baseline assert_equals: normal last baseline specified value expected "normal last baseline" but got ""
+PASS Checking place-self: stretch left
+PASS Checking place-self: stretch right
+PASS Checking place-self: stretch auto
+PASS Checking place-self: stretch normal
+PASS Checking place-self: stretch stretch
+PASS Checking place-self: stretch start
+PASS Checking place-self: stretch end
+PASS Checking place-self: stretch self-start
+PASS Checking place-self: stretch self-end
+PASS Checking place-self: stretch center
+PASS Checking place-self: stretch flex-start
+PASS Checking place-self: stretch flex-end
+PASS Checking place-self: stretch baseline
+PASS Checking place-self: stretch first baseline
+FAIL Checking place-self: stretch last baseline assert_equals: stretch last baseline specified value expected "stretch last baseline" but got ""
+PASS Checking place-self: start left
+PASS Checking place-self: start right
+PASS Checking place-self: start auto
+PASS Checking place-self: start normal
+PASS Checking place-self: start stretch
+PASS Checking place-self: start start
+PASS Checking place-self: start end
+PASS Checking place-self: start self-start
+PASS Checking place-self: start self-end
+PASS Checking place-self: start center
+PASS Checking place-self: start flex-start
+PASS Checking place-self: start flex-end
+PASS Checking place-self: start baseline
+PASS Checking place-self: start first baseline
+FAIL Checking place-self: start last baseline assert_equals: start last baseline specified value expected "start last baseline" but got ""
+PASS Checking place-self: end left
+PASS Checking place-self: end right
+PASS Checking place-self: end auto
+PASS Checking place-self: end normal
+PASS Checking place-self: end stretch
+PASS Checking place-self: end start
+PASS Checking place-self: end end
+PASS Checking place-self: end self-start
+PASS Checking place-self: end self-end
+PASS Checking place-self: end center
+PASS Checking place-self: end flex-start
+PASS Checking place-self: end flex-end
+PASS Checking place-self: end baseline
+PASS Checking place-self: end first baseline
+FAIL Checking place-self: end last baseline assert_equals: end last baseline specified value expected "end last baseline" but got ""
+PASS Checking place-self: self-start left
+PASS Checking place-self: self-start right
+PASS Checking place-self: self-start auto
+PASS Checking place-self: self-start normal
+PASS Checking place-self: self-start stretch
+PASS Checking place-self: self-start start
+PASS Checking place-self: self-start end
+PASS Checking place-self: self-start self-start
+PASS Checking place-self: self-start self-end
+PASS Checking place-self: self-start center
+PASS Checking place-self: self-start flex-start
+PASS Checking place-self: self-start flex-end
+PASS Checking place-self: self-start baseline
+PASS Checking place-self: self-start first baseline
+FAIL Checking place-self: self-start last baseline assert_equals: self-start last baseline specified value expected "self-start last baseline" but got ""
+PASS Checking place-self: self-end left
+PASS Checking place-self: self-end right
+PASS Checking place-self: self-end auto
+PASS Checking place-self: self-end normal
+PASS Checking place-self: self-end stretch
+PASS Checking place-self: self-end start
+PASS Checking place-self: self-end end
+PASS Checking place-self: self-end self-start
+PASS Checking place-self: self-end self-end
+PASS Checking place-self: self-end center
+PASS Checking place-self: self-end flex-start
+PASS Checking place-self: self-end flex-end
+PASS Checking place-self: self-end baseline
+PASS Checking place-self: self-end first baseline
+FAIL Checking place-self: self-end last baseline assert_equals: self-end last baseline specified value expected "self-end last baseline" but got ""
+PASS Checking place-self: center left
+PASS Checking place-self: center right
+PASS Checking place-self: center auto
+PASS Checking place-self: center normal
+PASS Checking place-self: center stretch
+PASS Checking place-self: center start
+PASS Checking place-self: center end
+PASS Checking place-self: center self-start
+PASS Checking place-self: center self-end
+PASS Checking place-self: center center
+PASS Checking place-self: center flex-start
+PASS Checking place-self: center flex-end
+PASS Checking place-self: center baseline
+PASS Checking place-self: center first baseline
+FAIL Checking place-self: center last baseline assert_equals: center last baseline specified value expected "center last baseline" but got ""
+PASS Checking place-self: flex-start left
+PASS Checking place-self: flex-start right
+PASS Checking place-self: flex-start auto
+PASS Checking place-self: flex-start normal
+PASS Checking place-self: flex-start stretch
+PASS Checking place-self: flex-start start
+PASS Checking place-self: flex-start end
+PASS Checking place-self: flex-start self-start
+PASS Checking place-self: flex-start self-end
+PASS Checking place-self: flex-start center
+PASS Checking place-self: flex-start flex-start
+PASS Checking place-self: flex-start flex-end
+PASS Checking place-self: flex-start baseline
+PASS Checking place-self: flex-start first baseline
+FAIL Checking place-self: flex-start last baseline assert_equals: flex-start last baseline specified value expected "flex-start last baseline" but got ""
+PASS Checking place-self: flex-end left
+PASS Checking place-self: flex-end right
+PASS Checking place-self: flex-end auto
+PASS Checking place-self: flex-end normal
+PASS Checking place-self: flex-end stretch
+PASS Checking place-self: flex-end start
+PASS Checking place-self: flex-end end
+PASS Checking place-self: flex-end self-start
+PASS Checking place-self: flex-end self-end
+PASS Checking place-self: flex-end center
+PASS Checking place-self: flex-end flex-start
+PASS Checking place-self: flex-end flex-end
+PASS Checking place-self: flex-end baseline
+PASS Checking place-self: flex-end first baseline
+FAIL Checking place-self: flex-end last baseline assert_equals: flex-end last baseline specified value expected "flex-end last baseline" but got ""
+PASS Checking place-self: baseline left
+PASS Checking place-self: baseline right
+PASS Checking place-self: baseline auto
+PASS Checking place-self: baseline normal
+PASS Checking place-self: baseline stretch
+PASS Checking place-self: baseline start
+PASS Checking place-self: baseline end
+PASS Checking place-self: baseline self-start
+PASS Checking place-self: baseline self-end
+PASS Checking place-self: baseline center
+PASS Checking place-self: baseline flex-start
+PASS Checking place-self: baseline flex-end
+PASS Checking place-self: baseline baseline
+PASS Checking place-self: baseline first baseline
+FAIL Checking place-self: baseline last baseline assert_equals: baseline last baseline specified value expected "baseline last baseline" but got ""
+PASS Checking place-self: first baseline left
+PASS Checking place-self: first baseline right
+PASS Checking place-self: first baseline auto
+PASS Checking place-self: first baseline normal
+PASS Checking place-self: first baseline stretch
+PASS Checking place-self: first baseline start
+PASS Checking place-self: first baseline end
+PASS Checking place-self: first baseline self-start
+PASS Checking place-self: first baseline self-end
+PASS Checking place-self: first baseline center
+PASS Checking place-self: first baseline flex-start
+PASS Checking place-self: first baseline flex-end
+PASS Checking place-self: first baseline baseline
+PASS Checking place-self: first baseline first baseline
+FAIL Checking place-self: first baseline last baseline assert_equals: first baseline last baseline specified value expected "baseline last baseline" but got ""
+FAIL Checking place-self: last baseline left assert_equals: last baseline left specified value expected "last baseline left" but got ""
+FAIL Checking place-self: last baseline right assert_equals: last baseline right specified value expected "last baseline right" but got ""
+FAIL Checking place-self: last baseline auto assert_equals: last baseline auto specified value expected "last baseline auto" but got ""
+FAIL Checking place-self: last baseline normal assert_equals: last baseline normal specified value expected "last baseline normal" but got ""
+FAIL Checking place-self: last baseline stretch assert_equals: last baseline stretch specified value expected "last baseline stretch" but got ""
+FAIL Checking place-self: last baseline start assert_equals: last baseline start specified value expected "last baseline start" but got ""
+FAIL Checking place-self: last baseline end assert_equals: last baseline end specified value expected "last baseline end" but got ""
+FAIL Checking place-self: last baseline self-start assert_equals: last baseline self-start specified value expected "last baseline self-start" but got ""
+FAIL Checking place-self: last baseline self-end assert_equals: last baseline self-end specified value expected "last baseline self-end" but got ""
+FAIL Checking place-self: last baseline center assert_equals: last baseline center specified value expected "last baseline center" but got ""
+FAIL Checking place-self: last baseline flex-start assert_equals: last baseline flex-start specified value expected "last baseline flex-start" but got ""
+FAIL Checking place-self: last baseline flex-end assert_equals: last baseline flex-end specified value expected "last baseline flex-end" but got ""
+FAIL Checking place-self: last baseline baseline assert_equals: last baseline baseline specified value expected "last baseline baseline" but got ""
+FAIL Checking place-self: last baseline first baseline assert_equals: last baseline first baseline specified value expected "last baseline baseline" but got ""
+FAIL Checking place-self: last baseline last baseline assert_equals: last baseline last baseline specified value expected "last baseline" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-004-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-004-expected.txt
index fed1565..7d26c42 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-004-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-baseline-004-expected.txt
@@ -5,47 +5,11 @@
 PASS .grid, container 4
 PASS .grid, container 5
 PASS .grid, container 6
-FAIL .grid, container 7 assert_equals: 
-<div class="container" data-expected-width="480" data-expected-height="165">
-    <div class="grid twoRows" data-offset-x="0" data-offset-y="40">
-        <div class="firstRowFirstColumn target" data-offset-x="0" data-offset-y="0"></div>
-        <div class="firstRowBothColumn" data-offset-x="0" data-offset-y="15"></div>
-        <div class="bothRowFirstColumn style2" style="align-self: last baseline" data-offset-x="0"></div>
-    </div>
-    <div class="grid twoRows" data-offset-x="160" data-offset-y="0">
-        <div class="bothRowFirstColumn target" data-offset-x="0" data-offset-y="10"></div>
-        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0"></div>
-        <div class="firstRowBothColumn" data-offset-x="0" data-offset-y="15"></div>
-    </div>
-    <div class="grid twoRows" data-offset-x="320" data-offset-y="55">
-        <div class="firstRowBothColumn target" data-offset-x="0" data-offset-y="15"></div>
-        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0"></div>
-        <div class="bothRowFirstColumn" data-offset-x="0" data-offset-y="10"></div>
-    </div>
-</div>
-offsetTop expected 40 but got 50
+PASS .grid, container 7
 PASS .grid, container 8
 PASS .grid, container 9
 PASS .grid, container 10
-FAIL .grid, container 11 assert_equals: 
-<div class="container" data-expected-width="480" data-expected-height="165">
-    <div class="grid twoRows" data-offset-x="0" data-offset-y="55">
-        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0"></div>
-        <div class="firstRowBothColumn alignSelfBaseline target" style="width: 100px; height: 20px;" data-offset-x="0" data-offset-y="15"></div>
-        <div class="bothRowFirstColumn" data-offset-x="0" data-offset-y="10"></div>
-    </div>
-    <div class="grid twoRows" data-offset-x="160" data-offset-y="40">
-        <div class="bothRowFirstColumn style2" style="align-self: last baseline" data-offset-x="0"></div>
-        <div class="firstRowFirstColumn alignSelfBaseline target" style="width: 50px; height: 50px;" data-offset-x="0" data-offset-y="0"></div>
-        <div class="firstRowBothColumn" data-offset-x="0" data-offset-y="15"></div>
-    </div>
-    <div class="grid twoRows" data-offset-x="320" data-offset-y="0">
-        <div class="firstRowBothColumn" data-offset-x="0" data-offset-y="15"></div>
-        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0"></div>
-        <div class="bothRowFirstColumn alignSelfBaseline target" style="width: 40px; height: 80px;" data-offset-x="0" data-offset-y="10"></div>
-    </div>
-</div>
-offsetTop expected 40 but got 50
+PASS .grid, container 11
 PASS .grid, container 12
 PASS .grid, container 13
 PASS .grid, container 14
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002-expected.txt b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002-expected.txt
index 3e438923..671c763 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002-expected.txt
@@ -2,7 +2,7 @@
 PASS mpadded with no attributes
 FAIL Different widths assert_approx_equals: width 0 expected 25 +/- 1 but got 100
 FAIL Different heights assert_approx_equals: height0 expected 25 +/- 1 but got 150
-FAIL Different depths assert_approx_equals: depth0 expected 25 +/- 1 but got 416
+FAIL Different depths assert_approx_equals: depth0 expected 25 +/- 1 but got 125
 FAIL Various combinations of height, depth and width. assert_approx_equals: width0 expected 25 +/- 1 but got 100
 PASS Preferred width
 PASS dynamic attributes (remove)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002.html
index 5eed04bb..420eda9 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/mpadded/mpadded-002.html
@@ -51,7 +51,7 @@
         var mpadded = getBox("depth" + i);
         assert_equals(mpadded.width, contentWidth, "width" + i);
         assert_approx_equals(getBox("baseline2").bottom - mpadded.top, contentHeight, epsilon, "height" + i);
-        assert_approx_equals(mpadded.bottom - getBox("baseline").bottom, 25*(i+1), epsilon, "depth" + i);
+        assert_approx_equals(mpadded.bottom - getBox("baseline2").bottom, 25*(i+1), epsilon, "depth" + i);
       }
     }, "Different depths");
 
@@ -60,7 +60,7 @@
         var mpadded = getBox("mpadded" + i);
         assert_approx_equals(mpadded.width, 25*(1+i%3), epsilon, "width" + i);
         assert_approx_equals(getBox("baseline2").bottom - mpadded.top, 25*(1+(i+1)%3), epsilon, "height" + i);
-        assert_approx_equals(mpadded.bottom - getBox("baseline").bottom, 25*(1+(i+2)%3), epsilon, "depth" + i);
+        assert_approx_equals(mpadded.bottom - getBox("baseline2").bottom, 25*(1+(i+2)%3), epsilon, "depth" + i);
       }
     }, "Various combinations of height, depth and width.");
 
diff --git a/third_party/blink/web_tests/external/wpt/web-share/disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/web-share/disabled-by-feature-policy.https.sub.html
new file mode 100644
index 0000000..9c18932
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-share/disabled-by-feature-policy.https.sub.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>WebShare Test: Can be disabled by feature policy</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
+  </head>
+  <body>
+    <script>
+      promise_test(async t => {
+        await test_driver.bless("web share");
+
+        await promise_rejects_dom(t, "NotAllowedError", navigator.share({}));
+      }, "share can be disabled by feature policy");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/web-share/disabled-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/web-share/disabled-by-feature-policy.https.sub.html.headers
new file mode 100644
index 0000000..a9b2b95
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-share/disabled-by-feature-policy.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: web-share 'none'
diff --git a/third_party/blink/web_tests/fast/alignment/parse-align-content-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-align-content-expected.txt
new file mode 100644
index 0000000..a4ca1e9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/alignment/parse-align-content-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+FAIL Test getting align-content values previously set through CSS. assert_equals: alignContent is not what is should. expected "last baseline" but got "normal"
+PASS Test setting invalid values to align-content through CSS.
+PASS Test initial value of align-content through JS
+FAIL Test getting and setting align-content through JS assert_equals: alignContent specified value is not what it should. expected "last baseline" but got "baseline"
+PASS Test bad combinations of align-content
+PASS Test the value 'initial'
+PASS Test the value 'initial' for grid containers
+PASS Test the value 'initial' for flex containers
+PASS Test the value 'inherit'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/alignment/parse-align-items-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-align-items-expected.txt
new file mode 100644
index 0000000..8574c56
--- /dev/null
+++ b/third_party/blink/web_tests/fast/alignment/parse-align-items-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+FAIL Test getting align-items set through CSS. assert_equals: alignItems is not what is should. expected "last baseline" but got "normal"
+PASS Test initial value of align-items through JS
+PASS Test getting and setting align-items through JS
+PASS Test bad combinations of align-items
+PASS Test the value 'initial'
+PASS Test the value 'initial' for grid containers
+PASS Test the value 'initial' for flex containers
+PASS Test the value 'inherit'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/alignment/parse-align-self-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-align-self-expected.txt
new file mode 100644
index 0000000..ce7444b
--- /dev/null
+++ b/third_party/blink/web_tests/fast/alignment/parse-align-self-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+FAIL Test getting align-self set through CSS. assert_equals: alignSelf is not what is should. expected "last baseline" but got "auto"
+PASS Test initial value of align-self through JS
+PASS Test getting and setting align-self through JS
+PASS Test 'auto' value resolution for the root node
+PASS Test bad combinations of align-self
+PASS Test the value 'initial'
+PASS Test the value 'initial' for grid containers
+PASS Test the value 'initial' for flex containers
+PASS Test the value 'initial' for positioned elements
+PASS Test the value 'initial' for positioned elements in grid containers
+PASS Test the value 'initial' for positioned elements in flex containers
+PASS Test the value 'inherit'
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/alignment/parse-justify-items-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-justify-items-expected.txt
new file mode 100644
index 0000000..30a1f0f0
--- /dev/null
+++ b/third_party/blink/web_tests/fast/alignment/parse-justify-items-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+FAIL Test getting justify-items set through CSS. assert_equals: justifyItems is not what is should. expected "last baseline" but got "normal"
+PASS Test initial value of justify-items through JS
+PASS Test getting and setting justify-items through JS
+PASS Test 'legacy' value resolution for the root node
+PASS Test bad combinations of justify-items
+PASS Test the value 'initial'
+PASS Test the value 'initial' for grid containers
+PASS Test the value 'initial' for flex containers
+PASS Test the value 'inherit'
+PASS Test the legacy alignment
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/alignment/parse-justify-self-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-justify-self-expected.txt
index 62a7a09..4448417 100644
--- a/third_party/blink/web_tests/fast/alignment/parse-justify-self-expected.txt
+++ b/third_party/blink/web_tests/fast/alignment/parse-justify-self-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 Harness Error. harness_status.status = 1 , harness_status.message = 1 duplicate test name: "Test the value 'initial' for positioned elements in grid containers"
-PASS Test getting justify-self set through CSS.
+FAIL Test getting justify-self set through CSS. assert_equals: justifySelf is not what is should. expected "last baseline" but got "auto"
 PASS Test initial value of justify-self through JS
 PASS Test getting and setting justify-self through JS
 PASS Test 'auto' value resolution for the root node
diff --git a/third_party/blink/web_tests/fast/alignment/parse-place-items-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-place-items-expected.txt
new file mode 100644
index 0000000..494a3df
--- /dev/null
+++ b/third_party/blink/web_tests/fast/alignment/parse-place-items-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'normal' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'baseline' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'first baseline' value through CSS.
+FAIL Test getting the Computed Value of place-items's longhand properties when setting 'last baseline' value through CSS. assert_equals: placeItems is not what is should. expected "last baseline" but got "normal"
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'start' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'flex-start' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'end' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'self-start' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'stretch' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'start end' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'start self-end' value through CSS.
+PASS Test getting the Computed Value of place-items's longhand properties when setting 'start baseline' value through CSS.
+PASS Test setting 'auto' as incorrect value through CSS.
+PASS Test setting 'center auto' as incorrect value through CSS.
+PASS Test setting 'none' as incorrect value through CSS.
+PASS Test setting 'safe' as incorrect value through CSS.
+PASS Test setting 'start safe' as incorrect value through CSS.
+PASS Test setting 'baseline safe' as incorrect value through CSS.
+PASS Test setting 'start end left' as incorrect value through CSS.
+PASS Test setting values through JS.
+PASS Test setting incorrect values through JS.
+PASS Test the 'initial' value of the place-items shorthand and its longhand properties' Computed value
+PASS Test the 'inherit' value of the place-items shorthand and its longhand properties' Computed value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/alignment/parse-place-self-expected.txt b/third_party/blink/web_tests/fast/alignment/parse-place-self-expected.txt
new file mode 100644
index 0000000..406ef6d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/alignment/parse-place-self-expected.txt
@@ -0,0 +1,27 @@
+This is a testharness.js-based test.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'normal' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'center auto' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'baseline' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'first baseline' value through CSS.
+FAIL Test getting the Computed Value of place-self's longhand properties when setting 'last baseline' value through CSS. assert_equals: placeSelf is not what is should. expected "last baseline" but got "auto"
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'start' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'flex-start' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'end' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'self-start' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'stretch' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'start end' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'start self-end' value through CSS.
+PASS Test getting the Computed Value of place-self's longhand properties when setting 'start baseline' value through CSS.
+PASS Test setting '' as incorrect value through CSS.
+PASS Test setting 'auto' as incorrect value through CSS.
+PASS Test setting 'none' as incorrect value through CSS.
+PASS Test setting 'safe' as incorrect value through CSS.
+PASS Test setting 'start safe' as incorrect value through CSS.
+PASS Test setting 'baseline safe' as incorrect value through CSS.
+PASS Test setting 'start end left' as incorrect value through CSS.
+PASS Test setting values through JS.
+PASS Test setting incorrect values through JS.
+PASS Test the 'initial' value of the place-self shorthand and its longhand properties' Computed value
+PASS Test the 'inherit' value of the place-self shorthand and its longhand properties' Computed value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash-expected.txt b/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash-expected.txt
index 2be2db5a..4b537e6 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash-expected.txt
@@ -19,7 +19,7 @@
 Add hash to file: protocol
 PASS a.href is 'file:///some%20path#hash%20value'
 Set hash to '#'
-PASS a.href is 'http://mydomain.com/'
+PASS a.href is 'http://mydomain.com/#'
 Add hash to non-standard protocol
 PASS a.href is 'foo:bar#hash'
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash.html b/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash.html
index 58beb77..bfe3767c 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLAnchorElement/set-href-attribute-hash.html
@@ -57,7 +57,7 @@
 debug("Set hash to '#'");
 a.href = "http://mydomain.com#middle";
 a.hash = "#";
-shouldBe("a.href", "'http://mydomain.com/'");
+shouldBe("a.href", "'http://mydomain.com/#'");
 
 // Firefox 3.5.2 does not allow setting hash to foo: scheme, and it should.
 debug("Add hash to non-standard protocol");
diff --git a/third_party/blink/web_tests/fast/domurl/url-hash.html b/third_party/blink/web_tests/fast/domurl/url-hash.html
index 5422190a..37cc0b06 100644
--- a/third_party/blink/web_tests/fast/domurl/url-hash.html
+++ b/third_party/blink/web_tests/fast/domurl/url-hash.html
@@ -20,7 +20,7 @@
 
     url.hash = '#';
     assert_equals(url.hash, '');
-    assert_equals(url.toString(), 'http://www.domain.com/');
+    assert_equals(url.toString(), 'http://www.domain.com/#');
 
     url.hash = 'a';
     assert_equals(url.hash, '#a');
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-area-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-area-expected.txt
index 20c8072..a1a06a0 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-area-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-area-expected.txt
@@ -97,6 +97,31 @@
   },
   "gridInfo": [
     {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "computedSize": 195.5,
+          "x": 97.75,
+          "y": 0
+        },
+        {
+          "computedSize": 195.5,
+          "x": 303.25,
+          "y": 0
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 295.5,
+          "x": 0,
+          "y": 147.75
+        },
+        {
+          "computedSize": 295.5,
+          "x": 0,
+          "y": 453.25
+        }
+      ],
       "rows": [
         "M",
         0,
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt
index 2d5fe683..38bb7fe1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt
@@ -98,6 +98,31 @@
   },
   "gridInfo": [
     {
+      "rotationAngle": -90,
+      "columnTrackSizes": [
+        {
+          "computedSize": 50,
+          "x": 118,
+          "y": 1173
+        },
+        {
+          "computedSize": 200,
+          "x": 118,
+          "y": 1023
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 25,
+          "x": 130.5,
+          "y": 1198
+        },
+        {
+          "computedSize": 25,
+          "x": 170.5,
+          "y": 1198
+        }
+      ],
       "rows": [
         "M",
         173,
@@ -334,6 +359,31 @@
   },
   "gridInfo": [
     {
+      "rotationAngle": -90,
+      "columnTrackSizes": [
+        {
+          "computedSize": 50,
+          "x": 228,
+          "y": 1083
+        },
+        {
+          "computedSize": 200,
+          "x": 228,
+          "y": 958
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 25,
+          "x": 240.5,
+          "y": 1108
+        },
+        {
+          "computedSize": 25,
+          "x": 265.5,
+          "y": 1108
+        }
+      ],
       "rows": [
         "M",
         228,
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-huge-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-huge-expected.txt
index 97a0d37..dbfa41f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-huge-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-huge-expected.txt
@@ -97,6 +97,1011 @@
   },
   "gridInfo": [
     {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "computedSize": 5,
+          "x": 10.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 20.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 30.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 40.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 50.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 60.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 70.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 80.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 90.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 100.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 110.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 120.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 130.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 140.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 150.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 160.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 170.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 180.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 190.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 200.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 210.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 220.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 230.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 240.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 250.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 260.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 270.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 280.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 290.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 300.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 310.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 320.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 330.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 340.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 350.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 360.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 370.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 380.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 390.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 400.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 410.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 420.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 430.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 440.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 450.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 460.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 470.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 480.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 490.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 500.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 510.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 520.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 530.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 540.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 550.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 560.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 570.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 580.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 590.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 600.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 610.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 620.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 630.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 640.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 650.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 660.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 670.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 680.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 690.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 700.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 710.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 720.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 730.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 740.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 750.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 760.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 770.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 780.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 790.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 800.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 810.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 820.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 830.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 840.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 850.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 860.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 870.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 880.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 890.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 900.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 910.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 920.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 930.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 940.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 950.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 960.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 970.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 980.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 990.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 1000.5,
+          "y": 8
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 10.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 20.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 30.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 40.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 50.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 60.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 70.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 80.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 90.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 100.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 110.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 120.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 130.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 140.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 150.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 160.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 170.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 180.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 190.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 200.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 210.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 220.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 230.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 240.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 250.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 260.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 270.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 280.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 290.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 300.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 310.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 320.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 330.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 340.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 350.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 360.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 370.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 380.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 390.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 400.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 410.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 420.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 430.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 440.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 450.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 460.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 470.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 480.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 490.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 500.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 510.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 520.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 530.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 540.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 550.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 560.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 570.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 580.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 590.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 600.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 610.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 620.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 630.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 640.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 650.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 660.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 670.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 680.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 690.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 700.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 710.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 720.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 730.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 740.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 750.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 760.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 770.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 780.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 790.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 800.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 810.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 820.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 830.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 840.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 850.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 860.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 870.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 880.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 890.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 900.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 910.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 920.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 930.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 940.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 950.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 960.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 970.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 980.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 990.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 1000.5
+        }
+      ],
       "rows": [
         "M",
         8,
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-line-names-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-line-names-expected.txt
index bc8a0d7..06a26d8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-line-names-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-line-names-expected.txt
@@ -97,6 +97,46 @@
   },
   "gridInfo": [
     {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "computedSize": 100,
+          "x": 50,
+          "y": 0
+        },
+        {
+          "computedSize": 500,
+          "x": 360,
+          "y": 0
+        },
+        {
+          "computedSize": 20,
+          "x": 630,
+          "y": 0
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 200,
+          "x": 0,
+          "y": 100
+        },
+        {
+          "computedSize": 200,
+          "x": 0,
+          "y": 310
+        },
+        {
+          "computedSize": 200,
+          "x": 0,
+          "y": 520
+        },
+        {
+          "computedSize": 200,
+          "x": 0,
+          "y": 730
+        }
+      ],
       "rows": [
         "M",
         0,
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-persistent-grid-huge-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-persistent-grid-huge-expected.txt
index de3158f..06d5e97 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-persistent-grid-huge-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-persistent-grid-huge-expected.txt
@@ -3,6 +3,1011 @@
 {
   "gridHighlights": [
     {
+      "rotationAngle": 0,
+      "columnTrackSizes": [
+        {
+          "computedSize": 5,
+          "x": 10.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 20.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 30.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 40.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 50.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 60.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 70.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 80.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 90.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 100.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 110.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 120.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 130.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 140.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 150.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 160.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 170.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 180.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 190.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 200.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 210.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 220.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 230.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 240.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 250.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 260.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 270.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 280.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 290.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 300.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 310.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 320.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 330.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 340.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 350.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 360.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 370.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 380.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 390.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 400.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 410.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 420.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 430.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 440.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 450.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 460.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 470.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 480.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 490.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 500.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 510.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 520.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 530.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 540.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 550.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 560.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 570.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 580.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 590.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 600.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 610.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 620.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 630.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 640.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 650.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 660.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 670.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 680.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 690.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 700.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 710.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 720.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 730.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 740.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 750.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 760.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 770.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 780.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 790.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 800.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 810.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 820.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 830.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 840.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 850.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 860.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 870.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 880.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 890.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 900.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 910.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 920.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 930.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 940.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 950.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 960.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 970.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 980.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 990.5,
+          "y": 8
+        },
+        {
+          "computedSize": 5,
+          "x": 1000.5,
+          "y": 8
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 10.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 20.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 30.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 40.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 50.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 60.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 70.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 80.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 90.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 100.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 110.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 120.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 130.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 140.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 150.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 160.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 170.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 180.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 190.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 200.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 210.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 220.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 230.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 240.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 250.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 260.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 270.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 280.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 290.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 300.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 310.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 320.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 330.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 340.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 350.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 360.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 370.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 380.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 390.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 400.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 410.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 420.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 430.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 440.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 450.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 460.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 470.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 480.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 490.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 500.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 510.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 520.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 530.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 540.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 550.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 560.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 570.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 580.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 590.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 600.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 610.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 620.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 630.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 640.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 650.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 660.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 670.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 680.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 690.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 700.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 710.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 720.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 730.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 740.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 750.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 760.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 770.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 780.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 790.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 800.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 810.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 820.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 830.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 840.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 850.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 860.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 870.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 880.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 890.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 900.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 910.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 920.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 930.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 940.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 950.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 960.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 970.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 980.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 990.5
+        },
+        {
+          "computedSize": 5,
+          "x": 8,
+          "y": 1000.5
+        }
+      ],
       "rows": [
         "M",
         8,
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt
index 4a3d010..9b10f34 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt
@@ -3,6 +3,31 @@
 {
   "gridHighlights": [
     {
+      "rotationAngle": -90,
+      "columnTrackSizes": [
+        {
+          "computedSize": 50,
+          "x": 118,
+          "y": 1173
+        },
+        {
+          "computedSize": 200,
+          "x": 118,
+          "y": 1023
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 25,
+          "x": 130.5,
+          "y": 1198
+        },
+        {
+          "computedSize": 25,
+          "x": 170.5,
+          "y": 1198
+        }
+      ],
       "rows": [
         "M",
         173,
@@ -140,6 +165,31 @@
       "isPrimaryGrid": true
     },
     {
+      "rotationAngle": -90,
+      "columnTrackSizes": [
+        {
+          "computedSize": 50,
+          "x": 108,
+          "y": 1183
+        },
+        {
+          "computedSize": 1200,
+          "x": 108,
+          "y": 508
+        }
+      ],
+      "rowTrackSizes": [
+        {
+          "computedSize": 100,
+          "x": 158,
+          "y": 1208
+        },
+        {
+          "computedSize": 100,
+          "x": 278,
+          "y": 1208
+        }
+      ],
       "rows": [
         "M",
         108,
diff --git a/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html b/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html
index ee744e8..0924a195 100644
--- a/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html
+++ b/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html
@@ -32,6 +32,8 @@
             assert_equals(typeof pushSubscription.endpoint, 'string');
 
             assert_idl_attribute(pushSubscription, 'expirationTime');
+            // TODO(viviy): check for not null after passing expiration time in
+            // PushMessagingManager
             assert_equals(pushSubscription.expirationTime, null);
 
             try {
diff --git a/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-zoom-hw-expected.png
index e38422a..5cf6c25 100644
--- a/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-zoom-hw-expected.png
+++ b/third_party/blink/web_tests/platform/linux/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-setters-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-setters-expected.txt
index 64230c1..73d0c36 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-setters-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-setters-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 601 tests; 311 PASS, 290 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 601 tests; 314 PASS, 287 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
 PASS <a>: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
@@ -583,9 +583,9 @@
 PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
 PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
 PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
-FAIL URL: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
-FAIL <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
-FAIL <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
+PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = '#'
+PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#'
+PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#'
 PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = ''
 PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = ''
 PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = ''
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index bb16739..e7ab00e 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
index a42e0c5..b25843b 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..d07c43a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/effect-reference-zoom-hw-expected.png
new file mode 100644
index 0000000..5dd7300
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/css3/filters/effect-reference-zoom-hw-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/cors/external/wpt/service-workers/cache-storage/serviceworker/cache-abort.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/cors/external/wpt/service-workers/cache-storage/serviceworker/cache-abort.https-expected.txt
new file mode 100644
index 0000000..5ceabd8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/cors/external/wpt/service-workers/cache-storage/serviceworker/cache-abort.https-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+PASS Cache Storage: Abort
+PASS put() on an already-aborted request should reject with AbortError
+PASS put() synchronously followed by abort should reject with AbortError
+FAIL put() followed by abort after headers received should reject with AbortError assert_unreached: Should have rejected: put should reject Reached unreachable code
+PASS add() on an already-aborted request should reject with AbortError
+PASS add() synchronously followed by abort should reject with AbortError
+FAIL add() followed by abort after headers received should reject with AbortError assert_unreached: Should have rejected: add should reject Reached unreachable code
+PASS addAll() on an already-aborted request should reject with AbortError
+PASS addAll() synchronously followed by abort should reject with AbortError
+FAIL addAll() followed by abort after headers received should reject with AbortError assert_unreached: Should have rejected: addAll should reject Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
new file mode 100644
index 0000000..c52649a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
new file mode 100644
index 0000000..c52649a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
new file mode 100644
index 0000000..6c05e045
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
new file mode 100644
index 0000000..6435d50
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
new file mode 100644
index 0000000..a42e0c5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
new file mode 100644
index 0000000..46558de
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac/css3/filters/backdrop-filter-boundary-expected.png
index d07c43a..5327b3c 100644
--- a/third_party/blink/web_tests/platform/mac/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/mac/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-setters-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-setters-expected.txt
index 64230c1..73d0c36 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-setters-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-setters-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 601 tests; 311 PASS, 290 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 601 tests; 314 PASS, 287 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
 PASS <a>: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged.
@@ -583,9 +583,9 @@
 PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
 PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
 PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
-FAIL URL: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
-FAIL <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
-FAIL <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
+PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = '#'
+PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#'
+PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#'
 PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = ''
 PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = ''
 PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = ''
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
index c52649a..f7caf7d 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
index c52649a..f7caf7d 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
index 6c05e045..049267c 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index cbe0afff..71fce49 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 6435d50..264913ccf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
index a42e0c5..b25843b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
index 46558de..f1e91c8 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png
index 7b4acc9..43b42ae 100644
--- a/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/win/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-setters-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-setters-expected.txt
index 42628966..5167caa 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-setters-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-setters-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 601 tests; 284 PASS, 317 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 601 tests; 287 PASS, 314 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 FAIL URL: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged. assert_equals: expected "a://example.net" but got "file:///A://example.net"
 FAIL <a>: Setting <a://example.net>.protocol = '' The empty string is not a valid scheme. Setter leaves the URL unchanged. assert_equals: expected "a://example.net" but got "file:///A://example.net"
@@ -583,9 +583,9 @@
 PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
 PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
 PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#main'
-FAIL URL: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
-FAIL <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
-FAIL <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#' assert_equals: expected "https://example.net/?lang=en-US#" but got "https://example.net/?lang=en-US"
+PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = '#'
+PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = '#'
+PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = '#'
 PASS URL: Setting <https://example.net?lang=en-US#nav>.hash = ''
 PASS <a>: Setting <https://example.net?lang=en-US#nav>.hash = ''
 PASS <area>: Setting <https://example.net?lang=en-US#nav>.hash = ''
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
index 71f768e..3bfc58a 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
index 71f768e..3bfc58a 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
new file mode 100644
index 0000000..9aae273
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index fa90c8d..487d226 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 5874dc2..ff3c4d1 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
index dc263a1..7621767 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
index d6b716e8..aaf28fe 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
deleted file mode 100644
index a897b29..0000000
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
index 0e5a471..ca081a6 100644
--- a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
@@ -58,5 +58,6 @@
 trust-token-redemption
 usb
 vertical-scroll
+web-share
 xr-spatial-tracking
 
diff --git a/tools/binary_size/generate_milestone_reports.py b/tools/binary_size/generate_milestone_reports.py
index be7d1b5..1d29a5c 100755
--- a/tools/binary_size/generate_milestone_reports.py
+++ b/tools/binary_size/generate_milestone_reports.py
@@ -45,8 +45,7 @@
 _PUSH_URL = 'gs://chrome-supersize/milestones/'
 
 _DESIRED_CPUS = ['arm', 'arm_64']
-# Measure Chrome.apk since it's not a bundle.
-_DESIRED_APKS = ['Monochrome.apk', 'Chrome.apk', 'AndroidWebview.apk']
+_DESIRED_APKS = ['Monochrome.apk', 'AndroidWebview.apk']
 # Versions are manually gathered from
 # https://omahaproxy.appspot.com/history?os=android&channel=stable
 _DESIRED_VERSIONS = [
@@ -73,7 +72,8 @@
     '80.0.3987.99',
     '81.0.4044.138',
     '83.0.4103.60',
-    '84.0.4147.20',  # Canary
+    '84.0.4147.89',
+    '85.0.4183.25',  # Canary
 ]
 
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e2443fcf..318935e6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4292,6 +4292,8 @@
   <int value="21" label="User navigation to a different domain in BROWSE mode"/>
   <int value="22" label="User clicked back button"/>
   <int value="23" label="User clicked back button during onboarding"/>
+  <int value="24" label="User navigatioin in RUNNING state"/>
+  <int value="25" label="The UI was closed unexpectedly"/>
 </enum>
 
 <enum name="AutofillAssistantFeatureModuleInstallation">
@@ -28607,6 +28609,7 @@
   <int value="75" label="CrossOriginIsolated"/>
   <int value="76" label="ClipboardRead"/>
   <int value="77" label="ClipboardWrite"/>
+  <int value="78" label="WebShare"/>
 </enum>
 
 <enum name="FeaturePolicyImageCompressionFormat">
@@ -41707,6 +41710,7 @@
   <int value="44240181" label="SharingQRCodeGenerator:disabled"/>
   <int value="48159177" label="reduced-referrer-granularity"/>
   <int value="51793504" label="protect-sync-credential-on-reauth:disabled"/>
+  <int value="52150780" label="OsSettingsPolymer3:disabled"/>
   <int value="52368742" label="enable-pixel-canvas-recording:disabled"/>
   <int value="54258707" label="NewTabstripAnimation:enabled"/>
   <int value="54571864" label="EnableDisplayZoomSetting:enabled"/>
@@ -42115,6 +42119,7 @@
   <int value="513258875" label="WinrtSensorsImplementation:disabled"/>
   <int value="513356954" label="InstantTethering:disabled"/>
   <int value="513372959" label="ViewsProfileChooser:enabled"/>
+  <int value="513728926" label="OsSettingsPolymer3:enabled"/>
   <int value="514569020" label="RemoteCopyImageNotification:enabled"/>
   <int value="516603570" label="QuickAnswersRichUi:disabled"/>
   <int value="517429103" label="AutofillImportDynamicForms:enabled"/>
@@ -53207,6 +53212,12 @@
   <int value="1" label="DocumentLevelTouchPreventDefaultCalled"/>
 </enum>
 
+<enum name="PasswordAccountStoreClearedOnStartup">
+  <int value="0" label="User is opted in, so no need to clear"/>
+  <int value="1" label="User is not opted in, and store was already empty"/>
+  <int value="2" label="User is not opted in, and store had to be cleared"/>
+</enum>
+
 <enum name="PasswordAddLoginSyncError">
   <int value="0" label="None"/>
   <int value="1" label="DB Not Available"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6d03f22..76776be 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -123805,6 +123805,16 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.AccountStorage.ClearedOnStartup"
+    enum="PasswordAccountStoreClearedOnStartup" expires_after="M87">
+  <owner>mamir@chromium.org</owner>
+  <owner>treib@chromium.org</owner>
+  <summary>
+    Records whether the account-scoped password storage had to be cleared during
+    profile initialization.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.AccountStorage.ClearedOptInForAllAccounts"
     units="accounts" expires_after="M87">
   <owner>mamir@chromium.org</owner>
@@ -140199,8 +140209,10 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.EvictedOriginsPerHour" units="units" expires_after="M85">
+<histogram name="Quota.EvictedOriginsPerHour" units="units"
+    expires_after="2021-07-03">
   <owner>jarrydg@chromium.org</owner>
+  <owner>chrome-owp-storage@google.com</owner>
   <summary>Number of evicted origins in an hour.</summary>
 </histogram>
 
@@ -140469,8 +140481,9 @@
   </summary>
 </histogram>
 
-<histogram name="Quota.UsageByOrigin" units="MB" expires_after="M85">
+<histogram name="Quota.UsageByOrigin" units="MB" expires_after="2021-07-03">
   <owner>jarrydg@chromium.org</owner>
+  <owner>chrome-owp-storage@google.com</owner>
   <summary>
     Disk space (in MB) currently used by an origin. Logged hourly.
   </summary>
@@ -179728,7 +179741,7 @@
 </histogram>
 
 <histogram name="Tracing.SharedBufferIsValid" enum="BooleanSuccess"
-    expires_after="M86">
+    expires_after="2021-07-17">
   <owner>eseckler@chromium.org</owner>
   <owner>tracing@chromium.org</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 0f719dc..8ae615c 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/0997f358ce073f83d6a4bba44ffce2d772077577/trace_processor_shell"
         },
         "linux": {
-            "hash": "db2aac375f29db374ad7aa1d26412d873aea0508",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/0997f358ce073f83d6a4bba44ffce2d772077577/trace_processor_shell"
+            "hash": "6d362c6c3b4aaa1f7eaab78780cc2bfa515a3a84",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/1dcc3cfc0639ebb0636c30a43e69f599f9c1a981/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 2d3528e..1c31722 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -23,7 +23,7 @@
  <item id="background_fetch_context" hash_code="16469669" type="0" content_hash_code="52235434" os_list="linux,windows" file_path="content/browser/background_fetch/background_fetch_delegate_proxy.cc"/>
  <item id="background_performance_tracer" hash_code="84575287" type="0" content_hash_code="120154250" os_list="linux,windows" file_path="chrome/browser/tracing/crash_service_uploader.cc"/>
  <item id="bidirectional_stream" hash_code="108665132" type="0" content_hash_code="130038340" os_list="linux,windows" file_path="net/http/bidirectional_stream.cc"/>
- <item id="blink_extension_resource_loader" hash_code="84165821" type="0" content_hash_code="3695143" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
+ <item id="blink_extension_resource_loader" hash_code="84165821" type="0" content_hash_code="63536185" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
  <item id="blink_resource_loader" hash_code="101845102" type="0" content_hash_code="75331172" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
  <item id="blob_read" hash_code="112303907" type="0" deprecated="2019-08-09" content_hash_code="135449692" file_path=""/>
  <item id="blob_reader" hash_code="5154306" type="0" deprecated="2018-06-14" content_hash_code="39702178" file_path=""/>
@@ -40,7 +40,7 @@
  <item id="certificate_verifier_url_loader" hash_code="80134684" type="0" content_hash_code="92630208" os_list="linux,windows" file_path="services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader.cc"/>
  <item id="certificate_verifier_url_request" hash_code="85988208" type="0" content_hash_code="59262468" os_list="linux,windows" file_path="net/cert_net/cert_net_fetcher_url_request.cc"/>
  <item id="chrome_HaTS_service" hash_code="136028241" type="0" content_hash_code="68990360" os_list="linux,windows" file_path="chrome/browser/ui/hats/hats_survey_status_checker.cc"/>
- <item id="chrome_apps_socket_api" hash_code="8591273" type="0" content_hash_code="70868355" os_list="linux,windows" file_path="extensions/browser/api/socket/socket.cc"/>
+ <item id="chrome_apps_socket_api" hash_code="8591273" type="0" content_hash_code="130709397" os_list="linux,windows" file_path="extensions/browser/api/socket/socket.cc"/>
  <item id="chrome_cleaner" hash_code="27071967" type="0" content_hash_code="111240292" os_list="windows" file_path="chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win.cc"/>
  <item id="chrome_cleanup_report" hash_code="71102679" type="0" content_hash_code="130565656" os_list="windows" file_path="chrome/chrome_cleaner/logging/cleaner_logging_service.cc"/>
  <item id="chrome_feedback_report_app" hash_code="134729048" type="0" content_hash_code="73916972" os_list="linux,windows" file_path="components/feedback/feedback_uploader.cc"/>
@@ -94,15 +94,15 @@
  <item id="download_manager_resume" hash_code="35380758" type="0" content_hash_code="41227674" os_list="linux,windows" file_path="components/download/internal/common/download_item_impl.cc"/>
  <item id="download_recovery_component" hash_code="131711536" type="0" content_hash_code="3243311" os_list="windows" file_path="chrome/chrome_cleaner/components/recovery_component.cc"/>
  <item id="download_web_contents_frame" hash_code="56351037" type="0" content_hash_code="3657889" os_list="linux,windows" file_path="content/browser/web_contents/web_contents_impl.cc"/>
- <item id="downloads_api_run_async" hash_code="121068967" type="0" content_hash_code="87443585" os_list="linux,windows" file_path="chrome/browser/extensions/api/downloads/downloads_api.cc"/>
+ <item id="downloads_api_run_async" hash_code="121068967" type="0" content_hash_code="9280914" os_list="linux,windows" file_path="chrome/browser/extensions/api/downloads/downloads_api.cc"/>
  <item id="downloads_dom_handler" hash_code="95951029" type="0" content_hash_code="137150731" os_list="linux,windows" file_path="chrome/browser/ui/webui/downloads/downloads_dom_handler.cc"/>
  <item id="drag_download_file" hash_code="95910019" type="0" content_hash_code="126492858" os_list="linux,windows" file_path="content/browser/download/drag_download_file.cc"/>
  <item id="enterprise_safe_browsing_realtime_url_lookup" hash_code="22262963" type="0" content_hash_code="14052810" os_list="linux,windows" file_path="chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service.cc"/>
  <item id="expect_ct_reporter" hash_code="57276415" type="0" deprecated="2018-06-20" content_hash_code="130492494" file_path="services/network/expect_ct_reporter.cc"/>
  <item id="extension_blacklist" hash_code="59592717" type="0" content_hash_code="116742516" os_list="linux,windows" file_path="chrome/browser/extensions/blocklist_state_fetcher.cc"/>
  <item id="extension_crx_fetcher" hash_code="21145003" type="0" content_hash_code="79150319" os_list="linux,windows" file_path="extensions/browser/updater/extension_downloader.cc"/>
- <item id="extension_install_signer" hash_code="50464499" type="0" content_hash_code="88088656" os_list="linux,windows" file_path="chrome/browser/extensions/install_signer.cc"/>
- <item id="extension_manifest_fetcher" hash_code="5151071" type="0" content_hash_code="75239211" os_list="linux,windows" file_path="extensions/browser/updater/extension_downloader.cc"/>
+ <item id="extension_install_signer" hash_code="50464499" type="0" content_hash_code="106712014" os_list="linux,windows" file_path="chrome/browser/extensions/install_signer.cc"/>
+ <item id="extension_manifest_fetcher" hash_code="5151071" type="0" content_hash_code="93862569" os_list="linux,windows" file_path="extensions/browser/updater/extension_downloader.cc"/>
  <item id="external_policy_fetcher" hash_code="9459438" type="0" content_hash_code="64260484" os_list="linux,windows" file_path="components/policy/core/common/cloud/external_policy_data_fetcher.cc"/>
  <item id="family_info" hash_code="30913825" type="0" deprecated="2019-07-30" content_hash_code="25369370" file_path=""/>
  <item id="favicon_loader" hash_code="112189210" type="0" content_hash_code="70773116" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
@@ -212,8 +212,8 @@
  <item id="payment_manifest_downloader" hash_code="84045030" type="0" content_hash_code="19293316" os_list="linux,windows" file_path="components/payments/core/payment_manifest_downloader.cc"/>
  <item id="payments_sync_cards" hash_code="95588446" type="0" content_hash_code="56526513" os_list="linux,windows" file_path="components/autofill/core/browser/payments/payments_client.cc"/>
  <item id="pdf_plugin_placeholder" hash_code="56866367" type="0" content_hash_code="16907221" os_list="linux,windows" file_path="chrome/browser/plugins/pdf_plugin_placeholder_observer.cc"/>
- <item id="pepper_tcp_socket" hash_code="120623198" type="0" content_hash_code="105652563" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_socket_utils.cc"/>
- <item id="pepper_udp_socket" hash_code="53512439" type="0" content_hash_code="85431089" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_socket_utils.cc"/>
+ <item id="pepper_tcp_socket" hash_code="120623198" type="0" content_hash_code="27489892" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_socket_utils.cc"/>
+ <item id="pepper_udp_socket" hash_code="53512439" type="0" content_hash_code="7268418" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_socket_utils.cc"/>
  <item id="per_user_topic_registration_request" hash_code="10498172" type="0" content_hash_code="57098847" os_list="linux,windows" file_path="components/invalidation/impl/per_user_topic_subscription_request.cc"/>
  <item id="permission_reporting" hash_code="131741641" type="0" deprecated="2018-03-06" content_hash_code="7213535" file_path=""/>
  <item id="permission_request_creator" hash_code="43206794" type="0" deprecated="2019-07-30" content_hash_code="73571699" file_path=""/>
@@ -335,7 +335,7 @@
  <item id="websocket_stream" hash_code="17188928" type="0" content_hash_code="7250776" os_list="linux,windows" file_path="net/websockets/websocket_stream.cc"/>
  <item id="webstore_data_fetcher" hash_code="26302604" type="0" content_hash_code="24000746" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_data_fetcher.cc"/>
  <item id="webstore_install_helper" hash_code="25921771" type="0" content_hash_code="10206361" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_install_helper.cc"/>
- <item id="webstore_installer" hash_code="18764319" type="0" content_hash_code="11030110" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_installer.cc"/>
+ <item id="webstore_installer" hash_code="18764319" type="0" content_hash_code="70871152" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_installer.cc"/>
  <item id="webui_content_scripts_download" hash_code="100545943" type="0" content_hash_code="119898059" os_list="linux,windows" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc"/>
  <item id="well_known_path_that_should_not_exist" hash_code="134618785" type="0" content_hash_code="55913167" os_list="linux,windows" file_path="chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc"/>
  <item id="worker_script_load" hash_code="72087791" type="0" content_hash_code="24889169" os_list="linux,windows" file_path="content/browser/worker_host/worker_script_fetcher.cc"/>
diff --git a/ui/base/clipboard/clipboard_ozone.cc b/ui/base/clipboard/clipboard_ozone.cc
index 12ca4cc..93db5c9a 100644
--- a/ui/base/clipboard/clipboard_ozone.cc
+++ b/ui/base/clipboard/clipboard_ozone.cc
@@ -556,7 +556,7 @@
   std::vector<uint8_t> data(text_data, text_data + text_len);
   async_clipboard_ozone_->InsertData(
       std::move(data), {kMimeTypeText, kMimeTypeX11Text, kMimeTypeX11String,
-                        kMimeTypeX11Utf8String});
+                        kMimeTypeTextUtf8, kMimeTypeX11Utf8String});
 }
 
 void ClipboardOzone::WriteHTML(const char* markup_data,
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index 1d7eaf5..2976aad 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -23,10 +23,12 @@
 
 #include "base/pickle.h"
 #include "base/run_loop.h"
+#include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -35,6 +37,8 @@
 #include "third_party/skia/include/core/SkUnPreMultiply.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/clipboard/clipboard_data_endpoint.h"
+#include "ui/base/clipboard/clipboard_dlp_controller.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/clipboard/test/clipboard_test_util.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
@@ -58,6 +62,8 @@
 
 namespace ui {
 
+class MockClipboardDlpController;
+
 template <typename ClipboardTraits>
 class ClipboardTest : public PlatformTest {
  public:
@@ -88,14 +94,40 @@
     return types;
   }
 
+  void AddDlpController() {
+    auto dlp_controller = std::make_unique<MockClipboardDlpController>();
+    dlp_controller_ = dlp_controller.get();
+    ClipboardTest::clipboard().SetClipboardDlpController(
+        std::move(dlp_controller));
+  }
+
+  MockClipboardDlpController* dlp_controller() const { return dlp_controller_; }
+
  private:
 #if defined(USE_X11)
   std::unique_ptr<PlatformEventSource> event_source_;
 #endif
   // Clipboard has a protected destructor, so scoped_ptr doesn't work here.
   Clipboard* clipboard_ = nullptr;
+
+  // MockClipboardDlpController object is owned by ClipboardTest.
+  MockClipboardDlpController* dlp_controller_ = nullptr;
 };
 
+// A mock delegate for testing.
+class MockClipboardDlpController : public ClipboardDlpController {
+ public:
+  MockClipboardDlpController();
+  ~MockClipboardDlpController();
+  MOCK_CONST_METHOD2(IsDataReadAllowed,
+                     bool(const ClipboardDataEndpoint* const data_src,
+                          const ClipboardDataEndpoint* const data_dst));
+};
+
+MockClipboardDlpController::MockClipboardDlpController() = default;
+
+MockClipboardDlpController::~MockClipboardDlpController() = default;
+
 // Hack for tests that need to call static methods of ClipboardTest.
 struct NullClipboardTraits {
   static Clipboard* Create() { return nullptr; }
@@ -1014,6 +1046,52 @@
   scw.WriteImage(SkBitmap());
 }
 
+// DLP is only intended to be used in Chrome OS, so the following DLP related
+// tests are only run on Chrome OS.
+#if defined(OS_CHROMEOS)
+// Test that copy/paste would work normally if the dlp controller didn't
+// restrict the clipboard data.
+TYPED_TEST(ClipboardTest, DlpAllowDataRead) {
+  this->AddDlpController();
+  const base::string16 kTestText(base::UTF8ToUTF16("World"));
+  {
+    ScopedClipboardWriter writer(
+        ClipboardBuffer::kCopyPaste,
+        std::make_unique<ClipboardDataEndpoint>(GURL()));
+    writer.WriteText(kTestText);
+  }
+  auto* dlp_controller = this->dlp_controller();
+  EXPECT_CALL(*dlp_controller, IsDataReadAllowed)
+      .WillRepeatedly(testing::Return(true));
+  base::string16 read_result;
+  this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+                             /* data_dst = */ nullptr, &read_result);
+  ::testing::Mock::VerifyAndClearExpectations(dlp_controller);
+  EXPECT_EQ(kTestText, read_result);
+}
+
+// Test that pasting clipboard data would not work if the dlp controller
+// restricted it.
+TYPED_TEST(ClipboardTest, DlpDisallowDataRead) {
+  this->AddDlpController();
+  const base::string16 kTestText(base::UTF8ToUTF16("World"));
+  {
+    ScopedClipboardWriter writer(
+        ClipboardBuffer::kCopyPaste,
+        std::make_unique<ClipboardDataEndpoint>(GURL()));
+    writer.WriteText(kTestText);
+  }
+  auto* dlp_controller = this->dlp_controller();
+  EXPECT_CALL(*dlp_controller, IsDataReadAllowed)
+      .WillRepeatedly(testing::Return(false));
+  base::string16 read_result;
+  this->clipboard().ReadText(ClipboardBuffer::kCopyPaste,
+                             /* data_dst = */ nullptr, &read_result);
+  ::testing::Mock::VerifyAndClearExpectations(dlp_controller);
+  EXPECT_EQ(base::string16(), read_result);
+}
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace ui
 
 #endif  // UI_BASE_CLIPBOARD_CLIPBOARD_TEST_TEMPLATE_H_
diff --git a/ui/base/clipboard/test/test_clipboard.cc b/ui/base/clipboard/test/test_clipboard.cc
index ac467227..575e44f 100644
--- a/ui/base/clipboard/test/test_clipboard.cc
+++ b/ui/base/clipboard/test/test_clipboard.cc
@@ -5,6 +5,8 @@
 #include "ui/base/clipboard/test/test_clipboard.h"
 
 #include <stddef.h>
+#include <memory>
+#include <utility>
 #include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -12,6 +14,7 @@
 #include "skia/ext/skia_utils_base.h"
 #include "ui/base/clipboard/clipboard_constants.h"
 #include "ui/base/clipboard/clipboard_data_endpoint.h"
+#include "ui/base/clipboard/clipboard_dlp_controller.h"
 #include "ui/base/clipboard/clipboard_monitor.h"
 
 namespace ui {
@@ -39,15 +42,18 @@
   return GetStore(buffer).sequence_number;
 }
 
-// TODO(crbug.com/1103215): Setting |dlp_controller| should be supported.
 void TestClipboard::SetClipboardDlpController(
-    std::unique_ptr<ClipboardDlpController> dlp_controller) {}
+    std::unique_ptr<ClipboardDlpController> dlp_controller) {
+  dlp_controller_ = std::move(dlp_controller);
+}
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 bool TestClipboard::IsFormatAvailable(
     const ClipboardFormatType& format,
     ClipboardBuffer buffer,
     const ui::ClipboardDataEndpoint* data_dst) const {
+  if (dlp_controller_ && !dlp_controller_->IsDataReadAllowed(
+                             GetStore(buffer).data_src.get(), data_dst))
+    return false;
 #if defined(OS_LINUX)
   // The linux clipboard treats the presence of text on the clipboard
   // as the url format being available.
@@ -63,13 +69,15 @@
   GetStore(buffer).Clear();
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 void TestClipboard::ReadAvailableTypes(
     ClipboardBuffer buffer,
     const ClipboardDataEndpoint* data_dst,
     std::vector<base::string16>* types) const {
   DCHECK(types);
   types->clear();
+  if (dlp_controller_ && !dlp_controller_->IsDataReadAllowed(
+                             GetStore(buffer).data_src.get(), data_dst))
+    return;
 
   if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer,
                         data_dst))
@@ -83,12 +91,16 @@
     types->push_back(base::UTF8ToUTF16(kMimeTypePNG));
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 std::vector<base::string16>
 TestClipboard::ReadAvailablePlatformSpecificFormatNames(
     ClipboardBuffer buffer,
     const ui::ClipboardDataEndpoint* data_dst) const {
-  const auto& data = GetStore(buffer).data;
+  const DataStore& store = GetStore(buffer);
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return {};
+
+  const auto& data = store.data;
   std::vector<base::string16> types;
   types.reserve(data.size());
   for (const auto& it : data)
@@ -113,10 +125,13 @@
   return types;
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 void TestClipboard::ReadText(ClipboardBuffer buffer,
                              const ClipboardDataEndpoint* data_dst,
                              base::string16* result) const {
+  if (dlp_controller_ && !dlp_controller_->IsDataReadAllowed(
+                             GetStore(buffer).data_src.get(), data_dst))
+    return;
+
   std::string result8;
   ReadAsciiText(buffer, data_dst, &result8);
   *result = base::UTF8ToUTF16(result8);
@@ -126,23 +141,30 @@
 void TestClipboard::ReadAsciiText(ClipboardBuffer buffer,
                                   const ClipboardDataEndpoint* data_dst,
                                   std::string* result) const {
-  result->clear();
   const DataStore& store = GetStore(buffer);
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return;
+
+  result->clear();
   auto it = store.data.find(ClipboardFormatType::GetPlainTextType());
   if (it != store.data.end())
     *result = it->second;
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 void TestClipboard::ReadHTML(ClipboardBuffer buffer,
                              const ClipboardDataEndpoint* data_dst,
                              base::string16* markup,
                              std::string* src_url,
                              uint32_t* fragment_start,
                              uint32_t* fragment_end) const {
+  const DataStore& store = GetStore(buffer);
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return;
+
   markup->clear();
   src_url->clear();
-  const DataStore& store = GetStore(buffer);
   auto it = store.data.find(ClipboardFormatType::GetHtmlType());
   if (it != store.data.end())
     *markup = base::UTF8ToUTF16(it->second);
@@ -151,18 +173,20 @@
   *fragment_end = base::checked_cast<uint32_t>(markup->size());
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 void TestClipboard::ReadRTF(ClipboardBuffer buffer,
                             const ClipboardDataEndpoint* data_dst,
                             std::string* result) const {
-  result->clear();
   const DataStore& store = GetStore(buffer);
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return;
+
+  result->clear();
   auto it = store.data.find(ClipboardFormatType::GetRtfType());
   if (it != store.data.end())
     *result = it->second;
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 void TestClipboard::ReadImage(ClipboardBuffer buffer,
                               const ClipboardDataEndpoint* data_dst,
                               ReadImageCallback callback) const {
@@ -180,6 +204,10 @@
                                  base::string16* title,
                                  std::string* url) const {
   const DataStore& store = GetDefaultStore();
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return;
+
   if (url) {
     auto it = store.data.find(ClipboardFormatType::GetUrlType());
     if (it != store.data.end())
@@ -189,12 +217,15 @@
     *title = base::UTF8ToUTF16(store.url_title);
 }
 
-// TODO(crbug.com/1103215): |data_dst| should be supported.
 void TestClipboard::ReadData(const ClipboardFormatType& format,
                              const ClipboardDataEndpoint* data_dst,
                              std::string* result) const {
-  result->clear();
   const DataStore& store = GetDefaultStore();
+  if (dlp_controller_ &&
+      !dlp_controller_->IsDataReadAllowed(store.data_src.get(), data_dst))
+    return;
+
+  result->clear();
   auto it = store.data.find(format);
   if (it != store.data.end())
     *result = it->second;
@@ -208,7 +239,6 @@
   last_modified_time_ = base::Time();
 }
 
-// TODO(crbug.com/1103215): |data_src| should be supported
 void TestClipboard::WritePortableRepresentations(
     ClipboardBuffer buffer,
     const ObjectMap& objects,
@@ -218,9 +248,9 @@
   for (const auto& kv : objects)
     DispatchPortableRepresentation(kv.first, kv.second);
   default_store_buffer_ = ClipboardBuffer::kCopyPaste;
+  GetStore(buffer).SetDataSource(std::move(data_src));
 }
 
-// TODO(crbug.com/1103215): |data_src| should be supported
 void TestClipboard::WritePlatformRepresentations(
     ClipboardBuffer buffer,
     std::vector<Clipboard::PlatformRepresentation> platform_representations,
@@ -229,6 +259,7 @@
   default_store_buffer_ = buffer;
   DispatchPlatformRepresentations(std::move(platform_representations));
   default_store_buffer_ = ClipboardBuffer::kCopyPaste;
+  GetStore(buffer).SetDataSource(std::move(data_src));
 }
 
 void TestClipboard::WriteText(const char* text_data, size_t text_len) {
@@ -292,9 +323,31 @@
   GetDefaultStore().data[format] = std::string(data_data, data_len);
 }
 
-TestClipboard::DataStore::DataStore() : sequence_number(0) {}
+TestClipboard::DataStore::DataStore() = default;
 
-TestClipboard::DataStore::DataStore(const DataStore& other) = default;
+TestClipboard::DataStore::DataStore(const DataStore& other) {
+  sequence_number = other.sequence_number;
+  data = other.data;
+  url_title = other.url_title;
+  html_src_url = other.html_src_url;
+  image = other.image;
+  data_src = other.data_src ? std::make_unique<ClipboardDataEndpoint>(
+                                  ClipboardDataEndpoint(*(other.data_src)))
+                            : nullptr;
+}
+
+TestClipboard::DataStore& TestClipboard::DataStore::operator=(
+    const DataStore& other) {
+  sequence_number = other.sequence_number;
+  data = other.data;
+  url_title = other.url_title;
+  html_src_url = other.html_src_url;
+  image = other.image;
+  data_src = other.data_src ? std::make_unique<ClipboardDataEndpoint>(
+                                  ClipboardDataEndpoint(*(other.data_src)))
+                            : nullptr;
+  return *this;
+}
 
 TestClipboard::DataStore::~DataStore() = default;
 
@@ -305,6 +358,11 @@
   image = SkBitmap();
 }
 
+void TestClipboard::DataStore::SetDataSource(
+    std::unique_ptr<ClipboardDataEndpoint> data_src) {
+  this->data_src = std::move(data_src);
+}
+
 const TestClipboard::DataStore& TestClipboard::GetStore(
     ClipboardBuffer buffer) const {
   CHECK(IsSupportedClipboardBuffer(buffer));
diff --git a/ui/base/clipboard/test/test_clipboard.h b/ui/base/clipboard/test/test_clipboard.h
index a90d1846..83324202 100644
--- a/ui/base/clipboard/test/test_clipboard.h
+++ b/ui/base/clipboard/test/test_clipboard.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -105,13 +106,16 @@
   struct DataStore {
     DataStore();
     DataStore(const DataStore& other);
+    DataStore& operator=(const DataStore& other);
     ~DataStore();
     void Clear();
-    uint64_t sequence_number;
+    void SetDataSource(std::unique_ptr<ClipboardDataEndpoint> data_src);
+    uint64_t sequence_number = 0;
     base::flat_map<ClipboardFormatType, std::string> data;
     std::string url_title;
     std::string html_src_url;
     SkBitmap image;
+    std::unique_ptr<ClipboardDataEndpoint> data_src = nullptr;
   };
 
   // The non-const versions increment the sequence number as a side effect.
@@ -124,6 +128,8 @@
   mutable base::flat_map<ClipboardBuffer, DataStore> stores_;
   base::Time last_modified_time_;
 
+  std::unique_ptr<ClipboardDlpController> dlp_controller_;
+
   DISALLOW_COPY_AND_ASSIGN(TestClipboard);
 };
 
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index b260b1d..c8784d1 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -738,11 +738,13 @@
   <message name="IDS_FILE_BROWSER_COPY_ITEMS_REMAINING_LONG" desc="File Manager status message including destination folder when copying multiple files or folders. 'Item' is used here as a generic term for file or directory.">
     Copying <ph name="NUMBER_OF_ITEMS">$1<ex>3</ex></ph> items to <ph name="FOLDER_NAME">$2<ex>images</ex></ph>
   </message>
-  <message name="IDS_FILE_BROWSER_COPY_TIME_REMAINING_ESTIMATE" desc="File Manager remaining time message when copying or moving files.">
-   About <ph name="REMAINING_TIME">$1<ex>2 min</ex></ph>
+  <message name="IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE"
+           desc="The progress panel message indicating estimate of remaining time of a file operation. The time will be in 1 part (hour or minute) like '2 hours' or '3 min'.">
+    About <ph name="REMAINING_TIME">$1<ex>1 min</ex></ph> remaining
   </message>
-  <message name="IDS_FILE_BROWSER_COPY_TIME_REMAINING_CLOSE" desc="File Manager close to finish remaining time message when copying or moving files.">
-   Less than <ph name="REMAINING_TIME">$1<ex>1 min</ex></ph>
+  <message name="IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE_2"
+           desc="The progress panel message indicating estimate of remaining time of a file operation. The time will be in 2 parts and has both hour and minute like '2 hours 1 min'.">
+    About <ph name="REMAINING_TIME_HOUR">$1<ex>2 hours</ex></ph> <ph name="REMAINING_TIME_MINUTE">$2<ex>3 min</ex></ph> remaining
   </message>
   <message name="IDS_FILE_BROWSER_COPY_TARGET_EXISTS_ERROR" desc="File Manager error message. 'Item' is used here as a generic term for file or directory.">
     Whoops, <ph name="FILE_NAME">$1<ex>movie.avi</ex></ph> already exists. Rename it and try again.
@@ -889,6 +891,15 @@
   <message name="IDS_FILE_BROWSER_UPLOAD_LABEL" desc="Upload label.">
     Upload
   </message>
+  <message name="IDS_FILE_BROWSER_COMPLETE_LABEL" desc="Complete label.">
+    Complete
+  </message>
+  <message name="IDS_FILE_BROWSER_DISMISS_LABEL" desc="Dismiss label.">
+    Dismiss
+  </message>
+  <message name="IDS_FILE_BROWSER_PENDING_LABEL" desc="Pending label.">
+    Pending
+  </message>
   <message name="IDS_FILE_BROWSER_SEARCH_TEXT_LABEL" desc="Search text field label.">
     Search
   </message>
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COMPLETE_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COMPLETE_LABEL.png.sha1
new file mode 100644
index 0000000..8625aef
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COMPLETE_LABEL.png.sha1
@@ -0,0 +1 @@
+cb6e7057bf647912d8cb17b3d518b8883592579a
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COPY_TIME_REMAINING_CLOSE.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COPY_TIME_REMAINING_CLOSE.png.sha1
deleted file mode 100644
index 3fffbc44..0000000
--- a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COPY_TIME_REMAINING_CLOSE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ae92434fea771fbf80149dfb68e3dc09f7ca5826
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COPY_TIME_REMAINING_ESTIMATE.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COPY_TIME_REMAINING_ESTIMATE.png.sha1
deleted file mode 100644
index 7855a46..0000000
--- a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_COPY_TIME_REMAINING_ESTIMATE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-65421e7c1dc4d4d785c2171cef8349b07087febf
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_DISMISS_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_DISMISS_LABEL.png.sha1
new file mode 100644
index 0000000..8625aef
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_DISMISS_LABEL.png.sha1
@@ -0,0 +1 @@
+cb6e7057bf647912d8cb17b3d518b8883592579a
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_PENDING_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_PENDING_LABEL.png.sha1
new file mode 100644
index 0000000..5296d73
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_PENDING_LABEL.png.sha1
@@ -0,0 +1 @@
+83bbff6f4484cf333764bc7c7c2f5d60f6ef7cc5
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE.png.sha1
new file mode 100644
index 0000000..f738ec93f
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE.png.sha1
@@ -0,0 +1 @@
+39dc258a62e6020b22b66fd662e7b4a23a230ed2
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE_2.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE_2.png.sha1
new file mode 100644
index 0000000..7b9bb41
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_TIME_REMAINING_ESTIMATE_2.png.sha1
@@ -0,0 +1 @@
+7e0a467a93244d07c548e6ea291f5bc29a0ff83d
\ No newline at end of file
diff --git a/ui/views/examples/BUILD.gn b/ui/views/examples/BUILD.gn
index ba6f3d7..90d16781 100644
--- a/ui/views/examples/BUILD.gn
+++ b/ui/views/examples/BUILD.gn
@@ -169,6 +169,13 @@
   if (use_x11) {
     deps += [ "//ui/gfx/x" ]
   }
+  if (is_chromeos) {
+    sources += [
+      "examples_views_delegate_chromeos.cc",
+      "examples_views_delegate_chromeos.h",
+    ]
+    deps += [ "//ui/wm:test_support" ]
+  }
 }
 
 executable("views_examples") {
diff --git a/ui/views/examples/DEPS b/ui/views/examples/DEPS
index cf9717c4..c45f382 100644
--- a/ui/views/examples/DEPS
+++ b/ui/views/examples/DEPS
@@ -11,3 +11,12 @@
   "+ui/snapshot", # Enable Skia Gold testing
   "+ui/views_content_client",
 ]
+
+specific_include_rules = {
+  "examples_main_proc.cc": [
+    "+ui/wm/core/wm_state.h",
+  ],
+  "examples_views_delegate_chromeos.cc": [
+    "+ui/wm/test/wm_test_helper.h",
+  ],
+}
diff --git a/ui/views/examples/examples_main_proc.cc b/ui/views/examples/examples_main_proc.cc
index f800840..9c27678 100644
--- a/ui/views/examples/examples_main_proc.cc
+++ b/ui/views/examples/examples_main_proc.cc
@@ -52,6 +52,10 @@
 #include "ui/wm/core/wm_state.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "ui/views/examples/examples_views_delegate_chromeos.h"
+#endif
+
 #if BUILDFLAG(ENABLE_DESKTOP_AURA)
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #endif
@@ -154,10 +158,14 @@
   ExamplesExitCode compare_result = ExamplesExitCode::kSucceeded;
 
   {
+#if defined(OS_CHROMEOS)
+    ExamplesViewsDelegateChromeOS views_delegate;
+#else
     views::DesktopTestViewsDelegate views_delegate;
 #if defined(USE_AURA)
     wm::WMState wm_state;
 #endif
+#endif
 #if BUILDFLAG(ENABLE_DESKTOP_AURA)
     std::unique_ptr<display::Screen> desktop_screen =
         base::WrapUnique(views::CreateDesktopScreen());
diff --git a/ui/views/examples/examples_views_delegate_chromeos.cc b/ui/views/examples/examples_views_delegate_chromeos.cc
new file mode 100644
index 0000000..cc3365f
--- /dev/null
+++ b/ui/views/examples/examples_views_delegate_chromeos.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 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 "ui/views/examples/examples_views_delegate_chromeos.h"
+
+#include "ui/views/examples/examples_window.h"
+#include "ui/wm/test/wm_test_helper.h"
+
+namespace views {
+namespace examples {
+
+namespace {
+constexpr gfx::Size kDefaultSize(1024, 768);
+}  // namespace
+
+ExamplesViewsDelegateChromeOS::ExamplesViewsDelegateChromeOS()
+    : observer_(this) {}
+
+ExamplesViewsDelegateChromeOS::~ExamplesViewsDelegateChromeOS() = default;
+
+void ExamplesViewsDelegateChromeOS::OnBeforeWidgetInit(
+    Widget::InitParams* params,
+    internal::NativeWidgetDelegate* delegate) {
+  views::TestViewsDelegate::OnBeforeWidgetInit(params, delegate);
+  if (!params->parent && !params->context) {
+    DCHECK(!wm_helper_);
+
+    wm_helper_ = std::make_unique<wm::WMTestHelper>(kDefaultSize);
+    wm_helper_->host()->Show();
+    observer_.Add(wm_helper_->host());
+    params->context = wm_helper_->host()->window();
+  }
+}
+
+void ExamplesViewsDelegateChromeOS::OnHostCloseRequested(
+    aura::WindowTreeHost* host) {
+  Widget* widget = GetExamplesWidget();
+  if (widget) {
+    observer_.Remove(host);
+    widget->Close();
+  }
+}
+
+}  // namespace examples
+}  // namespace views
diff --git a/ui/views/examples/examples_views_delegate_chromeos.h b/ui/views/examples/examples_views_delegate_chromeos.h
new file mode 100644
index 0000000..1bba04f
--- /dev/null
+++ b/ui/views/examples/examples_views_delegate_chromeos.h
@@ -0,0 +1,43 @@
+// Copyright (c) 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 UI_VIEWS_EXAMPLES_EXAMPLES_VIEWS_DELEGATE_CHROMEOS_H_
+#define UI_VIEWS_EXAMPLES_EXAMPLES_VIEWS_DELEGATE_CHROMEOS_H_
+
+#include <memory>
+
+#include "base/scoped_observer.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/aura/window_tree_host_observer.h"
+#include "ui/views/test/desktop_test_views_delegate.h"
+
+namespace wm {
+class WMTestHelper;
+}
+
+namespace views {
+namespace examples {
+
+class ExamplesViewsDelegateChromeOS : public DesktopTestViewsDelegate,
+                                      public aura::WindowTreeHostObserver {
+ public:
+  ExamplesViewsDelegateChromeOS();
+  ~ExamplesViewsDelegateChromeOS() override;
+
+ private:
+  // ViewsDelegate:
+  void OnBeforeWidgetInit(Widget::InitParams* params,
+                          internal::NativeWidgetDelegate* delegate) override;
+
+  // aura::WindowTreeHostObserver:
+  void OnHostCloseRequested(aura::WindowTreeHost* host) override;
+
+  ScopedObserver<aura::WindowTreeHost, aura::WindowTreeHostObserver> observer_;
+  std::unique_ptr<wm::WMTestHelper> wm_helper_;
+};
+
+}  // namespace examples
+}  // namespace views
+
+#endif  // UI_VIEWS_EXAMPLES_EXAMPLES_VIEWS_DELEGATE_CHROMEOS_H_
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc
index c8cb0f2..8b0a5ae3 100644
--- a/ui/views/examples/examples_window.cc
+++ b/ui/views/examples/examples_window.cc
@@ -207,6 +207,12 @@
 // static
 ExamplesWindowContents* ExamplesWindowContents::instance_ = nullptr;
 
+Widget* GetExamplesWidget() {
+  return ExamplesWindowContents::instance()
+             ? ExamplesWindowContents::instance()->GetWidget()
+             : nullptr;
+}
+
 void ShowExamplesWindow(base::OnceClosure on_close,
                         ExampleVector examples,
                         gfx::NativeWindow window_context) {
diff --git a/ui/views/examples/examples_window.h b/ui/views/examples/examples_window.h
index 93cff09..05aa540 100644
--- a/ui/views/examples/examples_window.h
+++ b/ui/views/examples/examples_window.h
@@ -17,10 +17,15 @@
 #include "ui/views/examples/views_examples_export.h"
 
 namespace views {
+class Widget;
+
 namespace examples {
 
 VIEWS_EXAMPLES_EXPORT extern const char kExamplesWidgetName[];
 
+// Returns the current widget.
+VIEWS_EXAMPLES_EXPORT Widget* GetExamplesWidget();
+
 // Shows a window with the views examples in it. |extra_examples| contains any
 // additional examples to add. |window_context| is used to determine where the
 // window should be created (see |Widget::InitParams::context| for details).
diff --git a/ui/views_content_client/views_content_client_main_parts_aura.cc b/ui/views_content_client/views_content_client_main_parts_aura.cc
index d540197..bbda2594 100644
--- a/ui/views_content_client/views_content_client_main_parts_aura.cc
+++ b/ui/views_content_client/views_content_client_main_parts_aura.cc
@@ -20,7 +20,9 @@
 void ViewsContentClientMainPartsAura::ToolkitInitialized() {
   ViewsContentClientMainParts::ToolkitInitialized();
 
+#if !defined(OS_CHROMEOS)
   wm_state_ = std::make_unique<::wm::WMState>();
+#endif
 }
 
 void ViewsContentClientMainPartsAura::PostMainMessageLoopRun() {
diff --git a/ui/views_content_client/views_content_client_main_parts_chromeos.cc b/ui/views_content_client/views_content_client_main_parts_chromeos.cc
index c1cdf28..5c74f13 100644
--- a/ui/views_content_client/views_content_client_main_parts_chromeos.cc
+++ b/ui/views_content_client/views_content_client_main_parts_chromeos.cc
@@ -43,7 +43,7 @@
   ViewsContentClientMainPartsAura::PreMainMessageLoopRun();
 
   // Set up basic pieces of views::corewm.
-  wm_test_helper_ = std::make_unique<wm::WMTestHelper>(gfx::Size(800, 600));
+  wm_test_helper_ = std::make_unique<wm::WMTestHelper>(gfx::Size(1024, 768));
   // Ensure the X window gets mapped.
   wm_test_helper_->host()->Show();
 
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index 8ef610ae..80b5baf9 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -88,6 +88,7 @@
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit:junit",
+    "//ui/android:ui_java_test_support",
     "//weblayer/public/java",
     "//weblayer/public/javatestutil:test_java",
     "//weblayer/shell/android:weblayer_shell_java",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java
index 2cd16e05..387b851 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaCaptureTest.java
@@ -23,10 +23,12 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.UiDisableIf;
 import org.chromium.weblayer.MediaCaptureCallback;
 import org.chromium.weblayer.TestWebLayer;
 import org.chromium.weblayer.shell.InstrumentationActivity;
@@ -86,6 +88,7 @@
      */
     @Test
     @MediumTest
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380
     public void testMediaCapture_basic() throws Throwable {
         mActivityTestRule.navigateAndWait(
                 mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia.html"));
@@ -115,6 +118,7 @@
      */
     @Test
     @MediumTest
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380
     public void testMediaCapture_rememberPermission() throws Throwable {
         mActivityTestRule.navigateAndWait(
                 mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia.html"));
@@ -144,6 +148,7 @@
      */
     @Test
     @MediumTest
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380
     public void testMediaCapture_twoStreams() throws Throwable {
         mActivityTestRule.navigateAndWait(
                 mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia2.html"));
@@ -172,6 +177,7 @@
     @Test
     @MediumTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.M)
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1107380
     public void testMediaCapture_twoStreamsNotification() throws Throwable {
         mActivityTestRule.navigateAndWait(
                 mActivityTestRule.getTestServer().getURL("/weblayer/test/data/getusermedia2.html"));
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index 4ae7060..4d80beb 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -371,6 +371,7 @@
     BrowserContextImpl* browser_context_impl =
         static_cast<BrowserContextImpl*>(browser_context);
     bool is_real_time_lookup_enabled =
+        !GetSafeBrowsingService()->GetSafeBrowsingDisabled() &&
         safe_browsing::RealTimePolicyEngine::CanPerformFullURLLookup(
             browser_context_impl->pref_service(),
             browser_context_impl->IsOffTheRecord(),
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.cc b/weblayer/browser/safe_browsing/safe_browsing_service.cc
index 3b9cff4..328f00d 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.cc
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.cc
@@ -244,4 +244,8 @@
   return network_context_->GetURLLoaderFactory();
 }
 
+bool SafeBrowsingService::GetSafeBrowsingDisabled() {
+  return safe_browsing_disabled_;
+}
+
 }  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.h b/weblayer/browser/safe_browsing/safe_browsing_service.h
index be6f1b8..668cb05 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.h
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.h
@@ -58,6 +58,7 @@
                     content::RenderProcessHost* render_process_host);
   void StopDBManager();
   void SetSafeBrowsingDisabled(bool disabled);
+  bool GetSafeBrowsingDisabled();
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
 
  private:
diff --git a/weblayer/public/java/org/chromium/weblayer/SettingType.java b/weblayer/public/java/org/chromium/weblayer/SettingType.java
index 87e2c51..583e437d 100644
--- a/weblayer/public/java/org/chromium/weblayer/SettingType.java
+++ b/weblayer/public/java/org/chromium/weblayer/SettingType.java
@@ -42,6 +42,9 @@
      * checkbox in the Safe Browsing interstitial which is displayed when the user encounters a
      * dangerous web page. The setting persists on disk.
      *
+     * Note: this setting applies when Safe Browsing is enabled (i.e. BASIC_SAFE_BROWSING_ENABLED
+     * is true).
+     *
      * @since 85
      */
     int EXTENDED_REPORTING_SAFE_BROWSING_ENABLED =
@@ -52,6 +55,9 @@
      * Allows the embedder to set whether it wants to enable/disable the Safe Browsing Real-time URL
      * checks. This functionality is disabled by default.
      *
+     * Note: this setting applies when Safe Browsing is enabled (i.e. BASIC_SAFE_BROWSING_ENABLED
+     * is true).
+     *
      * @since 85
      */
     int REAL_TIME_SAFE_BROWSING_ENABLED =