diff --git a/AUTHORS b/AUTHORS
index 1f2536f..638c9492 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -493,6 +493,7 @@
 Jinsong Fan <fanjinsong@sogou-inc.com>
 Jinsong Fan <jinsong.van@gmail.com>
 Jinwoo Song <jinwoo7.song@samsung.com>
+Jinyoung Hur <hur.ims@navercorp.com>
 Jinyoung Hur <hurims@gmail.com>
 Jitendra Kumar Sahoo <jitendra.ks@samsung.com>
 Joachim Bauch <jbauch@webrtc.org>
diff --git a/DEPS b/DEPS
index 19b4dbb..4f2ac01 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,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': '20f1b3462878a98125aa03d4aa74b955fcf4acf3',
+  'skia_revision': 'b64da3907f7638c87967d60cd250a6b02c914251',
   # 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': 'ee505470971ec0c0a00a0c3a0be51f6877475aff',
+  'v8_revision': '804d2150372f0e8a19996e7152d984898e8cb85d',
   # 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.
@@ -219,7 +219,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'ba1c65b08f2da6532811077fd51ffcfde6b92874',
+  'pdfium_revision': '0a1e0410572e3c9f378c426e9301f878798b3ab0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -250,7 +250,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'f6be92767d1d002f5e90a4673ee4e6d4f1e1744f',
+  'freetype_revision': '84b3616c94f48726c596ead4150218d4431b3412',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -274,7 +274,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': '9eeb00431109dd98382aa95f88961edb51ee7300',
+  'devtools_frontend_revision': '0096403e47fb3159757415c943e63826b228a192',
   # 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.
@@ -326,7 +326,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b31cd871ad1000a56302409b62d76e079f8a0b46',
+  'dawn_revision': '155241b665f8305d54145ca460e0b8cebe888336',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -565,7 +565,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f03c0a36e0fcc5ded1f7ce51b0a0589224689d01',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'e946f7e093404de560e8f63a76921e1bf7e2b1f6',
       'condition': 'checkout_ios',
   },
 
@@ -1615,7 +1615,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e35dcb0515eb3f0260d36cc3534bd49cbc33f116',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7bbf434aef522ac6e4a1cc0002aa61d698399549',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index 8dce8d65..ee4dc321d 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -253,7 +253,6 @@
     if (on_blur_closure_)
       on_blur_closure_.Run();
     views::Textfield::OnBlur();
-    SetPlaceholderText(prepared_placeholder_text_);
   }
 
   // views::Textfield:
@@ -261,7 +260,6 @@
     if (on_focus_closure_)
       on_focus_closure_.Run();
     views::Textfield::OnFocus();
-    SetPlaceholderText(base::string16());
   }
 
   void AboutToRequestFocusFromTabTraversal(bool reverse) override {
@@ -272,10 +270,6 @@
       SelectAll(/*reversed=*/false);
   }
 
-  void set_prepared_placeholder_text(const base::string16& text) {
-    prepared_placeholder_text_ = text;
-  }
-
   void UpdateFontListAndCursor() {
     SetCursorEnabled(GetText().empty());
     // We do not want the cursor to be too big, therefore the hidden font is
@@ -321,9 +315,6 @@
   base::RepeatingClosure on_focus_closure_;
   base::RepeatingClosure on_blur_closure_;
   base::RepeatingClosure on_tab_focus_closure_;
-
-  // Placeholder text to set when focus gets lost.
-  base::string16 prepared_placeholder_text_;
 };
 
 class LoginPasswordView::EasyUnlockIcon : public views::Button {
@@ -737,7 +728,7 @@
 
 void LoginPasswordView::SetPlaceholderText(
     const base::string16& placeholder_text) {
-  textfield_->set_prepared_placeholder_text(placeholder_text);
+  textfield_->SetPlaceholderText(placeholder_text);
 }
 
 void LoginPasswordView::SetReadOnly(bool read_only) {
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 7f2bf59..7be3ca124 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3517,6 +3517,7 @@
         "callback_unittest.nc",
         "containers/buffer_iterator_unittest.nc",
         "containers/checked_iterators_unittest.nc",
+        "containers/contains_unittest.nc",
         "containers/span_unittest.nc",
         "debug/crash_logging_unittest.nc",
         "memory/checked_ptr_unittest.nc",
diff --git a/base/allocator/partition_allocator/oom.h b/base/allocator/partition_allocator/oom.h
index 916ef12..6b9d042 100644
--- a/base/allocator/partition_allocator/oom.h
+++ b/base/allocator/partition_allocator/oom.h
@@ -16,7 +16,7 @@
 namespace {
 // The crash is generated in a NOINLINE function so that we can classify the
 // crash as an OOM solely by analyzing the stack trace.
-NOINLINE void OnNoMemory(size_t size) {
+[[noreturn]] NOINLINE void OnNoMemory(size_t size) {
   base::internal::RunPartitionAllocOomCallback();
   base::internal::OnNoMemoryInternal(size);
   IMMEDIATE_CRASH();
diff --git a/base/allocator/partition_allocator/partition_oom.cc b/base/allocator/partition_allocator/partition_oom.cc
index 9636728..9befc3f 100644
--- a/base/allocator/partition_allocator/partition_oom.cc
+++ b/base/allocator/partition_allocator/partition_oom.cc
@@ -20,7 +20,13 @@
 NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages(size_t size) {
   OOM_CRASH(size);
 }
-#endif
+
+[[noreturn]] NOINLINE void PartitionOutOfMemoryWithLargeVirtualSize(
+    size_t virtual_size) {
+  OOM_CRASH(virtual_size);
+}
+
+#endif  // !defined(ARCH_CPU_64_BITS)
 
 }  // namespace internal
 }  // namespace base
diff --git a/base/allocator/partition_allocator/partition_oom.h b/base/allocator/partition_allocator/partition_oom.h
index 466fe10..7cf1ec5 100644
--- a/base/allocator/partition_allocator/partition_oom.h
+++ b/base/allocator/partition_allocator/partition_oom.h
@@ -23,10 +23,14 @@
 // g_oom_handling_function is invoked when PartitionAlloc hits OutOfMemory.
 extern OomFunction g_oom_handling_function;
 
-BASE_EXPORT NOINLINE void PartitionExcessiveAllocationSize(size_t size);
+[[noreturn]] BASE_EXPORT NOINLINE void PartitionExcessiveAllocationSize(
+    size_t size);
 
 #if !defined(ARCH_CPU_64_BITS)
-NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages(size_t size);
+[[noreturn]] NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages(
+    size_t size);
+[[noreturn]] NOINLINE void PartitionOutOfMemoryWithLargeVirtualSize(
+    size_t virtual_size);
 #endif
 
 }  // namespace internal
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index cc0b8cc..e7abd126 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -310,17 +310,38 @@
 }  // namespace internal
 
 template <bool thread_safe>
-NOINLINE void PartitionRoot<thread_safe>::OutOfMemory(size_t size) {
+[[noreturn]] NOINLINE void PartitionRoot<thread_safe>::OutOfMemory(
+    size_t size) {
 #if !defined(ARCH_CPU_64_BITS)
+  size_t virtual_address_space_size =
+      total_size_of_super_pages.load(std::memory_order_relaxed) +
+      total_size_of_direct_mapped_pages.load(std::memory_order_relaxed);
+  size_t uncommitted_size =
+      virtual_address_space_size -
+      total_size_of_committed_pages.load(std::memory_order_relaxed);
+
   // Check whether this OOM is due to a lot of super pages that are allocated
   // but not committed, probably due to http://crbug.com/421387.
-  //
-  if (total_size_of_super_pages.load(std::memory_order_relaxed) +
-          total_size_of_direct_mapped_pages.load(std::memory_order_relaxed) -
-          total_size_of_committed_pages.load(std::memory_order_relaxed) >
-      kReasonableSizeOfUnusedPages) {
+  if (uncommitted_size > kReasonableSizeOfUnusedPages) {
     internal::PartitionOutOfMemoryWithLotsOfUncommitedPages(size);
   }
+
+  constexpr size_t kReasonableVirtualSize =
+#if defined(OS_WIN)
+      // 1GiB on Windows, as the entire address space is typically 2GiB.
+      1024 * 1024 * 1024;
+#else
+      // 1.5GiB elsewhere, since address space is typically 3GiB.
+      (1024 + 512) * 1024 * 1024;
+#endif
+  if (virtual_address_space_size > kReasonableVirtualSize) {
+    internal::PartitionOutOfMemoryWithLargeVirtualSize(
+        virtual_address_space_size);
+  }
+
+  // Make the virtual size visible to crash reports all the time.
+  base::debug::Alias(&virtual_address_space_size);
+
 #endif
   if (internal::g_oom_handling_function)
     (*internal::g_oom_handling_function)(size);
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index 98bd28b..342941eb 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -246,7 +246,7 @@
       PageAccessibilityDisposition accessibility_disposition)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
-  NOINLINE void OutOfMemory(size_t size);
+  [[noreturn]] NOINLINE void OutOfMemory(size_t size);
 
   // Returns a pointer aligned on |alignment|, or nullptr.
   //
diff --git a/base/allocator/partition_allocator/partition_stats.h b/base/allocator/partition_allocator/partition_stats.h
index 961b360..657a988 100644
--- a/base/allocator/partition_allocator/partition_stats.h
+++ b/base/allocator/partition_allocator/partition_stats.h
@@ -28,6 +28,8 @@
   uint64_t cache_fill_hits;
   uint64_t cache_fill_misses;  // Object too large.
 
+  uint64_t batch_fill_count;  // Number of central allocator requests.
+
   // Memory cost:
   uint64_t bucket_total_memory;
   uint64_t metadata_overhead;
diff --git a/base/allocator/partition_allocator/thread_cache.cc b/base/allocator/partition_allocator/thread_cache.cc
index 317373f..9621da0 100644
--- a/base/allocator/partition_allocator/thread_cache.cc
+++ b/base/allocator/partition_allocator/thread_cache.cc
@@ -301,6 +301,8 @@
   // clearing which would greatly increase calls to the central allocator. (3)
   // tries to keep memory usage low. So clearing half of the bucket, and filling
   // a quarter of it are sensible defaults.
+  INCREMENT_COUNTER(stats_.batch_fill_count);
+
   Bucket& bucket = buckets_[bucket_index];
   int count = bucket.limit / kBatchFillRatio;
 
@@ -388,6 +390,8 @@
   stats_.cache_fill_hits = 0;
   stats_.cache_fill_misses = 0;
 
+  stats_.batch_fill_count = 0;
+
   stats_.bucket_total_memory = 0;
   stats_.metadata_overhead = 0;
 
@@ -407,9 +411,11 @@
   stats->cache_fill_hits += stats_.cache_fill_hits;
   stats->cache_fill_misses += stats_.cache_fill_misses;
 
-  for (size_t i = 0; i < kBucketCount; i++) {
+  stats->batch_fill_count += stats_.batch_fill_count;
+
+  for (const Bucket& bucket : buckets_) {
     stats->bucket_total_memory +=
-        buckets_[i].count * static_cast<size_t>(buckets_[i].slot_size);
+        bucket.count * static_cast<size_t>(bucket.slot_size);
   }
   stats->metadata_overhead += sizeof(*this);
 }
diff --git a/base/allocator/partition_allocator/thread_cache.h b/base/allocator/partition_allocator/thread_cache.h
index 216a87d..225550f 100644
--- a/base/allocator/partition_allocator/thread_cache.h
+++ b/base/allocator/partition_allocator/thread_cache.h
@@ -257,6 +257,8 @@
 
   friend class ThreadCacheRegistry;
   friend class ThreadCacheTest;
+  FRIEND_TEST_ALL_PREFIXES(ThreadCacheTest, Simple);
+  FRIEND_TEST_ALL_PREFIXES(ThreadCacheTest, MultipleObjectsCachedPerBucket);
   FRIEND_TEST_ALL_PREFIXES(ThreadCacheTest, LargeAllocationsAreNotCached);
   FRIEND_TEST_ALL_PREFIXES(ThreadCacheTest, MultipleThreadCaches);
   FRIEND_TEST_ALL_PREFIXES(ThreadCacheTest, RecordStats);
diff --git a/base/allocator/partition_allocator/thread_cache_unittest.cc b/base/allocator/partition_allocator/thread_cache_unittest.cc
index c05ef20..627ed4ad 100644
--- a/base/allocator/partition_allocator/thread_cache_unittest.cc
+++ b/base/allocator/partition_allocator/thread_cache_unittest.cc
@@ -121,12 +121,13 @@
 };
 
 TEST_F(ThreadCacheTest, Simple) {
-  void* ptr = g_root->Alloc(kSmallSize, "");
-  ASSERT_TRUE(ptr);
-
   // There is a cache.
   auto* tcache = g_root->thread_cache_for_testing();
   EXPECT_TRUE(tcache);
+  DeltaCounter batch_fill_counter{tcache->stats_.batch_fill_count};
+
+  void* ptr = g_root->Alloc(kSmallSize, "");
+  ASSERT_TRUE(ptr);
 
   uint16_t index = PartitionRoot<ThreadSafe>::SizeToBucketIndex(kSmallSize);
   EXPECT_EQ(kFillCountForSmallBucket - 1,
@@ -141,6 +142,8 @@
   // Allocated from the thread cache.
   EXPECT_EQ(kFillCountForSmallBucket - 1,
             tcache->bucket_count_for_testing(index));
+
+  EXPECT_EQ(1u, batch_fill_counter.Delta());
 }
 
 TEST_F(ThreadCacheTest, InexactSizeMatch) {
@@ -167,11 +170,15 @@
 }
 
 TEST_F(ThreadCacheTest, MultipleObjectsCachedPerBucket) {
+  auto* tcache = g_root->thread_cache_for_testing();
+  DeltaCounter batch_fill_counter{tcache->stats_.batch_fill_count};
   size_t bucket_index =
       FillThreadCacheAndReturnIndex(kMediumSize, kFillCountForMediumBucket + 2);
-  auto* tcache = g_root->thread_cache_for_testing();
   EXPECT_EQ(2 * kFillCountForMediumBucket,
             tcache->bucket_count_for_testing(bucket_index));
+  // 2 batches, since there were more than |kFillCountForMediumBucket|
+  // allocations.
+  EXPECT_EQ(2u, batch_fill_counter.Delta());
 }
 
 TEST_F(ThreadCacheTest, ObjectsCachedCountIsLimited) {
diff --git a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
index 66d3690..ed60e3e 100644
--- a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
+++ b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
@@ -238,7 +238,7 @@
      * @param sample sample to be recorded, expected to fall in range {@code [0, max)}
      * @param max the smallest value counted in the overflow bucket, shouldn't be larger than 100
      */
-    private static void recordExactLinearHistogram(String name, int sample, int max) {
+    public static void recordExactLinearHistogram(String name, int sample, int max) {
         // Range [0, 1) is counted in the underflow bucket. The first "real" bucket starts at 1.
         final int min = 1;
         // One extra is added for the overflow bucket.
diff --git a/base/containers/contains.h b/base/containers/contains.h
index 63120f8..ec0a890 100644
--- a/base/containers/contains.h
+++ b/base/containers/contains.h
@@ -8,7 +8,6 @@
 #include <type_traits>
 #include <utility>
 
-#include "base/functional/identity.h"
 #include "base/ranges/algorithm.h"
 #include "base/ranges/ranges.h"
 #include "base/template_util.h"
@@ -17,6 +16,14 @@
 
 namespace internal {
 
+// Small helper to detect whether a given type has a nested `key_type` typedef.
+// Used below to catch misuses of the API for associative containers.
+template <typename T, typename SFINAE = void>
+struct HasKeyType : std::false_type {};
+
+template <typename T>
+struct HasKeyType<T, void_t<typename T::key_type>> : std::true_type {};
+
 // Probe whether a `contains` member function exists and return the result of
 // `container.contains(value)` if this is a valid expression. This is the
 // highest priority option.
@@ -51,14 +58,18 @@
 }
 
 // Generic fallback option, using a linear search over `container` to find
-// `value`. Has the lowest priority.
-template <typename Container, typename Value, typename Proj = identity>
+// `value`. Has the lowest priority. This will not compile for associative
+// containers, as this likely is a performance bug.
+template <typename Container, typename Value>
 constexpr bool ContainsImpl(const Container& container,
                             const Value& value,
-                            priority_tag<0>,
-                            Proj proj = {}) {
-  return ranges::find(container, value, std::move(proj)) !=
-         ranges::end(container);
+                            priority_tag<0>) {
+  static_assert(
+      !HasKeyType<Container>::value,
+      "Error: About to perform linear search on an associative container. "
+      "Either use a more generic comparator (e.g. std::less<>) or, if a linear "
+      "search is desired, provide an explicit projection parameter.");
+  return ranges::find(container, value) != ranges::end(container);
 }
 
 }  // namespace internal
@@ -78,8 +89,8 @@
 constexpr bool Contains(const Container& container,
                         const Value& value,
                         Proj proj) {
-  return internal::ContainsImpl(container, value, internal::priority_tag<0>(),
-                                std::move(proj));
+  return ranges::find(container, value, std::move(proj)) !=
+         ranges::end(container);
 }
 
 }  // namespace base
diff --git a/base/containers/contains_unittest.cc b/base/containers/contains_unittest.cc
index f3b9164b..66d2519 100644
--- a/base/containers/contains_unittest.cc
+++ b/base/containers/contains_unittest.cc
@@ -8,6 +8,8 @@
 #include <string>
 
 #include "base/containers/flat_set.h"
+#include "base/functional/identity.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,6 +35,14 @@
   EXPECT_FALSE(Contains(allowed_chars, 0, &ToLowerASCII<char>));
 }
 
+TEST(ContainsTest, GenericSetContainsWithProjection) {
+  constexpr StringPiece kFoo = "foo";
+  std::set<std::string> set = {"foo", "bar", "baz"};
+
+  // Opt into a linear search by explicitly providing a projection:
+  EXPECT_TRUE(Contains(set, kFoo, identity{}));
+}
+
 TEST(ContainsTest, ContainsWithFindAndNpos) {
   std::string str = "abcd";
 
diff --git a/base/containers/contains_unittest.nc b/base/containers/contains_unittest.nc
new file mode 100644
index 0000000..8931ad6
--- /dev/null
+++ b/base/containers/contains_unittest.nc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// https://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/containers/contains.h"
+
+#include <set>
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// The following code would perform a linear search through the set which is
+// likely unexpected and not intended. This is because the expression
+// `set.find(kFoo)` is ill-formed, since there is no implimit conversion from
+// StringPiece to `std::string`. This means Contains would fall back to the
+// general purpose `base::ranges::find(set, kFoo)` linear search.
+// To fix this clients can either use a more generic comparator like std::less<>
+// (in this case `set.find()` accepts any type that is comparable to a
+// std::string), or pass an explicit projection parameter to Contains, at which
+// point it will always perform a linear search.
+#if defined(NCTEST_CONTAINS_UNEXPECTED_LINEAR_SEARCH)  // [r"Error: About to perform linear search on an associative container."]
+void WontCompile() {
+  constexpr StringPiece kFoo = "foo";
+  std::set<std::string> set = {"foo", "bar", "baz"};
+  Contains(set, kFoo);
+}
+#endif
+
+}  // namespace base
diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc
index c885b30..453f29ea 100644
--- a/base/memory/discardable_shared_memory.cc
+++ b/base/memory/discardable_shared_memory.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 
+#include "base/allocator/partition_allocator/page_allocator.h"
 #include "base/atomicops.h"
 #include "base/bits.h"
 #include "base/feature_list.h"
@@ -460,6 +461,35 @@
   return true;
 }
 
+void DiscardableSharedMemory::ReleaseMemoryIfPossible(size_t offset,
+                                                      size_t length) {
+#if defined(OS_POSIX) && !defined(OS_NACL)
+// Linux and Android provide MADV_REMOVE which is preferred as it has a
+// behavior that can be verified in tests. Other POSIX flavors (MacOSX, BSDs),
+// provide MADV_FREE which has the same result but memory is purged lazily.
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#define MADV_PURGE_ARGUMENT MADV_REMOVE
+#elif defined(OS_APPLE)
+// MADV_FREE_REUSABLE is similar to MADV_FREE, but also marks the pages with the
+// reusable bit, which allows both Activity Monitor and memory-infra to
+// correctly track the pages.
+#define MADV_PURGE_ARGUMENT MADV_FREE_REUSABLE
+#else  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#define MADV_PURGE_ARGUMENT MADV_FREE
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+  // Advise the kernel to remove resources associated with purged pages.
+  // Subsequent accesses of memory pages will succeed, but might result in
+  // zero-fill-on-demand pages.
+  if (madvise(static_cast<char*>(shared_memory_mapping_.memory()) + offset,
+              length, MADV_PURGE_ARGUMENT)) {
+    DPLOG(ERROR) << "madvise() failed";
+  }
+#else   // defined(OS_POSIX) && !defined(OS_NACL)
+  DiscardSystemPages(
+      static_cast<char*>(shared_memory_mapping_.memory()) + offset, length);
+#endif  // defined(OS_POSIX) && !defined(OS_NACL)
+}
+
 bool DiscardableSharedMemory::IsMemoryResident() const {
   DCHECK(shared_memory_mapping_.IsValid());
 
diff --git a/base/memory/discardable_shared_memory.h b/base/memory/discardable_shared_memory.h
index 1c92c8c..c5c805a 100644
--- a/base/memory/discardable_shared_memory.h
+++ b/base/memory/discardable_shared_memory.h
@@ -116,6 +116,13 @@
   // different process. Returns NULL time if purged.
   Time last_known_usage() const { return last_known_usage_; }
 
+  // Releases any allocated pages in the specified range, if supported by the
+  // platform. Address space in the specified range continues to be reserved.
+  // The memory is not guaranteed to be released immediately.
+  // |offset| and |length| are both in bytes. |offset| and |length| must both be
+  // page aligned.
+  void ReleaseMemoryIfPossible(size_t offset, size_t length);
+
   // This returns true and sets |last_known_usage_| to 0 if
   // DiscardableSharedMemory object was successfully purged. Purging can fail
   // for two reasons; object might be locked or our last known usage timestamp
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 00fca84..a867123f 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -227,6 +227,8 @@
   dump->AddScalar("cache_fill_hits", "scalar", stats.cache_fill_hits);
   dump->AddScalar("cache_fill_misses", "scalar", stats.cache_fill_misses);
 
+  dump->AddScalar("batch_fill_count", "scalar", stats.batch_fill_count);
+
   dump->AddScalar("size", "bytes", stats.bucket_total_memory);
   dump->AddScalar("metadata_overhead", "bytes", stats.metadata_overhead);
 }
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 27338159..2009f95 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -81,8 +81,9 @@
 }
 
 // static
-size_t ProcessMemoryDump::CountResidentBytes(void* start_address,
-                                             size_t mapped_size) {
+base::Optional<size_t> ProcessMemoryDump::CountResidentBytes(
+    void* start_address,
+    size_t mapped_size) {
   const size_t page_size = GetSystemPageSize();
   const uintptr_t start_pointer = reinterpret_cast<uintptr_t>(start_address);
   DCHECK_EQ(0u, start_pointer % page_size);
@@ -160,8 +161,8 @@
 
   DCHECK(!failure);
   if (failure) {
-    total_resident_pages = 0;
     LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid";
+    return base::nullopt;
   }
   return total_resident_pages;
 }
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index c897387..e26442a 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -76,7 +76,8 @@
   // |start_address| and |mapped_size|. |mapped_size| is specified in bytes. The
   // value returned is valid only if the given range is currently mmapped by the
   // process. The |start_address| must be page-aligned.
-  static size_t CountResidentBytes(void* start_address, size_t mapped_size);
+  static base::Optional<size_t> CountResidentBytes(void* start_address,
+                                                   size_t mapped_size);
 
   // The same as above, but the given mapped range should belong to the
   // shared_memory's mapped region.
diff --git a/base/trace_event/process_memory_dump_unittest.cc b/base/trace_event/process_memory_dump_unittest.cc
index 64c98ef..903f9c9 100644
--- a/base/trace_event/process_memory_dump_unittest.cc
+++ b/base/trace_event/process_memory_dump_unittest.cc
@@ -486,17 +486,20 @@
   const size_t size1 = 5 * page_size;
   void* memory1 = Map(size1);
   memset(memory1, 0, size1);
-  size_t res1 = ProcessMemoryDump::CountResidentBytes(memory1, size1);
-  ASSERT_EQ(res1, size1);
+  base::Optional<size_t> res1 =
+      ProcessMemoryDump::CountResidentBytes(memory1, size1);
+  ASSERT_TRUE(res1.has_value());
+  ASSERT_EQ(res1.value(), size1);
   Unmap(memory1, size1);
 
   // Allocate a large memory segment (> 8Mib).
   const size_t kVeryLargeMemorySize = 15 * 1024 * 1024;
   void* memory2 = Map(kVeryLargeMemorySize);
   memset(memory2, 0, kVeryLargeMemorySize);
-  size_t res2 =
+  base::Optional<size_t> res2 =
       ProcessMemoryDump::CountResidentBytes(memory2, kVeryLargeMemorySize);
-  ASSERT_EQ(res2, kVeryLargeMemorySize);
+  ASSERT_TRUE(res2.has_value());
+  ASSERT_EQ(res2.value(), kVeryLargeMemorySize);
   Unmap(memory2, kVeryLargeMemorySize);
 }
 
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py
index eb960259..80d0b0a 100755
--- a/build/android/gradle/generate_gradle.py
+++ b/build/android/gradle/generate_gradle.py
@@ -578,13 +578,6 @@
       _TemplatePath(target_type.split('_')[0]), variables)
 
 
-def _IsTestDir(path):
-  return ('javatests/' in path or
-          'junit/' in path or
-          'test/' in path or
-          'testing/' in path)
-
-
 # Example: //chrome/android:monochrome
 def _GetNative(relative_func, target_names):
   """Returns an object containing native c++ sources list and its included path
@@ -638,8 +631,11 @@
   res_dirs = sorted(generator.processed_res_dirs)
   def Relativize(paths):
     return _RebasePath(paths, os.path.join(gradle_output_dir, _MODULE_ALL))
-  main_java_dirs = [d for d in java_dirs if not _IsTestDir(d)]
-  test_java_dirs = [d for d in java_dirs if _IsTestDir(d)]
+
+  # As after clank modularization, the java and javatests code will live side by
+  # side in the same module, we will list both of them in the main target here.
+  main_java_dirs = [d for d in java_dirs if 'junit/' not in d]
+  junit_test_java_dirs = [d for d in java_dirs if 'junit/' in d]
   variables['main'] = {
       'android_manifest': Relativize(_DEFAULT_ANDROID_MANIFEST_PATH),
       'java_dirs': Relativize(main_java_dirs),
@@ -648,7 +644,7 @@
       'res_dirs': Relativize(res_dirs),
   }
   variables['android_test'] = [{
-      'java_dirs': Relativize(test_java_dirs),
+      'java_dirs': Relativize(junit_test_java_dirs),
       'java_excludes': ['**/*.java'],
   }]
   if native_targets:
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 093a94b3..d8eb6546 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -274,8 +274,8 @@
 # - is_mac is set only for desktop Mac. It is not set on iOS.
 # - is_posix is true for mac and any Unix-like system (basically everything
 #   except Fuchsia and Windows).
-# - is_linux is true for desktop Linux and ChromeOS, but not Android (which is
-#   generally too different despite being based on the Linux kernel).
+# - is_linux is true for desktop Linux, but not for ChromeOS nor Android (which
+#   is generally too different despite being based on the Linux kernel).
 #
 # Do not add more is_* variants here for random lesser-used Unix systems like
 # aix or one of the BSDs. If you need to check these, just check the
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 3f020ec..77f3645c 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201216.3.1
+0.20201217.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 3f020ec..77f3645c 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201216.3.1
+0.20201217.1.1
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index c529919c..9392f0a 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -75,6 +75,102 @@
   }
 }
 
+// Returns the name of the event dispatch breakdown of EventLatency histograms
+// between `start_stage` and `end_stage`.
+constexpr const char* GetEventLatencyDispatchBreakdownName(
+    EventMetrics::DispatchStage start_stage,
+    EventMetrics::DispatchStage end_stage) {
+  switch (start_stage) {
+    case EventMetrics::DispatchStage::kGenerated:
+      DCHECK_EQ(end_stage,
+                EventMetrics::DispatchStage::kArrivedInRendererCompositor);
+      return "GenerationToRendererCompositor";
+    case EventMetrics::DispatchStage::kArrivedInRendererCompositor:
+      switch (end_stage) {
+        case EventMetrics::DispatchStage::kRendererCompositorStarted:
+          return "RendererCompositorQueueingDelay";
+        case EventMetrics::DispatchStage::kRendererMainStarted:
+          return "RendererCompositorToMain";
+        default:
+          NOTREACHED();
+          return nullptr;
+      }
+    case EventMetrics::DispatchStage::kRendererCompositorStarted:
+      DCHECK_EQ(end_stage,
+                EventMetrics::DispatchStage::kRendererCompositorFinished);
+      return "RendererCompositorProcessing";
+    case EventMetrics::DispatchStage::kRendererCompositorFinished:
+      DCHECK_EQ(end_stage, EventMetrics::DispatchStage::kRendererMainStarted);
+      return "RendererCompositorToMain";
+    case EventMetrics::DispatchStage::kRendererMainStarted:
+      DCHECK_EQ(end_stage, EventMetrics::DispatchStage::kRendererMainFinished);
+      return "RendererMainProcessing";
+    case EventMetrics::DispatchStage::kRendererMainFinished:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
+// Returns the name of EventLatency breakdown between `dispatch_stage` and
+// `compositor_stage`.
+constexpr const char* GetEventLatencyDispatchToCompositorBreakdownName(
+    EventMetrics::DispatchStage dispatch_stage,
+    CompositorFrameReporter::StageType compositor_stage) {
+  switch (dispatch_stage) {
+    case EventMetrics::DispatchStage::kRendererCompositorFinished:
+      switch (compositor_stage) {
+        case CompositorFrameReporter::StageType::
+            kBeginImplFrameToSendBeginMainFrame:
+          return "RendererCompositorFinishedToBeginImplFrame";
+        case CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit:
+          return "RendererCompositorFinishedToSendBeginMainFrame";
+        case CompositorFrameReporter::StageType::kCommit:
+          return "RendererCompositorFinishedToCommit";
+        case CompositorFrameReporter::StageType::kEndCommitToActivation:
+          return "RendererCompositorFinishedToEndCommit";
+        case CompositorFrameReporter::StageType::kActivation:
+          return "RendererCompositorFinishedToActivation";
+        case CompositorFrameReporter::StageType::
+            kEndActivateToSubmitCompositorFrame:
+          return "RendererCompositorFinishedToEndActivate";
+        case CompositorFrameReporter::StageType::
+            kSubmitCompositorFrameToPresentationCompositorFrame:
+          return "RendererCompositorFinishedToSubmitCompositorFrame";
+        default:
+          NOTREACHED();
+          return nullptr;
+      }
+      break;
+    case EventMetrics::DispatchStage::kRendererMainFinished:
+      switch (compositor_stage) {
+        case CompositorFrameReporter::StageType::
+            kBeginImplFrameToSendBeginMainFrame:
+          return "RendererMainFinishedToBeginImplFrame";
+        case CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit:
+          return "RendererMainFinishedToSendBeginMainFrame";
+        case CompositorFrameReporter::StageType::kCommit:
+          return "RendererMainFinishedToCommit";
+        case CompositorFrameReporter::StageType::kEndCommitToActivation:
+          return "RendererMainFinishedToEndCommit";
+        case CompositorFrameReporter::StageType::kActivation:
+          return "RendererMainFinishedToActivation";
+        case CompositorFrameReporter::StageType::
+            kEndActivateToSubmitCompositorFrame:
+          return "RendererMainFinishedToEndActivate";
+        case CompositorFrameReporter::StageType::
+            kSubmitCompositorFrameToPresentationCompositorFrame:
+          return "RendererMainFinishedToSubmitCompositorFrame";
+        default:
+          NOTREACHED();
+          return nullptr;
+      }
+      break;
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
 // Names for CompositorFrameReporter::StageType, which should be updated in case
 // of changes to the enum.
 constexpr const char* GetStageName(int stage_type_index,
@@ -905,6 +1001,11 @@
 }
 
 void CompositorFrameReporter::ReportEventLatencyTraceEvents() const {
+  // TODO(mohsen): This function is becoming large and there is concerns about
+  // having this in the compositor critical path. crbug.com/1072740 is
+  // considering doing the reporting off-thread, but as a short-term solution,
+  // we should investigate whether we can skip this function entirely if tracing
+  // is off and whether that has any positive impact or not.
   for (const auto& event_metrics : events_metrics_) {
     const base::TimeTicks generated_timestamp =
         event_metrics->GetDispatchStageTimestamp(
@@ -915,30 +1016,68 @@
         "cc,input", "EventLatency", trace_id, generated_timestamp, "event",
         event_metrics->GetTypeName());
 
-    // Find the first stage that happens after the event's arrival in the
-    // browser.
-    auto stage_it =
-        std::find_if(stage_history_.begin(), stage_history_.end(),
-                     [generated_timestamp](const StageData& stage) {
-                       return stage.start_time > generated_timestamp;
-                     });
+    // Event dispatch stages.
+    EventMetrics::DispatchStage dispatch_stage =
+        EventMetrics::DispatchStage::kGenerated;
+    base::TimeTicks dispatch_timestamp =
+        event_metrics->GetDispatchStageTimestamp(dispatch_stage);
+    while (dispatch_stage != EventMetrics::DispatchStage::kMaxValue) {
+      DCHECK(!dispatch_timestamp.is_null());
+
+      // Find the end dispatch stage.
+      auto end_stage = static_cast<EventMetrics::DispatchStage>(
+          static_cast<int>(dispatch_stage) + 1);
+      base::TimeTicks end_timestamp =
+          event_metrics->GetDispatchStageTimestamp(end_stage);
+      while (end_timestamp.is_null() &&
+             end_stage != EventMetrics::DispatchStage::kMaxValue) {
+        end_stage = static_cast<EventMetrics::DispatchStage>(
+            static_cast<int>(end_stage) + 1);
+        end_timestamp = event_metrics->GetDispatchStageTimestamp(end_stage);
+      }
+      if (end_timestamp.is_null())
+        break;
+
+      const char* breakdown_name =
+          GetEventLatencyDispatchBreakdownName(dispatch_stage, end_stage);
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+          "cc,input", breakdown_name, trace_id, dispatch_timestamp);
+      TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("cc,input", breakdown_name,
+                                                     trace_id, end_timestamp);
+
+      dispatch_stage = end_stage;
+      dispatch_timestamp = end_timestamp;
+    }
+
+    // Find the first compositor stage that happens after the final dispatch
+    // stage.
+    auto stage_it = std::find_if(stage_history_.begin(), stage_history_.end(),
+                                 [dispatch_timestamp](const StageData& stage) {
+                                   return stage.start_time > dispatch_timestamp;
+                                 });
     // TODO(crbug.com/1079116): Ideally, at least the start time of
     // SubmitCompositorFrameToPresentationCompositorFrame stage should be
-    // greater than the event time stamp, but apparently, this is not always the
-    // case (see crbug.com/1093698). For now, skip to the next event in such
-    // cases. Hopefully, the work to reduce discrepancies between the new
-    // EventLatency and the old Event.Latency metrics would fix this issue. If
-    // not, we need to reconsider investigating this issue.
+    // greater than the final event dispatch timestamp, but apparently, this is
+    // not always the case (see crbug.com/1093698). For now, skip to the next
+    // event in such cases. Hopefully, the work to reduce discrepancies between
+    // the new EventLatency and the old Event.Latency metrics would fix this
+    // issue. If not, we need to reconsider investigating this issue.
     if (stage_it == stage_history_.end())
       continue;
 
+    DCHECK(dispatch_stage ==
+               EventMetrics::DispatchStage::kRendererCompositorFinished ||
+           dispatch_stage ==
+               EventMetrics::DispatchStage::kRendererMainFinished);
+    const char* d2c_breakdown_name =
+        GetEventLatencyDispatchToCompositorBreakdownName(dispatch_stage,
+                                                         stage_it->stage_type);
     TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
-        "cc,input", "BrowserToRendererCompositor", trace_id,
-        generated_timestamp);
+        "cc,input", d2c_breakdown_name, trace_id, dispatch_timestamp);
     TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
-        "cc,input", "BrowserToRendererCompositor", trace_id,
-        stage_it->start_time);
+        "cc,input", d2c_breakdown_name, trace_id, stage_it->start_time);
 
+    // Compositor stages.
     for (; stage_it != stage_history_.end(); ++stage_it) {
       const int stage_type_index = static_cast<int>(stage_it->stage_type);
       CHECK_LT(stage_type_index, static_cast<int>(StageType::kStageTypeCount));
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 659ded91..e5d0729 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1248,7 +1248,6 @@
   "java/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImpl.java",
   "java/src/org/chromium/chrome/browser/signin/SigninFragment.java",
   "java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java",
-  "java/src/org/chromium/chrome/browser/signin/SigninHelper.java",
   "java/src/org/chromium/chrome/browser/signin/SigninHelperProvider.java",
   "java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java",
   "java/src/org/chromium/chrome/browser/signin/SigninPromoController.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 87bd5f7..d77e7b18 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -96,7 +96,6 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantRootViewContainer.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbar.java",
@@ -110,6 +109,8 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantServiceInjector.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/BaseOnboardingCoordinator.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/LayoutUtils.java",
@@ -199,11 +200,11 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantLiteService.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/BaseOnboardingCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetails.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsModel.java",
@@ -270,7 +271,6 @@
   testonly = true
 
   sources = [
-    "javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAutostartTest.java",
@@ -299,6 +299,7 @@
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTriggerScriptIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java",
+    "javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java",
   ]
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantRootViewContainer.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantRootViewContainer.java
index 56f53b3..b54fcb8 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantRootViewContainer.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantRootViewContainer.java
@@ -27,6 +27,7 @@
     private final BrowserControlsStateProvider mBrowserControlsStateProvider;
     private Rect mVisibleViewportRect = new Rect();
     private float mTalkbackSheetSizeFraction;
+    private boolean mTalkbackResizingDisabled;
 
     public AssistantRootViewContainer(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -53,6 +54,10 @@
         invalidate();
     }
 
+    public void disableTalkbackViewResizing() {
+        mTalkbackResizingDisabled = true;
+    }
+
     void destroy() {
         mBrowserControlsStateProvider.removeObserver(this);
     }
@@ -67,7 +72,7 @@
 
         int targetHeight;
         int mode;
-        if (ChromeAccessibilityUtil.get().isAccessibilityEnabled()) {
+        if (ChromeAccessibilityUtil.get().isAccessibilityEnabled() && !mTalkbackResizingDisabled) {
             // TODO(b/143944870): Make this more stable with landscape mode.
             targetHeight = (int) (availableHeight * mTalkbackSheetSizeFraction);
             mode = MeasureSpec.EXACTLY;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandlerImpl.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandlerImpl.java
index ba97e59..aab251f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandlerImpl.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandlerImpl.java
@@ -82,8 +82,8 @@
     public void performOnboarding(
             String experimentIds, Bundle arguments, Callback<Boolean> callback) {
         Map<String, String> parameters = toArgumentMap(arguments);
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator(experimentIds, parameters, mContext,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator(experimentIds, parameters, mContext,
                         mBottomSheetController, mBrowserControls, mCompositorViewHolder, mScrim);
         coordinator.show(accepted -> {
             coordinator.hide();
@@ -101,13 +101,13 @@
         }
 
         Map<String, String> argumentMap = toArgumentMap(arguments);
-        Callback<AssistantOnboardingCoordinator> afterOnboarding = (onboardingCoordinator) -> {
+        Callback<BottomSheetOnboardingCoordinator> afterOnboarding = (onboardingCoordinator) -> {
             callback.onResult(client.performDirectAction(
                     name, experimentIds, argumentMap, onboardingCoordinator));
         };
 
         if (!AutofillAssistantPreferencesUtil.isAutofillOnboardingAccepted()) {
-            AssistantOnboardingCoordinator coordinator = new AssistantOnboardingCoordinator(
+            BottomSheetOnboardingCoordinator coordinator = new BottomSheetOnboardingCoordinator(
                     experimentIds, argumentMap, mContext, mBottomSheetController, mBrowserControls,
                     mCompositorViewHolder, mScrim);
             coordinator.show(accepted -> {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
index 79798b25..0482f84 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
@@ -109,7 +109,7 @@
      */
     boolean start(String initialUrl, Map<String, String> parameters, String experimentIds,
             @Nullable String callerAccount, @Nullable String userName, boolean isChromeCustomTab,
-            @Nullable AssistantOnboardingCoordinator onboardingCoordinator) {
+            @Nullable BaseOnboardingCoordinator onboardingCoordinator) {
         if (mNativeClientAndroid == 0) return false;
 
         checkNativeClientIsAliveOrThrow();
@@ -213,7 +213,7 @@
      */
     public boolean performDirectAction(String actionId, String experimentIds,
             Map<String, String> arguments,
-            @Nullable AssistantOnboardingCoordinator onboardingCoordinator) {
+            @Nullable BaseOnboardingCoordinator onboardingCoordinator) {
         if (mNativeClientAndroid == 0) return false;
 
         // Note that only fetchWebsiteActions can start AA, so only it needs
@@ -405,8 +405,8 @@
         boolean start(long nativeClientAndroid, AutofillAssistantClient caller, String initialUrl,
                 String experimentIds, String callerAccount, String[] parameterNames,
                 String[] parameterValues, boolean isChromeCustomTab,
-                @Nullable AssistantOnboardingCoordinator onboardingCoordinator,
-                boolean onboardingShown, long nativeService);
+                @Nullable BaseOnboardingCoordinator onboardingCoordinator, boolean onboardingShown,
+                long nativeService);
         void startTriggerScript(long nativeClientAndroid, AutofillAssistantClient caller,
                 AssistantTriggerScriptBridge delegate, String initialUrl, String experimentIds,
                 String[] parameterNames, String[] parameterValues, long nativeServiceRequestSender);
@@ -426,7 +426,6 @@
 
         boolean performDirectAction(long nativeClientAndroid, AutofillAssistantClient caller,
                 String actionId, String experimentId, String[] argumentNames,
-                String[] argumentValues,
-                @Nullable AssistantOnboardingCoordinator onboardingCoordinator);
+                String[] argumentValues, @Nullable BaseOnboardingCoordinator onboardingCoordinator);
     }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java
index c780a69..aa7a070 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java
@@ -167,9 +167,11 @@
             return;
         }
 
-        AssistantOnboardingCoordinator onboardingCoordinator = new AssistantOnboardingCoordinator(
-                experimentIds, parameters, context, bottomSheetController, browserControls,
-                compositorViewHolder, bottomSheetController.getScrimCoordinator());
+        BottomSheetOnboardingCoordinator onboardingCoordinator =
+                new BottomSheetOnboardingCoordinator(experimentIds, parameters, context,
+                        bottomSheetController, browserControls, compositorViewHolder,
+                        bottomSheetController.getScrimCoordinator());
+
         onboardingCoordinator.show(accepted -> {
             if (parameters.containsKey(PARAMETER_TRIGGER_SCRIPT_USED)
                     || parameters.containsKey(PARAMETER_STARTED_WITH_TRIGGER_SCRIPT)) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index eb97928..7aeca2b 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -104,7 +104,7 @@
     @CalledByNative
     private static AutofillAssistantUiController create(ChromeActivity activity,
             boolean allowTabSwitching, long nativeUiController,
-            @Nullable AssistantOnboardingCoordinator onboardingCoordinator) {
+            @Nullable BaseOnboardingCoordinator onboardingCoordinator) {
         BottomSheetController sheetController =
                 BottomSheetControllerProvider.from(activity.getWindowAndroid());
         assert activity != null;
@@ -124,8 +124,7 @@
 
     private AutofillAssistantUiController(ChromeActivity activity, BottomSheetController controller,
             TabObscuringHandler tabObscuringHandler, boolean allowTabSwitching,
-            long nativeUiController,
-            @Nullable AssistantOnboardingCoordinator onboardingCoordinator) {
+            long nativeUiController, @Nullable BaseOnboardingCoordinator onboardingCoordinator) {
         mNativeUiController = nativeUiController;
         mActivity = activity;
         mCoordinator = new AssistantCoordinator(activity, controller, tabObscuringHandler,
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BaseOnboardingCoordinator.java
similarity index 67%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
rename to chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BaseOnboardingCoordinator.java
index e169d87b..df3168d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BaseOnboardingCoordinator.java
@@ -23,17 +23,7 @@
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.metrics.OnBoarding;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
-import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
-import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
-import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
-import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
-import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
-import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.embedder_support.util.UrlUtilitiesJni;
 import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.WebContents;
@@ -46,11 +36,12 @@
 import java.util.Map;
 
 /**
- * Coordinator responsible for showing the onboarding screen when the user is using the Autofill
- * Assistant for the first time.
+ * Base Coordinator class responsible for showing the onboarding screen when the user is using the
+ * Autofill Assistant for the first time.
  */
 @JNINamespace("autofill_assistant")
-class AssistantOnboardingCoordinator {
+// TODO(b/174445633): Add a public interface for |show()|, |hide()| and |getOboardingShown()|
+abstract class BaseOnboardingCoordinator {
     private static final String INTENT_IDENTFIER = "INTENT";
     private static final String FETCH_TIMEOUT_IDENTIFIER = "ONBOARDING_FETCH_TIMEOUT_MS";
     private static final String BUY_MOVIE_TICKETS_INTENT = "BUY_MOVIE_TICKET";
@@ -67,40 +58,24 @@
 
     private final String mExperimentIds;
     private final Map<String, String> mParameters;
-    private final Context mContext;
-    private final BottomSheetController mController;
-    private final BrowserControlsStateProvider mBrowserControls;
-    private final CompositorViewHolder mCompositorViewHolder;
-    private final ScrimCoordinator mScrimCoordinator;
-
-    @Nullable
-    private AssistantOverlayCoordinator mOverlayCoordinator;
+    private final Map<String, String> mStringMap = new HashMap<>();
 
     @Nullable
     private WebContentsObserver mWebContentsObserver;
-    private BottomSheetObserver mBottomSheetObserver;
-
-    @Nullable
-    private AssistantBottomSheetContent mContent;
-    private boolean mAnimate = true;
-
-    @Nullable
-    private ScrollView mView;
-    private final Map<String, String> mStringMap = new HashMap<>();
-
     private boolean mOnboardingShown;
 
-    AssistantOnboardingCoordinator(String experimentIds, Map<String, String> parameters,
-            Context context, BottomSheetController controller,
-            BrowserControlsStateProvider browserControls, CompositorViewHolder compositorViewHolder,
-            ScrimCoordinator scrim) {
+    final Context mContext;
+    boolean mAnimate = true;
+    @Nullable
+    ScrollView mView;
+
+    BaseOnboardingCoordinator(
+            String experimentIds, Map<String, String> parameters, Context context) {
         mExperimentIds = experimentIds;
         mParameters = parameters;
         mContext = context;
-        mController = controller;
-        mBrowserControls = browserControls;
-        mCompositorViewHolder = compositorViewHolder;
-        mScrimCoordinator = scrim;
+        mView = (ScrollView) LayoutUtils.createInflater(mContext).inflate(
+                R.layout.autofill_assistant_onboarding, /* root= */ null);
     }
 
     /**
@@ -113,6 +88,62 @@
      * navigation to that URL is allowed, other navigations will hide Autofill Assistant.
      */
     void show(Callback<Boolean> callback, WebContents webContents, String targetUrl) {
+        addWebContentObserver(callback, webContents, targetUrl);
+        show(callback);
+    }
+
+    /**
+     * Shows onboarding and provides the result to the given callback.
+     *
+     * <p>The {@code callback} will be called with true or false when the user accepts or cancels
+     * the onboarding (respectively).
+     *
+     * <p>Note that the onboarding screen will be hidden after the callback returns. Call, from the
+     * callback, {@link #hide} to hide it earlier or {@link #transferControls} to take ownership of
+     * it and possibly keep it past the end of the callback.
+     */
+    void show(Callback<Boolean> callback) {
+        AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_SHOWN);
+        mOnboardingShown = true;
+
+        initViewImpl(callback);
+        setupSharedView(callback);
+
+        int fetchTimeoutMs = 300;
+        if (mParameters.containsKey(FETCH_TIMEOUT_IDENTIFIER)) {
+            fetchTimeoutMs = Integer.parseInt(mParameters.get(FETCH_TIMEOUT_IDENTIFIER));
+        }
+        if (!mParameters.containsKey(INTENT_IDENTFIER) || fetchTimeoutMs == 0) {
+            updateAndShowView();
+        } else {
+            BaseOnboardingCoordinatorJni.get().fetchOnboardingDefinition(this,
+                    mParameters.get(INTENT_IDENTFIER), LocaleUtils.getDefaultLocaleString(),
+                    fetchTimeoutMs);
+        }
+    }
+
+    /**
+     * Returns {@code true} if the onboarding has been shown at the beginning when this
+     * autofill assistant flow got triggered.
+     */
+    boolean getOnboardingShown() {
+        return mOnboardingShown;
+    }
+
+    abstract void hide();
+    // TODO(b/175598484): Move transferControls to bottom sheet subclass
+    abstract AssistantOverlayCoordinator transferControls();
+
+    /** Destroy web contents observer. */
+    void destroy() {
+        if (mWebContentsObserver != null) {
+            mWebContentsObserver.destroy();
+            mWebContentsObserver = null;
+        }
+    }
+
+    private void addWebContentObserver(
+            Callback<Boolean> callback, WebContents webContents, String targetUrl) {
         mWebContentsObserver = new WebContentsObserver(webContents) {
             @Override
             public void didStartNavigation(NavigationHandle navigationHandle) {
@@ -128,141 +159,14 @@
             }
         };
         webContents.addObserver(mWebContentsObserver);
-
-        show(callback);
     }
 
     /**
-     * Shows onboarding and provides the result to the given callback.
-     *
-     * <p>The {@code callback} will be called with true or false when the user accepts or cancels
-     * the onboarding (respectively).
-     *
-     * <p>Note that the bottom sheet will be hidden after the callback returns. Call, from the
-     * callback, {@link #hide} to hide it earlier or {@link #transferControls} to take ownership of
-     * it and possibly keep it past the end of the callback.
+     * Setup the shared |mView|
      */
-    void show(Callback<Boolean> callback) {
-        AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_SHOWN);
-        mOnboardingShown = true;
-
-        // If there's a tab, cover it with an overlay.
-        AssistantOverlayModel overlayModel = new AssistantOverlayModel();
-        mOverlayCoordinator = new AssistantOverlayCoordinator(
-                mContext, mBrowserControls, mCompositorViewHolder, mScrimCoordinator, overlayModel);
-        overlayModel.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
-
-        mBottomSheetObserver = new EmptyBottomSheetObserver() {
-            @Override
-            public void onSheetStateChanged(int newState) {
-                if (mOverlayCoordinator == null) {
-                    return;
-                }
-
-                if (newState == SheetState.HIDDEN) {
-                    mOverlayCoordinator.suppress();
-                }
-                if (newState == SheetState.PEEK || newState == SheetState.HALF
-                        || newState == SheetState.FULL) {
-                    mOverlayCoordinator.restore();
-                }
-            }
-        };
-        mController.addObserver(mBottomSheetObserver);
-
-        AssistantBottomBarDelegate delegate = new AssistantBottomBarDelegate() {
-            @Override
-            public boolean onBackButtonPressed() {
-                onUserAction(
-                        /* accept= */ false, callback, OnBoarding.OB_NO_ANSWER,
-                        DropOutReason.ONBOARDING_BACK_BUTTON_CLICKED);
-                return true;
-            }
-
-            @Override
-            public void onBottomSheetClosedWithSwipe() {}
-        };
-        BottomSheetContent currentSheetContent = mController.getCurrentSheetContent();
-        if (currentSheetContent instanceof AssistantBottomSheetContent) {
-            mContent = (AssistantBottomSheetContent) currentSheetContent;
-            mContent.setDelegate(() -> delegate);
-        } else {
-            mContent = new AssistantBottomSheetContent(mContext, () -> delegate);
-        }
-        initContent(callback);
-    }
-
-    /**
-     * Transfers ownership of the controls to the caller, returns the overlay coordinator, if one
-     * was created.
-     *
-     * <p>This call is only useful when called from inside a callback provided to {@link #show}, as
-     * before that there are no controls and after that the coordinator automatically hides them.
-     * This call allows callbacks to reuse the controls setup for onboarding and provide a smooth
-     * transition.
-     */
-    @Nullable
-    AssistantOverlayCoordinator transferControls() {
-        assert isInProgress();
-
-        AssistantOverlayCoordinator coordinator = mOverlayCoordinator;
-        mOverlayCoordinator = null;
-        mContent = null;
-        return coordinator;
-    }
-
-    /** Hides the UI, if one is shown. */
-    void hide() {
-        mController.removeObserver(mBottomSheetObserver);
-
-        if (mOverlayCoordinator != null) {
-            mOverlayCoordinator.destroy();
-            mOverlayCoordinator = null;
-        }
-
-        if (mContent != null) {
-            mController.hideContent(mContent, /* animate= */ mAnimate);
-            mContent = null;
-        }
-
-        if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
-            mWebContentsObserver = null;
-        }
-    }
-
-    /**
-     * Returns {@code true} between the time {@link #show} is called and the time
-     * the callback has returned.
-     */
-    boolean isInProgress() {
-        return mContent != null;
-    }
-
-    /** Don't animate the bottom sheet expansion. */
-    @VisibleForTesting
-    void disableAnimationForTesting() {
-        mAnimate = false;
-    }
-
-    /**
-     * Returns {@code true} if the onboarding has been shown at the beginning when this
-     * autofill assistant flow got triggered.
-     */
-    boolean getOnboardingShown() {
-        return mOnboardingShown;
-    }
-
-    /**
-     * Set the content of the bottom sheet to be the Autofill Assistant onboarding.
-     */
-    private void initContent(Callback<Boolean> callback) {
-        mView = (ScrollView) LayoutUtils.createInflater(mContext).inflate(
-                R.layout.autofill_assistant_onboarding, /* root= */ null);
-
+    private void setupSharedView(Callback<Boolean> callback) {
         // Set focusable for accessibility.
         mView.setFocusable(true);
-
         mView.findViewById(R.id.button_init_ok)
                 .setOnClickListener(unusedView
                         -> onUserAction(
@@ -273,22 +177,10 @@
                         -> onUserAction(
                                 /* accept= */ false, callback, OnBoarding.OB_CANCELLED,
                                 DropOutReason.DECLINED));
-
-        int fetchTimeoutMs = 300;
-        if (mParameters.containsKey(FETCH_TIMEOUT_IDENTIFIER)) {
-            fetchTimeoutMs = Integer.parseInt(mParameters.get(FETCH_TIMEOUT_IDENTIFIER));
-        }
-        if (!mParameters.containsKey(INTENT_IDENTFIER) || fetchTimeoutMs == 0) {
-            updateAndShowView();
-        } else {
-            AssistantOnboardingCoordinatorJni.get().fetchOnboardingDefinition(this,
-                    mParameters.get(INTENT_IDENTFIER), LocaleUtils.getDefaultLocaleString(),
-                    fetchTimeoutMs);
-        }
     }
 
-    private void onUserAction(boolean accept, Callback<Boolean> callback,
-            @OnBoarding int onboardingAnswer, @DropOutReason int dropoutReason) {
+    void onUserAction(boolean accept, Callback<Boolean> callback, @OnBoarding int onboardingAnswer,
+            @DropOutReason int dropoutReason) {
         AutofillAssistantPreferencesUtil.setInitialPreferences(accept);
         AutofillAssistantMetrics.recordOnBoarding(onboardingAnswer);
         if (!accept) {
@@ -299,6 +191,44 @@
         hide();
     }
 
+    @CalledByNative
+    @VisibleForTesting
+    public void addEntryToStringMap(String key, String value) {
+        mStringMap.put(key, value);
+    }
+
+    @CalledByNative
+    @VisibleForTesting
+    public void updateAndShowView() {
+        updateView();
+        showViewImpl();
+    }
+
+    private void updateView() {
+        assert mView != null;
+
+        String termsAndConditionsKey = "terms_and_conditions";
+        String termsAndConditionsUrlKey = "terms_and_conditions_url";
+        updateTermsAndConditions(mView, mStringMap.get(termsAndConditionsKey),
+                mStringMap.get(termsAndConditionsUrlKey));
+
+        if (mStringMap.isEmpty()) {
+            updateViewBasedOnIntent(mView);
+        } else {
+            String onboardingTitleKey = "onboarding_title";
+            if (mStringMap.containsKey(onboardingTitleKey)) {
+                ((TextView) mView.findViewById(R.id.onboarding_try_assistant))
+                        .setText(mStringMap.get(onboardingTitleKey));
+            }
+
+            String onboardingTextKey = "onboarding_text";
+            if (mStringMap.containsKey(onboardingTextKey)) {
+                ((TextView) mView.findViewById(R.id.onboarding_subtitle))
+                        .setText(mStringMap.get(onboardingTextKey));
+            }
+        }
+    }
+
     private void updateTermsAndConditions(ScrollView initView,
             @Nullable String termsAndConditionsString, @Nullable String termsAndConditionsUrl) {
         TextView termsTextView = initView.findViewById(R.id.google_terms_message);
@@ -374,46 +304,18 @@
         }
     }
 
-    @CalledByNative
+    /** Don't animate the user interface. */
     @VisibleForTesting
-    public void addEntryToStringMap(String key, String value) {
-        mStringMap.put(key, value);
+    void disableAnimationForTesting() {
+        mAnimate = false;
     }
 
-    @CalledByNative
-    @VisibleForTesting
-    public void updateAndShowView() {
-        assert mView != null;
-
-        String termsAndConditionsKey = "terms_and_conditions";
-        String termsAndConditionsUrlKey = "terms_and_conditions_url";
-        updateTermsAndConditions(mView, mStringMap.get(termsAndConditionsKey),
-                mStringMap.get(termsAndConditionsUrlKey));
-
-        if (mStringMap.isEmpty()) {
-            updateViewBasedOnIntent(mView);
-        } else {
-            String onboardingTitleKey = "onboarding_title";
-            if (mStringMap.containsKey(onboardingTitleKey)) {
-                ((TextView) mView.findViewById(R.id.onboarding_try_assistant))
-                        .setText(mStringMap.get(onboardingTitleKey));
-            }
-
-            String onboardingTextKey = "onboarding_text";
-            if (mStringMap.containsKey(onboardingTextKey)) {
-                ((TextView) mView.findViewById(R.id.onboarding_subtitle))
-                        .setText(mStringMap.get(onboardingTextKey));
-            }
-        }
-
-        mContent.setContent(mView, mView);
-        BottomSheetUtils.showContentAndMaybeExpand(
-                mController, mContent, /* shouldExpand = */ true, mAnimate);
-    }
+    abstract void initViewImpl(Callback<Boolean> callback);
+    abstract void showViewImpl();
 
     @NativeMethods
     interface Natives {
-        void fetchOnboardingDefinition(AssistantOnboardingCoordinator coordinator, String intent,
-                String locale, int timeoutMs);
+        void fetchOnboardingDefinition(
+                BaseOnboardingCoordinator coordinator, String intent, String locale, int timeoutMs);
     }
-}
+}
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinator.java
new file mode 100644
index 0000000..93e0cc4e
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinator.java
@@ -0,0 +1,152 @@
+// 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.autofill_assistant;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
+import org.chromium.chrome.browser.autofill_assistant.metrics.OnBoarding;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
+import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
+import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
+
+import java.util.Map;
+
+/**
+ * Coordinator responsible for showing the bottom sheet onboarding screen when the user is using the
+ * Autofill Assistant for the first time.
+ */
+class BottomSheetOnboardingCoordinator extends BaseOnboardingCoordinator {
+    @Nullable
+    private AssistantBottomSheetContent mContent;
+    private BottomSheetObserver mBottomSheetObserver;
+    private final BottomSheetController mController;
+    private final BrowserControlsStateProvider mBrowserControls;
+    private final CompositorViewHolder mCompositorViewHolder;
+    private final ScrimCoordinator mScrimCoordinator;
+
+    @Nullable
+    AssistantOverlayCoordinator mOverlayCoordinator;
+
+    BottomSheetOnboardingCoordinator(String experimentIds, Map<String, String> parameters,
+            Context context, BottomSheetController controller,
+            BrowserControlsStateProvider browserControls, CompositorViewHolder compositorViewHolder,
+            ScrimCoordinator scrim) {
+        super(experimentIds, parameters, context);
+        this.mController = controller;
+        this.mBrowserControls = browserControls;
+        this.mCompositorViewHolder = compositorViewHolder;
+        this.mScrimCoordinator = scrim;
+    }
+
+    @Override
+    void initViewImpl(Callback<Boolean> callback) {
+        // If there's a tab, cover it with an overlay.
+        AssistantOverlayModel overlayModel = new AssistantOverlayModel();
+        mOverlayCoordinator = new AssistantOverlayCoordinator(
+                mContext, mBrowserControls, mCompositorViewHolder, mScrimCoordinator, overlayModel);
+        overlayModel.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
+
+        mBottomSheetObserver = new EmptyBottomSheetObserver() {
+            @Override
+            public void onSheetStateChanged(int newState) {
+                if (mOverlayCoordinator == null) {
+                    return;
+                }
+
+                if (newState == SheetState.HIDDEN) {
+                    mOverlayCoordinator.suppress();
+                }
+                if (newState == SheetState.PEEK || newState == SheetState.HALF
+                        || newState == SheetState.FULL) {
+                    mOverlayCoordinator.restore();
+                }
+            }
+        };
+        mController.addObserver(mBottomSheetObserver);
+
+        AssistantBottomBarDelegate delegate = new AssistantBottomBarDelegate() {
+            @Override
+            public boolean onBackButtonPressed() {
+                onUserAction(
+                        /* accept= */ false, callback, OnBoarding.OB_NO_ANSWER,
+                        DropOutReason.ONBOARDING_BACK_BUTTON_CLICKED);
+                return true;
+            }
+
+            @Override
+            public void onBottomSheetClosedWithSwipe() {}
+        };
+        BottomSheetContent currentSheetContent = mController.getCurrentSheetContent();
+        if (currentSheetContent instanceof AssistantBottomSheetContent) {
+            mContent = (AssistantBottomSheetContent) currentSheetContent;
+            mContent.setDelegate(() -> delegate);
+        } else {
+            mContent = new AssistantBottomSheetContent(mContext, () -> delegate);
+        }
+    }
+
+    @Override
+    void showViewImpl() {
+        mContent.setContent(mView, mView);
+        BottomSheetUtils.showContentAndMaybeExpand(
+                mController, mContent, /* shouldExpand = */ true, mAnimate);
+    }
+
+    /**
+     * Transfers ownership of the controls to the caller, returns the overlay coordinator, if one
+     * was created.
+     *
+     * <p>This call is only useful when called from inside a callback provided to {@link #show}, as
+     * before that there are no controls and after that the coordinator automatically hides them.
+     * This call allows callbacks to reuse the controls setup for onboarding and provide a smooth
+     * transition.
+     */
+    @Nullable
+    @Override
+    AssistantOverlayCoordinator transferControls() {
+        assert isInProgress();
+        mContent = null;
+        AssistantOverlayCoordinator coordinator = mOverlayCoordinator;
+        mOverlayCoordinator = null;
+        return coordinator;
+    }
+
+    /** Hides the UI, if one is shown. */
+    @Override
+    void hide() {
+        mController.removeObserver(mBottomSheetObserver);
+        if (mContent != null) {
+            mController.hideContent(mContent, /* animate= */ mAnimate);
+            mContent = null;
+        }
+        if (mOverlayCoordinator != null) {
+            mOverlayCoordinator.destroy();
+            mOverlayCoordinator = null;
+        }
+        destroy();
+    }
+
+    /**
+     * Returns {@code true} between the time {@link #show} is called and the time
+     * the callback has returned.
+     */
+    @VisibleForTesting
+    boolean isInProgress() {
+        return mContent != null;
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
index e58f94e..54e8011 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
@@ -142,6 +142,7 @@
         AssistantRootViewContainer rootViewContainer =
                 (AssistantRootViewContainer) LayoutUtils.createInflater(mContext).inflate(
                         R.layout.autofill_assistant_bottom_sheet_content, /* root= */ null);
+        rootViewContainer.disableTalkbackViewResizing();
         ScrollView scrollableContent = rootViewContainer.findViewById(R.id.scrollable_content);
         rootViewContainer.addView(mHeaderCoordinator.getView(), 0);
         rootViewContainer.addView(mChipsContainer,
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
similarity index 90%
rename from chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java
rename to chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
index edae79f..c2efc0c 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinatorTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
@@ -54,7 +54,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
@@ -66,11 +65,11 @@
 import java.util.List;
 
 /**
- * Tests {@link AssistantOnboardingCoordinator}
+ * Tests {@link BottomSheetOnboardingCoordinator}
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
-public class AssistantOnboardingCoordinatorTest {
+public class BottomSheetOnboardingCoordinatorTest {
     @Rule
     public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
 
@@ -82,7 +81,6 @@
 
     private ChromeActivity mActivity;
     private BottomSheetController mBottomSheetController;
-    private Tab mTab;
     private ScrimCoordinator mScrimCoordinator;
 
     @Before
@@ -91,15 +89,14 @@
         mActivity = mCustomTabActivityTestRule.getActivity();
         mBottomSheetController = TestThreadUtils.runOnUiThreadBlocking(
                 () -> AutofillAssistantUiTestUtil.getBottomSheetController(mActivity));
-        mTab = mActivity.getTabModelSelector().getCurrentTab();
         mScrimCoordinator = mCustomTabActivityTestRule.getActivity()
                                     .getRootUiCoordinatorForTesting()
                                     .getScrimCoordinator();
     }
 
-    private AssistantOnboardingCoordinator createCoordinator(Tab tab) {
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("", new HashMap<String, String>(), mActivity,
+    private BottomSheetOnboardingCoordinator createCoordinator() {
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("", new HashMap<String, String>(), mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
         coordinator.disableAnimationForTesting();
@@ -121,7 +118,7 @@
     private void testOnboarding(@IdRes int buttonToClick, boolean expectAccept) throws Exception {
         AutofillAssistantPreferencesUtil.setInitialPreferences(!expectAccept);
 
-        AssistantOnboardingCoordinator coordinator = createCoordinator(mTab);
+        BottomSheetOnboardingCoordinator coordinator = createCoordinator();
         showOnboardingAndWait(coordinator, mCallback);
 
         assertTrue(TestThreadUtils.runOnUiThreadBlocking(coordinator::isInProgress));
@@ -136,7 +133,7 @@
     @Test
     @MediumTest
     public void testOnboardingWithNoTabs() {
-        AssistantOnboardingCoordinator coordinator = createCoordinator(/* tab= */ null);
+        BottomSheetOnboardingCoordinator coordinator = createCoordinator();
         showOnboardingAndWait(coordinator, mCallback);
 
         onView(withId(R.id.button_init_ok)).perform(click());
@@ -147,7 +144,7 @@
     @Test
     @MediumTest
     public void testTransferControls() throws Exception {
-        AssistantOnboardingCoordinator coordinator = createCoordinator(mTab);
+        BottomSheetOnboardingCoordinator coordinator = createCoordinator();
 
         List<AssistantOverlayCoordinator> capturedOverlays =
                 Collections.synchronizedList(new ArrayList<>());
@@ -173,7 +170,7 @@
     @Test
     @MediumTest
     public void testShownFlag() {
-        AssistantOnboardingCoordinator coordinator = createCoordinator(/* tab= */ null);
+        BottomSheetOnboardingCoordinator coordinator = createCoordinator();
         assertFalse(coordinator.getOnboardingShown());
 
         showOnboardingAndWait(coordinator, mCallback);
@@ -187,8 +184,8 @@
 
         HashMap<String, String> parameters = new HashMap();
         parameters.put("INTENT", "RENT_CAR");
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("", parameters, mActivity,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("", parameters, mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
         coordinator.disableAnimationForTesting();
@@ -211,8 +208,8 @@
 
         HashMap<String, String> parameters = new HashMap();
         parameters.put("INTENT", "BUY_MOVIE_TICKET");
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("4363482", parameters, mActivity,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("4363482", parameters, mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
         coordinator.disableAnimationForTesting();
@@ -234,8 +231,8 @@
         AutofillAssistantPreferencesUtil.setInitialPreferences(true);
 
         HashMap<String, String> parameters = new HashMap();
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("", parameters, mActivity,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("", parameters, mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
         coordinator.disableAnimationForTesting();
@@ -257,8 +254,8 @@
 
         HashMap<String, String> parameters = new HashMap<>();
         parameters.put("ONBOARDING_FETCH_TIMEOUT_MS", "0");
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("", parameters, mActivity,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("", parameters, mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
 
@@ -309,8 +306,8 @@
 
         HashMap<String, String> parameters = new HashMap<>();
         parameters.put("ONBOARDING_FETCH_TIMEOUT_MS", "0");
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("", parameters, mActivity,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("", parameters, mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
 
@@ -335,8 +332,8 @@
 
         HashMap<String, String> parameters = new HashMap<>();
         parameters.put("ONBOARDING_FETCH_TIMEOUT_MS", "0");
-        AssistantOnboardingCoordinator coordinator =
-                new AssistantOnboardingCoordinator("", parameters, mActivity,
+        BottomSheetOnboardingCoordinator coordinator =
+                new BottomSheetOnboardingCoordinator("", parameters, mActivity,
                         mBottomSheetController, mActivity.getBrowserControlsManager(),
                         mActivity.getCompositorViewHolder(), mScrimCoordinator);
 
@@ -373,7 +370,7 @@
 
     /** Trigger onboarding and wait until it is fully displayed. */
     private void showOnboardingAndWait(
-            AssistantOnboardingCoordinator coordinator, Callback<Boolean> callback) {
+            BottomSheetOnboardingCoordinator coordinator, Callback<Boolean> callback) {
         TestThreadUtils.runOnUiThreadBlocking(() -> coordinator.show(callback));
         waitUntilViewMatchesCondition(withId(R.id.button_init_ok), isCompletelyDisplayed());
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
index 3bcf3608..60966b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.autofill;
 
+import android.app.Activity;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -26,6 +27,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
+import org.chromium.chrome.browser.profiles.Profile;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -61,6 +64,16 @@
         int NOT_ENOUGH_INFO = 6;
         int NONE = 7;
     }
+
+    /**
+     * Launches the Autofill help page on top of the current @{link android.app.Activity} and
+     * current @{link Profile}.
+     */
+    public static void launchAutofillHelpPage(Activity activity, Profile profile) {
+        HelpAndFeedbackLauncherImpl.getInstance().show(
+                activity, activity.getString(R.string.help_context_autofill), profile, null);
+    }
+
     /**
      * Show Tooltip UI.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
index bfed528..0cc03b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
@@ -9,7 +9,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
@@ -42,8 +41,8 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.settings.CreditCardNumberFormattingTextWatcher;
-import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.widget.AlwaysDismissedDialog;
@@ -76,7 +75,7 @@
 
     private static EditorObserverForTest sObserverForTest;
 
-    private final Context mContext;
+    private final Activity mActivity;
     private final Handler mHandler;
     private final TextView.OnEditorActionListener mEditorActionListener;
     private final int mHalfRowMargin;
@@ -115,7 +114,7 @@
         super(activity, R.style.Theme_Chromium_Fullscreen);
         // Sets transparent background for animating content view.
         getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-        mContext = activity;
+        mActivity = activity;
         mHandler = new Handler();
         mIsDismissed = false;
         mEditorActionListener = new TextView.OnEditorActionListener() {
@@ -172,12 +171,6 @@
         getWindow().setAttributes(attributes);
     }
 
-    /** Launches the Autofill help page on top of the current Context and current Profile. */
-    public static void launchAutofillHelpPage(Context context, Profile profile) {
-        HelpAndFeedbackLauncherImpl.getInstance().show((Activity) context,
-                context.getString(R.string.help_context_autofill), profile, null);
-    }
-
     /**
      * Prepares the toolbar for use.
      *
@@ -202,7 +195,7 @@
                     mDeleteRunnable.run();
                     animateOutDialog();
                 } else if (item.getItemId() == R.id.help_menu_id) {
-                    launchAutofillHelpPage(mContext, mProfile);
+                    AutofillUiUtils.launchAutofillHelpPage(mActivity, mProfile);
                 }
                 return true;
             }
@@ -406,7 +399,7 @@
                 addFieldViewToEditor(mDataView, fieldModel);
             } else {
                 // Create a LinearLayout to put it and the next view side by side.
-                LinearLayout rowLayout = new LinearLayout(mContext);
+                LinearLayout rowLayout = new LinearLayout(mActivity);
                 mDataView.addView(rowLayout);
 
                 View firstView = addFieldViewToEditor(rowLayout, fieldModel);
@@ -448,9 +441,9 @@
         View childView = null;
 
         if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_ICONS) {
-            childView = new EditorIconsField(mContext, parent, fieldModel).getLayout();
+            childView = new EditorIconsField(mActivity, parent, fieldModel).getLayout();
         } else if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_LABEL) {
-            childView = new EditorLabelField(mContext, parent, fieldModel).getLayout();
+            childView = new EditorLabelField(mActivity, parent, fieldModel).getLayout();
         } else if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN) {
             Runnable prepareEditorRunnable = new Runnable() {
                 @Override
@@ -465,7 +458,7 @@
                 }
             };
             EditorDropdownField dropdownView =
-                    new EditorDropdownField(mContext, parent, fieldModel, prepareEditorRunnable);
+                    new EditorDropdownField(mActivity, parent, fieldModel, prepareEditorRunnable);
             mFieldViews.add(dropdownView);
             mDropdownFields.add(dropdownView.getDropdown());
 
@@ -475,7 +468,7 @@
             checkbox.setId(R.id.payments_edit_checkbox);
             checkbox.setText(fieldModel.getLabel());
             checkbox.setChecked(fieldModel.isChecked());
-            checkbox.setMinimumHeight(mContext.getResources().getDimensionPixelSize(
+            checkbox.setMinimumHeight(mActivity.getResources().getDimensionPixelSize(
                     R.dimen.editor_dialog_checkbox_min_height));
             checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                 @Override
@@ -499,7 +492,7 @@
             }
 
             EditorTextField inputLayout = new EditorTextField(
-                    mContext, fieldModel, mEditorActionListener, filter, formatter);
+                    mActivity, fieldModel, mEditorActionListener, filter, formatter);
             mFieldViews.add(inputLayout);
 
             EditText input = inputLayout.getEditText();
@@ -527,15 +520,15 @@
      */
     public void show(EditorModel editorModel) {
         // If an asynchronous task calls show, while the activity is already finishing, return.
-        if (((Activity) mContext).isFinishing()) return;
+        if (mActivity.isFinishing()) return;
 
         setOnShowListener(this);
         setOnDismissListener(this);
         mEditorModel = editorModel;
-        mLayout = LayoutInflater.from(mContext).inflate(R.layout.payment_request_editor, null);
+        mLayout = LayoutInflater.from(mActivity).inflate(R.layout.payment_request_editor, null);
         setContentView(mLayout);
 
-        mFooter = LayoutInflater.from(mContext).inflate(
+        mFooter = LayoutInflater.from(mActivity).inflate(
                 R.layout.editable_option_editor_footer, null, false);
 
         prepareToolbar();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
index 3a39dc4..2986be5d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
@@ -27,7 +27,7 @@
 import androidx.fragment.app.Fragment;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
+import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
@@ -93,8 +93,10 @@
             deleteEntry();
             getActivity().finish();
             return true;
-        } else if (item.getItemId() == R.id.help_menu_id) {
-            EditorDialog.launchAutofillHelpPage(getActivity(), Profile.getLastUsedRegularProfile());
+        }
+        if (item.getItemId() == R.id.help_menu_id) {
+            AutofillUiUtils.launchAutofillHelpPage(
+                    getActivity(), Profile.getLastUsedRegularProfile());
             return true;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
index 0e938171..84f2b351 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
@@ -10,6 +10,9 @@
 import android.hardware.biometrics.BiometricManager;
 import android.os.Build;
 import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
@@ -19,10 +22,12 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
 import org.chromium.chrome.browser.payments.AndroidPaymentAppFactory;
 import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 
@@ -37,7 +42,7 @@
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         getActivity().setTitle(R.string.autofill_payment_methods);
-
+        setHasOptionsMenu(true);
         PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getStyledContext());
         // Suppresses unwanted animations while Preferences are removed from and re-added to the
         // screen.
@@ -47,6 +52,24 @@
     }
 
     @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.clear();
+        MenuItem help =
+                menu.add(Menu.NONE, R.id.menu_id_targeted_help, Menu.NONE, R.string.menu_help);
+        help.setIcon(R.drawable.ic_help_and_feedback);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.menu_id_targeted_help) {
+            AutofillUiUtils.launchAutofillHelpPage(
+                    getActivity(), Profile.getLastUsedRegularProfile());
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         // Always rebuild our list of credit cards.  Although we could detect if credit cards are
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
index 3a13b0a..e3ac6c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
@@ -8,6 +8,9 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -17,6 +20,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.StrictModeContext;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
@@ -41,7 +45,7 @@
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         getActivity().setTitle(R.string.autofill_addresses_settings_title);
-
+        setHasOptionsMenu(true);
         PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getStyledContext());
         // Suppresses unwanted animations while Preferences are removed from and re-added to the
         // screen.
@@ -51,6 +55,24 @@
     }
 
     @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.clear();
+        MenuItem help =
+                menu.add(Menu.NONE, R.id.menu_id_targeted_help, Menu.NONE, R.string.menu_help);
+        help.setIcon(R.drawable.ic_help_and_feedback);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.menu_id_targeted_help) {
+            AutofillUiUtils.launchAutofillHelpPage(
+                    getActivity(), Profile.getLastUsedRegularProfile());
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         // Always rebuild our list of profiles.  Although we could detect if profiles are added or
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
index 15e9fc8a..2db3148 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
@@ -91,6 +91,7 @@
     private final LensQueryResult mLensQueryResultWithShoppingItent =
             (new LensQueryResult.Builder()).withIsShoppyIntent(true).build();
     private boolean mEnableLensWithSearchByImageText;
+    private boolean mIsLensIntentInProgress;
     private @Nullable UkmRecorder.Bridge mUkmRecorderBridge;
     private ContextMenuNativeDelegate mNativeDelegate;
     private static final String LENS_SEARCH_MENU_ITEM_KEY = "searchWithGoogleLensMenuItem";
@@ -774,6 +775,11 @@
                     TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile());
             if (tracker.isInitialized()) tracker.dismissed(FeatureConstants.EPHEMERAL_TAB_FEATURE);
         }
+
+        if (!mIsLensIntentInProgress) {
+            // TODO(crbug/1158604): Remove leftover Lens dependencies.
+            LensUtils.terminateLensConnectionsIfNecessary(mItemDelegate.isIncognito());
+        }
     }
 
     private WindowAndroid getWindow() {
@@ -834,6 +840,7 @@
      */
     protected void searchWithGoogleLens(
             boolean requiresConfirmation, @Nullable LensQueryResult lensQueryResult) {
+        mIsLensIntentInProgress = true;
         mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, (Uri imageUri) -> {
             ShareHelper.shareImageWithGoogleLens(getWindow(), imageUri, mItemDelegate.isIncognito(),
                     mParams.getSrcUrl(), mParams.getTitleText(), mParams.getPageUrl(),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index 6e8d394..f3ecd364 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -224,6 +224,8 @@
         Log.i(TAG, "Created mCurrentContextMenu: " + mCurrentContextMenu);
         Log.i(TAG, "Activity was " + mWindow.getActivity().get() + " when the menu was created.");
 
+        // TODO(crbug/1158604): Remove leftover Lens dependencies.
+        LensUtils.startLensConnectionIfNecessary(mIsIncognito);
         if (mChipDelegate != null) {
             menuCoordinator.displayMenuWithChip(mWindow, mWebContents, mCurrentContextMenuParams,
                     items, mCallback, mOnMenuShown, mOnMenuClosed, mChipDelegate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
index ba6c035..0fa34357 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
@@ -158,7 +158,9 @@
         intent.setPackage(context.getPackageName());
         intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
         intent.putExtra(CustomTabIntentDataProvider.EXTRA_UI_TYPE, CustomTabsUiType.OFFLINE_PAGE);
-        intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, isIncognito);
+        // TODO(crbug.com/1148275): Pass isIncognito boolean here after finding a way not to
+        // reload the downloaded page for Incognito CCT.
+        intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, false);
 
         IntentHandler.addTrustedIntentExtras(intent);
         if (!(context instanceof Activity)) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
index 538ffe98..2f7a945 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
@@ -16,6 +16,8 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.gsa.GSAState;
+import org.chromium.chrome.browser.lens.LensController;
+import org.chromium.chrome.browser.lens.LensIntentParams;
 import org.chromium.chrome.browser.lens.LensQueryResult;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -28,6 +30,7 @@
 /**
  * This class provides utilities for intenting into Google Lens.
  */
+// TODO(crbug/1157496): Consolidate param-checks into a single function.
 public class LensUtils {
     private static final String LENS_CONTRACT_URI = "googleapp://lens";
     private static final String LENS_DIRECT_INTENT_CONTRACT_URI = "google://lens";
@@ -47,6 +50,8 @@
             "minAgsaVersionNameForShopping";
     private static final String MIN_AGSA_VERSION_DIRECT_INTENT_FEATURE_PARAM_NAME =
             "minAgsaVersionForDirectIntent";
+    private static final String MIN_AGSA_VERSION_DIRECT_INTENT_SDK_FEATURE_PARAM_NAME =
+            "minAgsaVersionForDirectIntentSdk";
     private static final String USE_SEARCH_BY_IMAGE_TEXT_FEATURE_PARAM_NAME =
             "useSearchByImageText";
     private static final String LENS_SHOPPING_ALLOWLIST_ENTRIES_FEATURE_PARAM_NAME =
@@ -58,12 +63,15 @@
     private static final String SEND_ALT_PARAM_NAME = "sendAlt";
     private static final String SEND_PAGE_PARAM_NAME = "sendPage";
     private static final String USE_DIRECT_INTENT_FEATURE_PARAM_NAME = "useDirectIntent";
+    private static final String USE_DIRECT_INTENT_SDK_INTEGRATION_PARAM_NAME =
+            "useDirectIntentSdkIntegration";
     private static final String DISABLE_ON_INCOGNITO_PARAM_NAME = "disableOnIncognito";
     private static final String ORDER_SHARE_IMAGE_BEFORE_LENS_PARAM_NAME =
             "orderShareImageBeforeLens";
     private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE = "10.65";
     private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT = "11.16";
     private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_DIRECT_INTENT = "11.34";
+    private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_DIRECT_INTENT_SDK = "11.39.7";
     private static final int LENS_INTENT_TYPE_LENS_CHROME_SHOPPING = 18;
     private static final String LENS_SHOPPING_FEATURE_FLAG_VARIANT_NAME = "lensShopVariation";
     private static final String LENS_DEFAULT_SHOPPING_URL_PATTERNS =
@@ -221,6 +229,32 @@
     }
 
     /**
+     * Gets the minimum AGSA version required to support the direct intent SDK
+     * integration on this device. Takes the value from a server provided value if a
+     * field trial is active but otherwise will take the value from a client side
+     * default (unless the lens feature is not enabled at all, in which case return
+     * an empty string).
+     *
+     * @return The minimum version name string or an empty string if not available.
+     */
+    public static String getMinimumAgsaVersionForDirectIntentSdkSupport() {
+        // Shopping feature AGSA version takes priority over Search with Google Lens
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS)) {
+            final String serverProvidedMinAgsaVersion =
+                    ChromeFeatureList.getFieldTrialParamByFeature(
+                            ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS,
+                            MIN_AGSA_VERSION_DIRECT_INTENT_SDK_FEATURE_PARAM_NAME);
+            if (TextUtils.isEmpty(serverProvidedMinAgsaVersion)) {
+                // Falls into this block if the user enabled the feature using chrome://flags
+                // and the param was not set by the server.
+                return MIN_AGSA_VERSION_NAME_FOR_LENS_DIRECT_INTENT_SDK;
+            }
+            return serverProvidedMinAgsaVersion;
+        }
+        return "";
+    }
+
+    /**
      * Checks whether the device is below Android O. We restrict to these versions
      * to limit to OS"s where image processing vulnerabilities can be retroactively
      * fixed if they are discovered in the future.
@@ -342,6 +376,59 @@
         return intent;
     }
 
+    /**
+     * Start an early Lens AGSA connection if feature parameter is enabled and client is not
+     * incognito. Eligibity checks happen in LensController.
+     *
+     * @param isIncognito Whether the client is incognito
+     */
+    public static void startLensConnectionIfNecessary(boolean isIncognito) {
+        // TODO(crbug/1157543): Pass isIncognito through to LensController.
+        if (!isIncognito) {
+            LensController.getInstance().startLensConnection();
+        }
+    }
+
+    /**
+     * Terminate an early Lens AGSA connection if feature parameter is enabled and client is not
+     * incognito. Eligibity checks happen in LensController.
+     *
+     * @param isIncognito Whether the client is incognito
+     */
+    public static void terminateLensConnectionsIfNecessary(boolean isIncognito) {
+        // TODO(crbug/1157543): Pass isIncognito through to LensController.
+        if (!isIncognito) {
+            LensController.getInstance().terminateLensConnections();
+        }
+    }
+
+    /**
+     * Build a LensIntentParams object from the provided parameters in order to intent into Lens.
+     *
+     * @param imageUri         The content provider URI generated by chrome (or
+     *                         empty URI) if only resolving the activity.
+     * @param isIncognito      Whether the current tab is in incognito mode.
+     * @param srcUrl           The 'src' attribute of the image.
+     * @param titleOrAltText   The 'title' or, if empty, the 'alt' attribute of the
+     *                         image.
+     * @param pageUrl          The url of the top level frame of the page.
+     * @param requiresConfirmation Whether the request requires an confirmation dialog.
+     * @return The intent parameters to intent to Google Lens.
+     */
+    public static LensIntentParams buildLensIntentParams(final Uri imageUri,
+            final boolean isIncognito, final String srcUrl, final String titleOrAltText,
+            final String pageUrl, final boolean requiresConfirmation) {
+        LensIntentParams.Builder intentParamsBuilder = new LensIntentParams.Builder();
+        return intentParamsBuilder.withImageUri(imageUri)
+                .withIsIncognito(isIncognito)
+                .withRequiresConfirmation(requiresConfirmation)
+                .withIntentType(getLensShoppingIntentType())
+                .withImageTitleOrAltText(titleOrAltText)
+                .withSrcUrl(srcUrl)
+                .withPageUrl(pageUrl)
+                .build();
+    }
+
     public static boolean isGoogleLensFeatureEnabled(boolean isIncognito) {
         return ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS)
                 && !(isIncognito
@@ -380,6 +467,24 @@
     }
 
     /**
+     * Enables the starting of LenActivity directly, rather than going through the Lens
+     * session running in AGSA. Also checks if the required AGSA version for direct intent
+     * is below or equal to the provided version.
+     */
+    public static boolean useDirectIntentSdkIntegration(final Context context) {
+        // TODO(https://crbug.com/1146591): Refactor GSA state checks to avoid multiple version
+        // grabs.
+        String agsaVersionName = sFakeInstalledAgsaVersion != null
+                ? sFakeInstalledAgsaVersion
+                : getLensActivityVersionNameIfAvailable(context);
+        return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                       ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS,
+                       USE_DIRECT_INTENT_SDK_INTEGRATION_PARAM_NAME, false)
+                && !GSAState.getInstance(context).isAgsaVersionBelowMinimum(
+                        agsaVersionName, getMinimumAgsaVersionForDirectIntentSdkSupport());
+    }
+
+    /**
      * Whether to display the lens menu item with the search by image text
      */
     public static boolean useLensWithSearchByImageText() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
index 8f20213..5aa434a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
@@ -27,6 +27,8 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.lens.LensController;
+import org.chromium.chrome.browser.lens.LensIntentParams;
 import org.chromium.chrome.browser.lens.LensQueryResult;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -134,19 +136,27 @@
     public static void shareImageWithGoogleLens(final WindowAndroid window, Uri imageUri,
             boolean isIncognito, GURL srcUrl, String titleOrAltText, GURL pageUrl,
             LensQueryResult lensQueryResult, boolean requiresConfirmation) {
-        Intent shareIntent =
-                LensUtils.getShareWithGoogleLensIntent(ContextUtils.getApplicationContext(),
-                        imageUri, isIncognito, SystemClock.elapsedRealtimeNanos(), srcUrl,
-                        titleOrAltText, pageUrl, lensQueryResult, requiresConfirmation);
-        try {
-            // Pass an empty callback to ensure the triggered activity can identify the source
-            // of the intent (startActivityForResult allows app identification).
-            fireIntent(window, shareIntent, (w, resultCode, data) -> {});
-        } catch (ActivityNotFoundException e) {
-            // The initial version check should guarantee that the activity is available. However,
-            // the exception may be thrown in test environments after mocking out the version check.
-            if (Boolean.TRUE.equals(sIgnoreActivityNotFoundException)) return;
-            throw e;
+        if (LensUtils.useDirectIntentSdkIntegration(ContextUtils.getApplicationContext())) {
+            LensIntentParams intentParams = LensUtils.buildLensIntentParams(imageUri, isIncognito,
+                    srcUrl.getValidSpecOrEmpty(), titleOrAltText, pageUrl.getValidSpecOrEmpty(),
+                    requiresConfirmation);
+            LensController.getInstance().startLens(window, intentParams);
+        } else {
+            Intent shareIntent =
+                    LensUtils.getShareWithGoogleLensIntent(ContextUtils.getApplicationContext(),
+                            imageUri, isIncognito, SystemClock.elapsedRealtimeNanos(), srcUrl,
+                            titleOrAltText, pageUrl, lensQueryResult, requiresConfirmation);
+            try {
+                // Pass an empty callback to ensure the triggered activity can identify the source
+                // of the intent (startActivityForResult allows app identification).
+                fireIntent(window, shareIntent, (w, resultCode, data) -> {});
+            } catch (ActivityNotFoundException e) {
+                // The initial version check should guarantee that the activity is available.
+                // However, the exception may be thrown in test environments after mocking out the
+                // version check.
+                if (Boolean.TRUE.equals(sIgnoreActivityNotFoundException)) return;
+                throw e;
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelperProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelperProvider.java
index 86e7be8..27143f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelperProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelperProvider.java
@@ -8,6 +8,7 @@
 
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
+import org.chromium.chrome.browser.signin.services.SigninHelper;
 import org.chromium.chrome.browser.signin.services.SigninPreferencesManager;
 import org.chromium.chrome.browser.sync.SyncController;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetCoordinator.java
index 1733431..730eedfe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetCoordinator.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.incognito.interstitial.IncognitoInterstitialCoordinator;
 import org.chromium.chrome.browser.incognito.interstitial.IncognitoInterstitialDelegate;
 import org.chromium.chrome.browser.signin.services.SigninMetricsUtils;
+import org.chromium.chrome.browser.signin.services.SigninPreferencesManager;
 import org.chromium.chrome.browser.signin.ui.account_picker.AccountPickerDelegate;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -81,7 +82,10 @@
             BottomSheetController bottomSheetController,
             AccountPickerDelegate accountPickerDelegate,
             IncognitoInterstitialDelegate incognitoInterstitialDelegate) {
+        SigninPreferencesManager.getInstance().incrementAccountPickerBottomSheetShownCount();
         SigninMetricsUtils.logAccountConsistencyPromoAction(AccountConsistencyPromoAction.SHOWN);
+        SigninMetricsUtils.logAccountConsistencyPromoShownCount(
+                "Signin.AccountConsistencyPromoAction.Shown.Count");
 
         mAccountPickerBottomSheetMediator = new AccountPickerBottomSheetMediator(
                 activity, accountPickerDelegate, this::dismissBottomSheet);
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 fe85bdb7..7eb5e0e 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
@@ -222,6 +222,8 @@
 
     private void signIn() {
         mModel.set(AccountPickerBottomSheetProperties.VIEW_STATE, ViewState.SIGNIN_IN_PROGRESS);
+        SigninMetricsUtils.logAccountConsistencyPromoShownCount(
+                "Signin.AccountConsistencyPromoAction.SignedIn.Count");
         if (TextUtils.equals(mSelectedAccountName, mAddedAccountName)) {
             SigninMetricsUtils.logAccountConsistencyPromoAction(
                     AccountConsistencyPromoAction.SIGNED_IN_WITH_ADDED_ACCOUNT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index c4d64df..4a25aca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -205,9 +205,11 @@
         mTurnOffSync.setOnPreferenceClickListener(
                 SyncSettingsUtils.toOnClickListener(this, this::onTurnOffSyncClicked));
 
+        Profile profile = Profile.getLastUsedRegularProfile();
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
                 && !mIsFromSigninScreen) {
-            mTurnOffSync.setVisible(true);
+            // Child profiles should not be able to sign out.
+            mTurnOffSync.setVisible(!profile.isChild());
             findPreference(PREF_ADVANCED_CATEGORY).setVisible(true);
 
             /**
@@ -239,7 +241,6 @@
             type.setOnPreferenceChangeListener(this);
         }
 
-        Profile profile = Profile.getLastUsedRegularProfile();
         if (profile.isChild()) {
             mGoogleActivityControls.setSummary(
                     R.string.sign_in_google_activity_controls_summary_child_account);
@@ -392,7 +393,9 @@
         PersonalDataManager.setPaymentsIntegrationEnabled(mSyncEverything.isChecked()
                 || (mSyncPaymentsIntegration.isChecked() && mSyncAutofill.isChecked()));
 
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)) {
+        // For child profiles sync should always be on.
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+                && !Profile.getLastUsedRegularProfile().isChild()) {
             boolean atLeastOneDataTypeEnabled =
                     mSyncEverything.isChecked() || selectedModelTypes.size() > 0;
             if (mProfileSyncService.isSyncRequested() && !atLeastOneDataTypeEnabled) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
index a4c33d4..8d04f49e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
@@ -36,6 +36,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
@@ -451,6 +452,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "https://crbug.com/1159767")
     public void testRedirectionFromIntentWarm() throws Exception {
         Context context = ContextUtils.getApplicationContext();
         // TODO(crbug.com/1153686): This test times out on M tablets.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java
index 3c0ffd68..a95ae50b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java
@@ -29,6 +29,7 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.lens.LensIntentParams;
 import org.chromium.chrome.browser.lens.LensQueryResult;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -914,6 +915,36 @@
         assertTrue(isInShoppingAllowlistOnUiThread(googleShoppingItemUrl));
     }
 
+    /**
+     * Test {@link LensUtils#buildLensIntentParams()} method for building intent parameters for
+     * startng Lens.
+     */
+    @Test
+    @SmallTest
+    public void buildLensIntentParamsTest() {
+        final String contentUrl = "content://image-url";
+        final boolean isIncognito = false;
+        final String srcUrl = "https://www.google.com";
+        final String titleOrAltText = "Image Title";
+        final String pageUrl = "https://www.google.com";
+        final boolean requiresConfirmation = false;
+        LensIntentParams lensIntentParams = LensUtils.buildLensIntentParams(Uri.parse(contentUrl),
+                isIncognito, srcUrl, titleOrAltText, pageUrl, requiresConfirmation);
+
+        Assert.assertEquals("Lens intent parameters has incorrect image URI.", contentUrl,
+                lensIntentParams.getImageUri().toString());
+        Assert.assertEquals("Lens intent parameters has incorrect incognito value.", isIncognito,
+                lensIntentParams.getIsIncognito());
+        Assert.assertEquals("Lens intent parameters has incorrect src URL.", srcUrl,
+                lensIntentParams.getSrcUrl());
+        Assert.assertEquals("Lens intent parameters has incorrect title or alt text.",
+                titleOrAltText, lensIntentParams.getImageTitleOrAltText());
+        Assert.assertEquals("Lens intent parameters has incorrect page URL.", pageUrl,
+                lensIntentParams.getPageUrl());
+        Assert.assertEquals("Lens intent parameters has incorrect requires confirmation value.",
+                requiresConfirmation, lensIntentParams.getRequiresConfirmation());
+    }
+
     private boolean isInShoppingAllowlistOnUiThread(GURL imageUri) {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> LensUtils.isInShoppingAllowlist(imageUri));
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 888c675..bd69bf9 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
@@ -33,6 +33,7 @@
 import androidx.test.filters.MediumTest;
 
 import org.hamcrest.Matcher;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.ClassRule;
@@ -54,6 +55,8 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.incognito.interstitial.IncognitoInterstitialDelegate;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerBottomSheetCoordinator;
 import org.chromium.chrome.browser.signin.ui.account_picker.AccountPickerDelegate;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
@@ -98,6 +101,8 @@
                     /* accountName= */ "test.account2@gmail.com", /* avatar= */ null,
                     /* fullName= */ null, /* givenName= */ null);
 
+    private final int mShowCount = 1;
+
     // Disable animations to reduce flakiness.
     @ClassRule
     public static final DisableAnimationsTestRule sNoAnimationsRule =
@@ -144,6 +149,14 @@
         when(mAccountPickerDelegateMock.isIncognitoModeEnabled()).thenReturn(true);
         mAccountManagerTestRule.addAccount(PROFILE_DATA1);
         mAccountManagerTestRule.addAccount(PROFILE_DATA2);
+        SharedPreferencesManager.getInstance().removeKey(
+                ChromePreferenceKeys.ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT);
+    }
+
+    @After
+    public void tearDown() {
+        SharedPreferencesManager.getInstance().removeKey(
+                ChromePreferenceKeys.ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT);
     }
 
     @Test
@@ -151,9 +164,25 @@
     public void testCollapsedSheetWithAccount() {
         MetricsUtils.HistogramDelta accountConsistencyHistogram = new HistogramDelta(
                 "Signin.AccountConsistencyPromoAction", AccountConsistencyPromoAction.SHOWN);
+        MetricsUtils.HistogramDelta shownCountHistogram =
+                new HistogramDelta("Signin.AccountConsistencyPromoAction.Shown.Count", mShowCount);
         buildAndShowCollapsedBottomSheet();
         checkCollapsedAccountList(PROFILE_DATA1);
         Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
+        Assert.assertEquals(1, shownCountHistogram.getDelta());
+    }
+
+    @Test
+    @MediumTest
+    public void testPromoShownHistogramMaxCount() {
+        final int max = 100;
+        SharedPreferencesManager.getInstance().writeInt(
+                ChromePreferenceKeys.ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT, max + 5);
+        MetricsUtils.HistogramDelta shownCountHistogram =
+                new HistogramDelta("Signin.AccountConsistencyPromoAction.Shown.Count", max);
+        buildAndShowCollapsedBottomSheet();
+        checkCollapsedAccountList(PROFILE_DATA1);
+        Assert.assertEquals(1, shownCountHistogram.getDelta());
     }
 
     @Test
@@ -352,9 +381,12 @@
         MetricsUtils.HistogramDelta accountConsistencyHistogram =
                 new HistogramDelta("Signin.AccountConsistencyPromoAction",
                         AccountConsistencyPromoAction.SIGNED_IN_WITH_DEFAULT_ACCOUNT);
+        MetricsUtils.HistogramDelta signedInCountHistogram = new HistogramDelta(
+                "Signin.AccountConsistencyPromoAction.SignedIn.Count", mShowCount);
         buildAndShowCollapsedBottomSheet();
         clickContinueButtonAndCheckSignInInProgressSheet();
         Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
+        Assert.assertEquals(1, signedInCountHistogram.getDelta());
     }
 
     @Test
@@ -363,12 +395,15 @@
         MetricsUtils.HistogramDelta accountConsistencyHistogram =
                 new HistogramDelta("Signin.AccountConsistencyPromoAction",
                         AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT);
+        MetricsUtils.HistogramDelta signedInCountHistogram = new HistogramDelta(
+                "Signin.AccountConsistencyPromoAction.SignedIn.Count", mShowCount);
         buildAndShowExpandedBottomSheet();
         onView(withText(PROFILE_DATA2.getAccountEmail())).perform(click());
         CriteriaHelper.pollUiThread(mCoordinator.getBottomSheetViewForTesting().findViewById(
                 R.id.account_picker_selected_account)::isShown);
         clickContinueButtonAndCheckSignInInProgressSheet();
         Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
+        Assert.assertEquals(1, signedInCountHistogram.getDelta());
     }
 
     @Test
@@ -383,6 +418,8 @@
         MetricsUtils.HistogramDelta signedInWithNonDefaultAccountHistogram =
                 new HistogramDelta("Signin.AccountConsistencyPromoAction",
                         AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT);
+        MetricsUtils.HistogramDelta signedInCountHistogram = new HistogramDelta(
+                "Signin.AccountConsistencyPromoAction.SignedIn.Count", mShowCount);
         buildAndShowExpandedBottomSheet();
         onVisibleView(withText(R.string.signin_add_account_to_device)).perform(click());
         verify(mAccountPickerDelegateMock).addAccount(callbackArgumentCaptor.capture());
@@ -396,6 +433,7 @@
         Assert.assertEquals(1, addAccountHistogram.getDelta());
         Assert.assertEquals(1, signedInWithAddedAccountHistogram.getDelta());
         Assert.assertEquals(0, signedInWithNonDefaultAccountHistogram.getDelta());
+        Assert.assertEquals(1, signedInCountHistogram.getDelta());
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java
index be7b8a5..de307bc5b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java
@@ -14,6 +14,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.Batch;
+import org.chromium.chrome.browser.signin.services.SigninHelper;
 import org.chromium.chrome.browser.signin.services.SigninPreferencesManager;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java
index 54d4930..65e13b14 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java
@@ -23,9 +23,9 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.SigninHelper;
 import org.chromium.chrome.browser.signin.SigninHelperProvider;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
+import org.chromium.chrome.browser.signin.services.SigninHelper;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.signin.MockChangeEventChecker;
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 115fc5cc..0a573a32 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-89.0.4351.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-89.0.4357.3_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index a000d7d..c795c1d 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -559,20 +559,23 @@
   <message name="IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT_AND_DEVICE" desc="Label for an undo tooltip following deletion of a password from the account and the device.">
     Password deleted from this device and your Google Account
   </message>
-    <!-- TODO(crbug.com/1139263): Make it translateable and add translation screenshots once final mocks are available. -->
-  <message translateable="false" name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT" desc="Message displayed in the moving multiple passwords to the user Google Account banner.">
-    Move them to your Google Account, and make it across-device accessible
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT" desc="Message displayed in the moving multiple passwords to the user Google Account banner.">
+    To use them on all your devices, move them to your Google Account
   </message>
-  <!-- TODO(crbug.com/1139263): Make it translateable and add translation screenshots once final mocks are available. -->
-  <message translateable="false" name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT" desc="Message shown in the footer of the moving multiple password to the Google Account dialog. It reflects how many passwords are selected and will be movec.">
+   <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT" desc="Label shown in the link that open moving multiple password dialog.">
     {COUNT, plural,
-      =1 {You are moving {COUNT} password from your device to your Google Account}
-      other {You are moving {COUNT} passwords from your device to your Google Account}}
+      =1 {{COUNT} password is accessible on this device}
+      other {{COUNT} passwords are accessible on this device}}
   </message>
-  <!-- TODO(crbug.com/1139263): Make it translateable and add translation screenshots once final mocks are available. -->
-  <message translateable="false" name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE" desc="Title for the dialog that asks the user to select which passwords to move to their Google Account.">
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER" desc="Footer for the dialog that asks the user to select which passwords to move to their Google Account.">
+    Passwords will be moved from your device to your Google Account
+  </message>
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE" desc="Title for the dialog that asks the user to select which passwords to move to their Google Account.">
     Choose which passwords to move
   </message>
+  <message name="IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_SNACKBAR" desc="Text in the snackbar that appears after the user successfuly moved local passwords to their Google Account.">
+    Passwords moved to your Google Account
+  </message>
   <message name="IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_TITLE" desc="Title for the dialog that confirms whether the user wishes to move a password to their Google Account.">
     Move to Google Account?
   </message>
@@ -585,9 +588,8 @@
   <message name="IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT" desc="Text for the button that cancels the action of moving a password to the user Google Account.">
     No thanks
   </message>
-  <!-- TODO(crbug.com/1139263): Make it translateable and add translation screenshots once final mocks are available. -->
-  <message translateable="false" name="IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT" desc="Text for the button that opens the dialog that moves multiple passwords to the user Google Account.">
-    Move Passwords ...
+  <message name="IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT" desc="Text for the button that opens the dialog that moves multiple passwords to the user Google Account.">
+    Move passwords
   </message>
   <message name="IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_TITLE" desc="Title for the dialog that asks the user which versions of a password to remove (device, Google Account or both).">
     Delete password?
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1
new file mode 100644
index 0000000..80acf6a
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT.png.sha1
@@ -0,0 +1 @@
+eca1d45d15c5cfa5cf94eba96f1ca96f763d1887
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1
new file mode 100644
index 0000000..80acf6a
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_COUNT.png.sha1
@@ -0,0 +1 @@
+eca1d45d15c5cfa5cf94eba96f1ca96f763d1887
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER.png.sha1
new file mode 100644
index 0000000..bfe31349
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER.png.sha1
@@ -0,0 +1 @@
+886f6d4fe2fb704a0ed8e39365da209ad44fee15
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..bfe31349
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+886f6d4fe2fb704a0ed8e39365da209ad44fee15
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_SNACKBAR.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_SNACKBAR.png.sha1
new file mode 100644
index 0000000..1adae42
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_SNACKBAR.png.sha1
@@ -0,0 +1 @@
+96e9e6adaa4dec2f34b5fac9b45a9f3edf7f9b84
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..80acf6a
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PASSWORD_OPEN_MOVE_MULTIPLE_PASSWORDS_TO_ACCOUNT_DIALOG_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+eca1d45d15c5cfa5cf94eba96f1ca96f763d1887
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 54b7a87..71c195d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1286,6 +1286,7 @@
 const FeatureEntry::FeatureVariation
     kOmniboxOnFocusSuggestionsContextualWebVariations[] = {
         {"GOC Only", {}, 0, "t3317583"},
+        {"pSuggest Only", {}, 0, "t3318055"},
         {"GOC, pSuggest Fallback", {}, 0, "t3317692"},
         {"GOC, pSuggest Backfill", {}, 0, "t3317694"},
         {"GOC, Default Hidden", {}, 0, "t3317834"},
diff --git a/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.cc b/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.cc
index 1d05228..cb7dc74 100644
--- a/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.cc
+++ b/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.cc
@@ -6,7 +6,7 @@
 
 #include "base/android/jni_string.h"
 #include "base/containers/flat_map.h"
-#include "chrome/android/features/autofill_assistant/jni_headers/AssistantOnboardingCoordinator_jni.h"
+#include "chrome/android/features/autofill_assistant/jni_headers/BaseOnboardingCoordinator_jni.h"
 #include "chrome/browser/android/autofill_assistant/onboarding_fetcher_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/autofill_assistant/browser/autofill_assistant_onboarding_fetcher.h"
@@ -21,27 +21,27 @@
   for (const auto& it : string_map) {
     DCHECK(!it.first.empty());
     DCHECK(!it.second.empty());
-    Java_AssistantOnboardingCoordinator_addEntryToStringMap(
+    Java_BaseOnboardingCoordinator_addEntryToStringMap(
         env, jonboarding_coordinator,
         base::android::ConvertUTF8ToJavaString(env, it.first),
         base::android::ConvertUTF8ToJavaString(env, it.second));
   }
-  Java_AssistantOnboardingCoordinator_updateAndShowView(
-      env, jonboarding_coordinator);
+  Java_BaseOnboardingCoordinator_updateAndShowView(env,
+                                                   jonboarding_coordinator);
 }
 
 }  // namespace
 
 // static
-void JNI_AssistantOnboardingCoordinator_FetchOnboardingDefinition(
+void JNI_BaseOnboardingCoordinator_FetchOnboardingDefinition(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jonboarding_coordinator,
     const base::android::JavaParamRef<jstring>& jintent,
     const base::android::JavaParamRef<jstring>& jlocale,
     jint timeout_ms) {
   if (!jonboarding_coordinator || !jintent || !jlocale || !timeout_ms) {
-    Java_AssistantOnboardingCoordinator_updateAndShowView(
-        env, jonboarding_coordinator);
+    Java_BaseOnboardingCoordinator_updateAndShowView(env,
+                                                     jonboarding_coordinator);
     return;
   }
 
diff --git a/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.h b/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.h
index 1ecd3a5d..370ddab3 100644
--- a/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.h
+++ b/chrome/browser/android/autofill_assistant/onboarding_coordinator_bridge.h
@@ -10,7 +10,7 @@
 
 namespace autofill_assistant {
 
-static void JNI_AssistantOnboardingCoordinator_FetchOnboardingDefinition(
+static void JNI_BaseOnboardingCoordinator_FetchOnboardingDefinition(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jonboarding_coordinator,
     const base::android::JavaParamRef<jstring>& jintent,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index aded546..d9e747d3 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4411,7 +4411,6 @@
       chrome_navigation_ui_data->prerender_mode() !=
           prerender::mojom::PrerenderMode::kNoPrerender) {
     result.push_back(std::make_unique<prerender::PrerenderURLLoaderThrottle>(
-        chrome_navigation_ui_data->prerender_mode(),
         chrome_navigation_ui_data->prerender_histogram_prefix(),
         GetPrerenderCanceler(wc_getter)));
   }
diff --git a/chrome/browser/chromeos/dbus/plugin_vm_service_provider.cc b/chrome/browser/chromeos/dbus/plugin_vm_service_provider.cc
index 3788a8e1..66af625 100644
--- a/chrome/browser/chromeos/dbus/plugin_vm_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/plugin_vm_service_provider.cc
@@ -13,8 +13,8 @@
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/webui/settings/chromeos/app_management/app_management_uma.h"
@@ -22,7 +22,6 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chromeos/dbus/plugin_vm_service/plugin_vm_service.pb.h"
 #include "components/prefs/pref_service.h"
-#include "components/user_manager/user_manager.h"
 #include "dbus/message.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -33,14 +32,6 @@
 constexpr char kShowSettingsPageDetails[] = "pluginVm/details";
 constexpr char kShowSettingsPageSharedPaths[] = "pluginVm/sharedPaths";
 
-namespace {
-Profile* GetPrimaryProfile() {
-  const user_manager::User* primary_user =
-      user_manager::UserManager::Get()->GetPrimaryUser();
-  return chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
-}
-}  // namespace
-
 namespace chromeos {
 
 PluginVmServiceProvider::PluginVmServiceProvider() {}
@@ -118,7 +109,7 @@
     return;
   }
 
-  Profile* primary_profile = GetPrimaryProfile();
+  Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
   if (request.subpage_path() == kShowSettingsPageDetails) {
     chrome::ShowAppManagementPage(
         primary_profile, plugin_vm::kPluginVmShelfAppId,
@@ -145,8 +136,8 @@
   std::unique_ptr<dbus::Response> response =
       dbus::Response::FromMethodCall(method_call);
   plugin_vm_service::GetAppLicenseUserIdResponse payload;
-  payload.set_user_id(
-      plugin_vm::GetPluginVmUserIdForProfile(GetPrimaryProfile()));
+  payload.set_user_id(plugin_vm::GetPluginVmUserIdForProfile(
+      ProfileManager::GetPrimaryUserProfile()));
   dbus::MessageWriter writer(response.get());
   writer.AppendProtoAsArrayOfBytes(payload);
   std::move(response_sender).Run(std::move(response));
@@ -159,7 +150,7 @@
       dbus::Response::FromMethodCall(method_call);
   plugin_vm_service::GetPermissionsResponse payload;
   payload.set_data_collection_enabled(
-      GetPrimaryProfile()->GetPrefs()->GetBoolean(
+      ProfileManager::GetPrimaryUserProfile()->GetPrefs()->GetBoolean(
           plugin_vm::prefs::kPluginVmDataCollectionAllowed));
   dbus::MessageWriter writer(response.get());
   writer.AppendProtoAsArrayOfBytes(payload);
diff --git a/chrome/browser/chromeos/exo/chrome_data_exchange_delegate.cc b/chrome/browser/chromeos/exo/chrome_data_exchange_delegate.cc
index de929dc50..01094a28 100644
--- a/chrome/browser/chromeos/exo/chrome_data_exchange_delegate.cc
+++ b/chrome/browser/chromeos/exo/chrome_data_exchange_delegate.cc
@@ -27,9 +27,8 @@
 #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_files.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/user_manager/user_manager.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "content/public/common/drop_data.h"
 #include "storage/browser/file_system/external_mount_points.h"
 #include "storage/browser/file_system/file_system_context.h"
@@ -50,17 +49,6 @@
 constexpr char kUriListSeparator[] = "\r\n";
 constexpr char kVmFileScheme[] = "vmfile";
 
-// We are using our own GetPrimaryProfile() rather than
-// ProfileManager::GetPrimaryUserProfile() to help unittest.
-Profile* GetPrimaryProfile() {
-  if (!user_manager::UserManager::IsInitialized())
-    return nullptr;
-  const auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
-  if (!primary_user)
-    return nullptr;
-  return chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
-}
-
 // We implement our own URLToPath() and PathToURL() rather than use
 // net::FileUrlToFilePath() or net::FilePathToFileURL() since //net code does
 // not support Windows network paths such as //ChromeOS/MyFiles on OS_CHROMEOS.
@@ -114,7 +102,7 @@
 }
 
 storage::FileSystemContext* GetFileSystemContext() {
-  Profile* primary_profile = GetPrimaryProfile();
+  Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
   if (!primary_profile)
     return nullptr;
 
@@ -175,7 +163,7 @@
 void ShareAndSend(aura::Window* target,
                   std::vector<FileInfo> files,
                   exo::DataExchangeDelegate::SendDataCallback callback) {
-  Profile* primary_profile = GetPrimaryProfile();
+  Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
   aura::Window* toplevel = target->GetToplevelWindow();
   bool is_crostini = crostini::IsCrostiniWindow(toplevel);
   bool is_plugin_vm = plugin_vm::IsPluginVmAppWindow(toplevel);
@@ -279,7 +267,7 @@
 std::vector<ui::FileInfo> ChromeDataExchangeDelegate::GetFilenames(
     aura::Window* source,
     const std::vector<uint8_t>& data) const {
-  Profile* primary_profile = GetPrimaryProfile();
+  Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
   aura::Window* toplevel = source->GetToplevelWindow();
   bool is_crostini = crostini::IsCrostiniWindow(toplevel);
   bool is_plugin_vm = plugin_vm::IsPluginVmAppWindow(toplevel);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index 6c98838..9aa4513f 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -58,7 +58,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DuplicateFinderTest) {
-  RunTestURL("background/js/duplicate_finder_unittest_gen.html");
+  RunTestURL("background/js/duplicate_finder_unittest.m_gen.html");
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, ExifParser) {
@@ -181,11 +181,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MediaImportHandlerTest) {
-  RunTestURL("background/js/media_import_handler_unittest_gen.html");
+  RunTestURL("background/js/media_import_handler_unittest.m_gen.html");
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MediaScannerTest) {
-  RunTestURL("background/js/media_scanner_unittest_gen.html");
+  RunTestURL("background/js/media_scanner_unittest.m_gen.html");
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MetadataCacheItem) {
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index 0db32ad..2acb330d 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/download/download_dir_util.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/disks/disk.h"
 #include "chromeos/disks/disk_mount_manager.h"
@@ -83,15 +84,6 @@
 constexpr char kArcDriveContentUrlPrefix[] =
     "content://org.chromium.arc.volumeprovider/MyDrive/";
 
-Profile* GetPrimaryProfile() {
-  if (!user_manager::UserManager::IsInitialized())
-    return nullptr;
-  const auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
-  if (!primary_user)
-    return nullptr;
-  return chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
-}
-
 // Helper function for |ConvertToContentUrls|.
 void OnSingleContentUrlResolved(const base::RepeatingClosure& barrier_closure,
                                 std::vector<GURL>* out_urls,
@@ -561,7 +553,7 @@
 
   // Obtain the primary profile. This information is required because currently
   // only the file systems for the primary profile is exposed to ARC.
-  Profile* primary_profile = GetPrimaryProfile();
+  Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
   if (!primary_profile)
     return false;
 
@@ -734,8 +726,8 @@
 void ConvertToContentUrls(
     const std::vector<storage::FileSystemURL>& file_system_urls,
     ConvertToContentUrlsCallback callback) {
-  ConvertToContentUrls(GetPrimaryProfile(), file_system_urls,
-                       std::move(callback));
+  ConvertToContentUrls(ProfileManager::GetPrimaryUserProfile(),
+                       file_system_urls, std::move(callback));
 }
 
 bool ReplacePrefix(std::string* s,
diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc
index ed48b48..fa22258b 100644
--- a/chrome/browser/devtools/protocol/page_handler.cc
+++ b/chrome/browser/devtools/protocol/page_handler.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/devtools/protocol/page_handler.h"
 
-#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
+#include "components/subresource_filter/content/browser/devtools_interaction_tracker.h"
 #include "components/webapps/installable/installable_manager.h"
 #include "ui/gfx/image/image.h"
 
@@ -22,10 +22,15 @@
 void PageHandler::ToggleAdBlocking(bool enabled) {
   if (!web_contents())
     return;
-  if (auto* client =
-          ChromeSubresourceFilterClient::FromWebContents(web_contents())) {
-    client->ToggleForceActivationInCurrentWebContents(enabled);
-  }
+
+  // Create the DevtoolsInteractionTracker lazily (note that this call is a
+  // no-op if the object was already created).
+  subresource_filter::DevtoolsInteractionTracker::CreateForWebContents(
+      web_contents());
+
+  subresource_filter::DevtoolsInteractionTracker::FromWebContents(
+      web_contents())
+      ->ToggleForceActivation(enabled);
 }
 
 protocol::Response PageHandler::Enable() {
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index e0840b80..271e545 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -835,6 +835,7 @@
     "//components/policy:generated",
     "//components/policy/core/browser",
     "//components/pref_registry",
+    "//components/privacy_sandbox:privacy_sandbox_prefs",
     "//components/proxy_config",
     "//components/rappor",
     "//components/resources",
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 cca2138..907a998b 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
@@ -80,6 +80,7 @@
 using password_manager::CompromiseType;
 using password_manager::InsecureCredentialTypeFlags;
 using password_manager::IsLeaked;
+using password_manager::IsMuted;
 using password_manager::LeakCheckCredential;
 using password_manager::PasswordForm;
 using password_manager::SavedPasswordsPresenter;
@@ -143,8 +144,7 @@
     CompromiseType compromise_type = CompromiseType::kLeaked) {
   return CompromisedCredentials(
       std::string(signon_realm), base::ASCIIToUTF16(username),
-      base::Time::Now() - time_since_creation, compromise_type,
-      /*is_muted=*/false);
+      base::Time::Now() - time_since_creation, compromise_type, IsMuted(false));
 }
 
 PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
@@ -812,11 +812,10 @@
       IsLeaked(true));
   RunUntilIdle();
 
-  EXPECT_THAT(
-      store().compromised_credentials(),
-      ElementsAre(CompromisedCredentials(
-          kExampleCom, base::ASCIIToUTF16(kUsername1), base::Time::Now(),
-          CompromiseType::kLeaked, /*is_muted=*/false)));
+  EXPECT_THAT(store().compromised_credentials(),
+              ElementsAre(CompromisedCredentials(
+                  kExampleCom, base::ASCIIToUTF16(kUsername1),
+                  base::Time::Now(), CompromiseType::kLeaked, IsMuted(false))));
 }
 
 // Test that a found leak creates a compromised credential in the password
@@ -848,16 +847,16 @@
       UnorderedElementsAre(
           CompromisedCredentials(kExampleCom, base::ASCIIToUTF16(kUsername1),
                                  base::Time::Now(), CompromiseType::kLeaked,
-                                 /*is_muted=*/false),
+                                 IsMuted(false)),
           CompromisedCredentials(kExampleOrg, base::ASCIIToUTF16(kUsername1),
                                  base::Time::Now(), CompromiseType::kLeaked,
-                                 /*is_muted=*/false),
+                                 IsMuted(false)),
           CompromisedCredentials(
               kExampleCom, base::ASCIIToUTF16(kUsername2Upper),
-              base::Time::Now(), CompromiseType::kLeaked, /*is_muted=*/false),
+              base::Time::Now(), CompromiseType::kLeaked, IsMuted(false)),
           CompromisedCredentials(
               kExampleOrg, base::ASCIIToUTF16(kUsername2Email),
-              base::Time::Now(), CompromiseType::kLeaked, /*is_muted=*/false)));
+              base::Time::Now(), CompromiseType::kLeaked, IsMuted(false))));
 }
 
 // Verifies that the case where the user has no saved passwords is reported
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 1f888dd0..cb636a01 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/generated_safe_browsing_pref.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
@@ -35,6 +36,7 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/search_engines/default_search_manager.h"
@@ -266,6 +268,11 @@
   (*s_allowlist)[::prefs::kDnsOverHttpsTemplates] =
       settings_api::PrefType::PREF_TYPE_STRING;
 
+  // Privacy sandbox
+  if (base::FeatureList::IsEnabled(features::kPrivacySandboxSettings))
+    (*s_allowlist)[::prefs::kPrivacySandboxApisEnabled] =
+        settings_api::PrefType::PREF_TYPE_BOOLEAN;
+
   // Security page
   (*s_allowlist)[::kGeneratedPasswordLeakDetectionPref] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a9bc87e..a278de6 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -847,7 +847,7 @@
   {
     "name": "desktop-version-default",
     "owners": [ "gambard", "bling-flags@google.com" ],
-    "expiry_milestone": 89
+    "expiry_milestone": 92
   },
   {
     "name": "destroy-profile-on-browser-close",
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 138b10b..ae42485 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -173,6 +173,8 @@
      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetDiscardable},
     {"discardable", "Discardable.FreelistSize", MetricSize::kSmall,
      "freelist_size", EmitTo::kSizeInUmaOnly, nullptr},
+    {"discardable", "Discardable.ResidentSize", MetricSize::kSmall,
+     "resident_size", EmitTo::kSizeInUmaOnly, nullptr},
     {"discardable", "Discardable.VirtualSize", MetricSize::kSmall,
      "virtual_size", EmitTo::kSizeInUmaOnly, nullptr},
     {"extensions/functions", "ExtensionFunctions", MetricSize::kLarge,
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager.cc b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
index 20e1412..54ecaee 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
@@ -120,7 +120,7 @@
     case TextAttachment::Type::kUrl:
       return IDS_NEARBY_TEXT_ATTACHMENTS_LINKS;
     default:
-      return IDS_NEARBY_UNKNOWN_ATTACHMENTS;
+      return IDS_NEARBY_TEXT_ATTACHMENTS_UNKNOWN;
   }
 }
 
diff --git a/chrome/browser/password_check/android/password_check_manager_unittest.cc b/chrome/browser/password_check/android/password_check_manager_unittest.cc
index 70751fc..d7aa9e8 100644
--- a/chrome/browser/password_check/android/password_check_manager_unittest.cc
+++ b/chrome/browser/password_check/android/password_check_manager_unittest.cc
@@ -170,7 +170,8 @@
     CompromiseType compromise_type = CompromiseType::kLeaked) {
   return CompromisedCredentials(
       std::string(signon_realm), base::ASCIIToUTF16(username),
-      base::Time::Now() - time_since_creation, compromise_type, false);
+      base::Time::Now() - time_since_creation, compromise_type,
+      password_manager::IsMuted(false));
 }
 
 // Creates matcher for a given compromised credential
diff --git a/chrome/browser/password_manager/multi_profile_credentials_filter_unittest.cc b/chrome/browser/password_manager/multi_profile_credentials_filter_unittest.cc
index bd40032..5dfe84d 100644
--- a/chrome/browser/password_manager/multi_profile_credentials_filter_unittest.cc
+++ b/chrome/browser/password_manager/multi_profile_credentials_filter_unittest.cc
@@ -35,10 +35,13 @@
 class TestDiceWebSigninInterceptorDelegate
     : public DiceWebSigninInterceptor::Delegate {
  public:
-  void ShowSigninInterceptionBubble(
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+  ShowSigninInterceptionBubble(
       content::WebContents* web_contents,
       const BubbleParameters& bubble_parameters,
-      base::OnceCallback<void(SigninInterceptionResult)> callback) override {}
+      base::OnceCallback<void(SigninInterceptionResult)> callback) override {
+    return nullptr;
+  }
   void ShowProfileCustomizationBubble(Browser* browser) override {}
 };
 
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 db72792d..769c765b 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
@@ -48,7 +48,9 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if !defined(OS_ANDROID)
+#include "chrome/browser/performance_manager/mechanisms/page_freezer.h"
 #include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
+#include "chrome/browser/performance_manager/policies/page_freezing_policy.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)
@@ -148,6 +150,12 @@
     graph->PassToGraph(std::make_unique<
                        performance_manager::policies::HighPMFDiscardPolicy>());
   }
+
+  // The freezing policy isn't enabled on Android yet as it doesn't play well
+  // with the freezing logic already in place in renderers. This logic should be
+  // moved to PerformanceManager, this is tracked in https://crbug.com/1156803.
+  graph->PassToGraph(
+      std::make_unique<performance_manager::policies::PageFreezingPolicy>());
 #endif  // !defined(OS_ANDROID)
 
   graph->PassToGraph(
diff --git a/chrome/browser/performance_manager/policies/page_freezing_policy.cc b/chrome/browser/performance_manager/policies/page_freezing_policy.cc
index 61806df2..a624a3a4 100644
--- a/chrome/browser/performance_manager/policies/page_freezing_policy.cc
+++ b/chrome/browser/performance_manager/policies/page_freezing_policy.cc
@@ -46,9 +46,6 @@
 
   if (page_node->IsAudible())
     OnIsAudibleChanged(page_node);
-
-  if (page_node->IsVisible())
-    OnIsVisibleChanged(page_node);
 }
 
 void PageFreezingPolicy::OnBeforePageNodeRemoved(const PageNode* page_node) {
@@ -59,13 +56,6 @@
   page_node_being_removed_ = nullptr;
 }
 
-void PageFreezingPolicy::OnIsVisibleChanged(const PageNode* page_node) {
-  UpdateNegativeFreezingVote(page_node, CannotFreezeReason::kVisible,
-                             page_node->IsVisible()
-                                 ? NegativeVoteAction::kEmit
-                                 : NegativeVoteAction::kRemove);
-}
-
 void PageFreezingPolicy::OnIsAudibleChanged(const PageNode* page_node) {
   UpdateNegativeFreezingVote(page_node, CannotFreezeReason::kAudible,
                              page_node->IsAudible()
@@ -185,8 +175,6 @@
   switch (reason) {
     case CannotFreezeReason::kAudible:
       return "Page is audible";
-    case CannotFreezeReason::kVisible:
-      return "Page is visible";
     case CannotFreezeReason::kHoldingIndexedDBLock:
       return "Page is holding an IndexedDB lock";
     case CannotFreezeReason::kHoldingWebLock:
@@ -232,7 +220,7 @@
               ->GetRegisteredObjectAs<freezing::FreezingVoteAggregator>()
               ->GetVotingChannel());
     } else {
-      DCHECK(!iter->second->IsValid());
+      DCHECK(!iter->second->HasVoteForContext(page_node));
     }
     // Submit the negative freezing vote.
     iter->second->SubmitVote(page_node,
diff --git a/chrome/browser/performance_manager/policies/page_freezing_policy.h b/chrome/browser/performance_manager/policies/page_freezing_policy.h
index 82ff985c..216c9407 100644
--- a/chrome/browser/performance_manager/policies/page_freezing_policy.h
+++ b/chrome/browser/performance_manager/policies/page_freezing_policy.h
@@ -22,7 +22,6 @@
 // freezing vote is positive.
 //
 // Tabs in one of the following states won't be frozen:
-//   - Visible;
 //   - Audible;
 //   - Capturing video;
 //   - Capturing audio;
@@ -33,6 +32,9 @@
 //   - Connected to a USB device;
 //   - Holding at least one IndexedDB lock;
 //   - Holding at least one WebLock.
+//
+// Note that visible tabs can't be frozen and tabs that becomes visible are
+// automatically unfrozen, there's no need to track this feature here.
 class PageFreezingPolicy : public GraphOwned,
                            public PageNode::ObserverDefaultImpl,
                            public PageLiveStateObserver {
@@ -52,7 +54,6 @@
  protected:
   // List of states that prevent a tab from being frozen.
   enum class CannotFreezeReason {
-    kVisible,
     kAudible,
     kHoldingIndexedDBLock,
     kHoldingWebLock,
@@ -90,7 +91,6 @@
   // PageNodeObserver implementation:
   void OnPageNodeAdded(const PageNode* page_node) override;
   void OnBeforePageNodeRemoved(const PageNode* page_node) override;
-  void OnIsVisibleChanged(const PageNode* page_node) override;
   void OnIsAudibleChanged(const PageNode* page_node) override;
   void OnPageIsHoldingWebLockChanged(const PageNode* page_node) override;
   void OnPageIsHoldingIndexedDBLockChanged(const PageNode* page_node) override;
diff --git a/chrome/browser/performance_manager/policies/page_freezing_policy_unittest.cc b/chrome/browser/performance_manager/policies/page_freezing_policy_unittest.cc
index 911314fc..a24ae2b 100644
--- a/chrome/browser/performance_manager/policies/page_freezing_policy_unittest.cc
+++ b/chrome/browser/performance_manager/policies/page_freezing_policy_unittest.cc
@@ -93,15 +93,6 @@
   PageFreezingPolicy* policy_;
 };
 
-TEST_F(PageFreezingPolicyTest, VisiblePageGetsCannotFreezeVote) {
-  page_node()->SetIsVisible(true);
-  EXPECT_EQ(page_node()->freezing_vote()->value(),
-            freezing::FreezingVoteValue::kCannotFreeze);
-  EXPECT_EQ(page_node()->freezing_vote()->reason(),
-            PageFreezingPolicyAccess::CannotFreezeReasonToString(
-                PageFreezingPolicyAccess::CannotFreezeReason::kVisible));
-}
-
 TEST_F(PageFreezingPolicyTest, AudiblePageGetsCannotFreezeVote) {
   page_node()->SetIsAudible(true);
   EXPECT_EQ(page_node()->freezing_vote()->value(),
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index f11e4e6b..b9216e04 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -4,9 +4,11 @@
 
 #include "content/public/browser/picture_in_picture_window_controller.h"
 
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/scoped_observation.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
@@ -52,6 +54,7 @@
 #include "ui/events/base_event_utils.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/view_observer.h"
 #include "ui/views/widget/widget_observer.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -95,6 +98,47 @@
 const base::FilePath::CharType kPictureInPictureWindowSizePage[] =
     FILE_PATH_LITERAL("media/picture-in-picture/window-size.html");
 
+// Determines whether |control| is visible taking into account OverlayWindow's
+// custom control hiding that includes setting the size to 0x0.
+bool IsOverlayWindowControlVisible(views::View* control) {
+  return control->IsDrawn() && !control->size().IsEmpty();
+}
+
+// An observer used to notify about control visibility changes.
+class ControlVisibilityObserver : views::ViewObserver {
+ public:
+  explicit ControlVisibilityObserver(views::View* observed_view,
+                                     bool expected_visible,
+                                     base::OnceClosure callback)
+      : expected_visible_(expected_visible),
+        visibility_change_callback_(std::move(callback)) {
+    observation_.Observe(observed_view);
+
+    MaybeNotifyOfVisibilityChange(observed_view);
+  }
+
+  // views::ViewObserver overrides.
+  void OnViewVisibilityChanged(views::View* observed_view,
+                               views::View* starting_view) override {
+    MaybeNotifyOfVisibilityChange(observed_view);
+  }
+  void OnViewBoundsChanged(views::View* observed_view) override {
+    MaybeNotifyOfVisibilityChange(observed_view);
+  }
+
+ private:
+  void MaybeNotifyOfVisibilityChange(views::View* observed_view) {
+    if (visibility_change_callback_ &&
+        IsOverlayWindowControlVisible(observed_view) == expected_visible_) {
+      std::move(visibility_change_callback_).Run();
+    }
+  }
+
+  base::ScopedObservation<views::View, views::ViewObserver> observation_{this};
+  bool expected_visible_;
+  base::OnceClosure visibility_change_callback_;
+};
+
 }  // namespace
 
 class PictureInPictureWindowControllerBrowserTest
@@ -164,6 +208,24 @@
     EXPECT_FALSE(in_picture_in_picture);
   }
 
+  // Makes sure all |controls| have the expected visibility state, waiting if
+  // necessary.
+  void AssertControlsVisible(std::vector<views::View*> controls,
+                             bool expected_visible) {
+    base::RunLoop run_loop;
+    const auto barrier =
+        base::BarrierClosure(controls.size(), run_loop.QuitClosure());
+    std::vector<std::unique_ptr<ControlVisibilityObserver>> observers;
+    for (views::View* control : controls) {
+      observers.push_back(std::make_unique<ControlVisibilityObserver>(
+          control, expected_visible, barrier));
+    }
+    run_loop.Run();
+
+    for (views::View* control : controls)
+      ASSERT_EQ(IsOverlayWindowControlVisible(control), expected_visible);
+  }
+
   class WidgetBoundsChangeWaiter final : public views::WidgetObserver {
    public:
     explicit WidgetBoundsChangeWaiter(views::Widget* widget)
@@ -189,7 +251,8 @@
     base::RunLoop run_loop_;
   };
 
-  void MoveMouseOver(OverlayWindowViews* window) {
+  void MoveMouseOverOverlayWindow() {
+    auto* const window = GetOverlayWindow();
     gfx::Point p(window->GetBounds().x(), window->GetBounds().y());
     ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p, p, ui::EventTimeForNow(),
                               ui::EF_NONE, ui::EF_NONE);
@@ -400,7 +463,7 @@
 
   EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
   Wait(base::TimeDelta::FromSeconds(3));
-  MoveMouseOver(GetOverlayWindow());
+  MoveMouseOverOverlayWindow();
   TakeOverlayWindowScreenshot(GetOverlayWindow());
 
   base::FilePath expected_pause_image_path =
@@ -428,7 +491,7 @@
 
   EXPECT_TRUE(content::ExecuteScript(active_web_contents, "video.pause();"));
   Wait(base::TimeDelta::FromSeconds(3));
-  MoveMouseOver(GetOverlayWindow());
+  MoveMouseOverOverlayWindow();
   TakeOverlayWindowScreenshot(GetOverlayWindow());
   ASSERT_TRUE(ReadImageFile(expected_play_image_path, &expected_image));
   EXPECT_TRUE(CompareImages(GetResultBitmap(), expected_image));
@@ -831,18 +894,11 @@
 
   EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
   EXPECT_TRUE(GetOverlayWindow()->video_layer_for_testing()->visible());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->previous_track_controls_view_for_testing()
-                   ->layer()
-                   ->visible());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->play_pause_controls_view_for_testing()
-                   ->layer()
-                   ->visible());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->next_track_controls_view_for_testing()
-                   ->layer()
-                   ->visible());
+  AssertControlsVisible(
+      {GetOverlayWindow()->previous_track_controls_view_for_testing(),
+       GetOverlayWindow()->play_pause_controls_view_for_testing(),
+       GetOverlayWindow()->next_track_controls_view_for_testing()},
+      false);
 }
 
 // Tests that we can enter Picture-in-Picture when a video is not preloaded,
@@ -1717,26 +1773,26 @@
   ASSERT_NE(nullptr, GetOverlayWindow());
 
   // Play/Pause button is displayed if video is not a mediastream.
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->play_pause_controls_view_for_testing()}, true);
 
   // Play/Pause button is hidden if video is a mediastream.
   bool result = false;
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
       active_web_contents, "changeVideoSrcToMediaStream();", &result));
   EXPECT_TRUE(result);
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->play_pause_controls_view_for_testing()}, false);
 
   // Play/Pause button is not hidden anymore when video is not a mediastream.
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
       active_web_contents, "changeVideoSrc();", &result));
   EXPECT_TRUE(result);
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->play_pause_controls_view_for_testing()}, true);
 }
 
 // Check that page visibility API events are fired when tab is hidden, shown,
@@ -1871,11 +1927,9 @@
 
   // Skip Ad button is not displayed initially when mouse is hovering over the
   // window.
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->skip_ad_controls_view_for_testing()
-                   ->layer()
-                   ->visible());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->skip_ad_controls_view_for_testing()}, false);
 
   content::WebContents* active_web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -1884,33 +1938,28 @@
   // hovering over the window and media session action handler has been set.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "setMediaSessionActionHandler('skipad');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->skip_ad_controls_view_for_testing()
-                   ->layer()
-                   ->visible());
+  MoveMouseOverOverlayWindow();
+  // Wait for the controls to show if necessary, then verify the Skip Ad button
+  // is not among those shown.
+  AssertControlsVisible(
+      {GetOverlayWindow()->back_to_tab_controls_for_testing()}, true);
+  EXPECT_FALSE(IsOverlayWindowControlVisible(
+      GetOverlayWindow()->skip_ad_controls_view_for_testing()));
 
   // Play video and check that Skip Ad button is now displayed when
   // video plays and mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(GetOverlayWindow()
-                  ->skip_ad_controls_view_for_testing()
-                  ->layer()
-                  ->visible());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->skip_ad_controls_view_for_testing()}, true);
 
   // Unset action handler and check that Skip Ad button is not displayed when
   // video plays and mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "unsetMediaSessionActionHandler('skipad');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->skip_ad_controls_view_for_testing()
-                   ->layer()
-                   ->visible());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->skip_ad_controls_view_for_testing()}, false);
 }
 
 // Tests that the Play/Plause button is displayed in the Picture-in-Picture
@@ -1932,10 +1981,13 @@
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
       active_web_contents, "changeVideoSrcToMediaStream();", &result));
   EXPECT_TRUE(result);
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  // Wait for the controls to show if necessary, then verify the Play/Pause
+  // button is not among those shown.
+  AssertControlsVisible(
+      {GetOverlayWindow()->back_to_tab_controls_for_testing()}, true);
+  EXPECT_FALSE(IsOverlayWindowControlVisible(
+      GetOverlayWindow()->play_pause_controls_view_for_testing()));
 
   // Play second video (non-muted) so that Media Session becomes active.
   ASSERT_TRUE(
@@ -1945,28 +1997,29 @@
   // is still hidden when mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(active_web_contents,
                                      "setMediaSessionActionHandler('play');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  // Wait for the controls to show if necessary, then verify the Play/Pause
+  // button is not among those shown.
+  AssertControlsVisible(
+      {GetOverlayWindow()->back_to_tab_controls_for_testing()}, true);
+  EXPECT_FALSE(IsOverlayWindowControlVisible(
+      GetOverlayWindow()->play_pause_controls_view_for_testing()));
 
   // Set Media Session action "pause" handler and check that Play/Pause button
   // is now displayed when mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(active_web_contents,
                                      "setMediaSessionActionHandler('pause');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->play_pause_controls_view_for_testing()}, true);
 
   // Unset Media Session action "pause" handler and check that Play/Pause button
   // is hidden when mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "unsetMediaSessionActionHandler('pause');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->play_pause_controls_view_for_testing()}, false);
 
   ASSERT_TRUE(
       content::ExecuteScript(active_web_contents, "exitPictureInPicture();"));
@@ -1974,16 +2027,14 @@
   // Reset Media Session action "pause" handler and check that Play/Pause button
   // is now displayed when mouse is hovering over the window when it enters
   // Picture-in-Picture again.
-  base::RunLoop().RunUntilIdle();
   ASSERT_TRUE(content::ExecuteScript(active_web_contents,
                                      "setMediaSessionActionHandler('pause');"));
   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
       active_web_contents, "enterPictureInPicture();", &result));
   EXPECT_TRUE(result);
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(
-      GetOverlayWindow()->play_pause_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->play_pause_controls_view_for_testing()}, true);
 }
 
 // Tests that a Next Track button is displayed in the Picture-in-Picture window
@@ -1996,97 +2047,53 @@
 
   // Next Track button is not displayed initially when mouse is hovering over
   // the window.
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->next_track_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->next_track_controls_view_for_testing()}, false);
 
   content::WebContents* active_web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
 
-  // Next Track button is not displayed if video is not playing even if mouse is
-  // hovering over the window and media session action handler has been set.
+  // Next Track button is not displayed if video is not playing even if mouse
+  // is hovering over the window and media session action handler has been set.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "setMediaSessionActionHandler('nexttrack');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->next_track_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  // Wait for the controls to show if necessary, then verify the Next Track
+  // button is not among those shown.
+  AssertControlsVisible(
+      {GetOverlayWindow()->back_to_tab_controls_for_testing()}, true);
+  EXPECT_FALSE(IsOverlayWindowControlVisible(
+      GetOverlayWindow()->next_track_controls_view_for_testing()));
 
-  // Play video and check that Next Track button is now displayed when
-  // video plays and mouse is hovering over the window.
+  // Play video and check that Next Track button is now displayed when video
+  // plays and mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(
-      GetOverlayWindow()->next_track_controls_view_for_testing()->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->next_track_controls_view_for_testing()}, true);
 
-  gfx::Rect next_track_bounds = GetOverlayWindow()
-                                    ->next_track_controls_view_for_testing()
-                                    ->GetBoundsInScreen();
-
-  // Unset action handler and check that Next Track button is not displayed when
-  // video plays and mouse is hovering over the window.
+  // Unset action handler and check that Next Track button is not displayed
+  // when video plays and mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "unsetMediaSessionActionHandler('nexttrack');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(
-      GetOverlayWindow()->next_track_controls_view_for_testing()->IsDrawn());
-
-  // Next Track button is still at the same previous location.
-  EXPECT_EQ(next_track_bounds, GetOverlayWindow()
-                                   ->next_track_controls_view_for_testing()
-                                   ->GetBoundsInScreen());
-}
-
-// Tests that Next Track button bounds are updated right away when
-// Picture-in-Picture window controls are hidden.
-IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
-                       NextTrackButtonBounds) {
-  LoadTabAndEnterPictureInPicture(
-      browser(), base::FilePath(kPictureInPictureWindowSizePage));
-  ASSERT_NE(GetOverlayWindow(), nullptr);
-
-  content::WebContents* active_web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  gfx::Rect next_track_bounds = GetOverlayWindow()
-                                    ->next_track_controls_view_for_testing()
-                                    ->GetBoundsInScreen();
-
-  ASSERT_TRUE(content::ExecuteScript(
-      active_web_contents, "setMediaSessionActionHandler('nexttrack');"));
-  ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_NE(next_track_bounds, GetOverlayWindow()
-                                   ->next_track_controls_view_for_testing()
-                                   ->GetBoundsInScreen());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->next_track_controls_view_for_testing()}, false);
 }
 
 // Tests that a Previous Track button is displayed in the Picture-in-Picture
 // window when Media Session Action "previoustrack" is handled by the website.
-// TODO(crbug.com/985303): Flaky on Linux.
-// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
-// complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
-#define MAYBE_PreviousTrackButtonVisibility \
-  DISABLED_PreviousTrackButtonVisibility
-#else
-#define MAYBE_PreviousTrackButtonVisibility PreviousTrackButtonVisibility
-#endif
 IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
-                       MAYBE_PreviousTrackButtonVisibility) {
+                       PreviousTrackButtonVisibility) {
   LoadTabAndEnterPictureInPicture(
       browser(), base::FilePath(kPictureInPictureWindowSizePage));
-  ASSERT_NE(GetOverlayWindow(), nullptr);
 
   // Previous Track button is not displayed initially when mouse is hovering
   // over the window.
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->previous_track_controls_view_for_testing()
-                   ->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->previous_track_controls_view_for_testing()}, false);
 
   content::WebContents* active_web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -2096,67 +2103,28 @@
   // set.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "setMediaSessionActionHandler('previoustrack');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->previous_track_controls_view_for_testing()
-                   ->IsDrawn());
+  MoveMouseOverOverlayWindow();
+  // Wait for the controls to show if necessary, then verify the Previous Track
+  // button is not among those shown.
+  AssertControlsVisible(
+      {GetOverlayWindow()->back_to_tab_controls_for_testing()}, true);
+  EXPECT_FALSE(IsOverlayWindowControlVisible(
+      GetOverlayWindow()->previous_track_controls_view_for_testing()));
 
   // Play video and check that Previous Track button is now displayed when
   // video plays and mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_TRUE(GetOverlayWindow()
-                  ->previous_track_controls_view_for_testing()
-                  ->IsDrawn());
-
-  gfx::Rect previous_track_bounds =
-      GetOverlayWindow()
-          ->previous_track_controls_view_for_testing()
-          ->GetBoundsInScreen();
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->previous_track_controls_view_for_testing()}, true);
 
   // Unset action handler and check that Previous Track button is not displayed
   // when video plays and mouse is hovering over the window.
   ASSERT_TRUE(content::ExecuteScript(
       active_web_contents, "unsetMediaSessionActionHandler('previoustrack');"));
-  base::RunLoop().RunUntilIdle();
-  MoveMouseOver(GetOverlayWindow());
-  EXPECT_FALSE(GetOverlayWindow()
-                   ->previous_track_controls_view_for_testing()
-                   ->IsDrawn());
-  // Previous Track button is still at the same previous location.
-  EXPECT_EQ(previous_track_bounds,
-            GetOverlayWindow()
-                ->previous_track_controls_view_for_testing()
-                ->GetBoundsInScreen());
-}
-
-// Tests that Previous Track button bounds are updated right away when
-// Picture-in-Picture window controls are hidden.
-IN_PROC_BROWSER_TEST_F(MediaSessionPictureInPictureWindowControllerBrowserTest,
-                       PreviousTrackButtonBounds) {
-  LoadTabAndEnterPictureInPicture(
-      browser(), base::FilePath(kPictureInPictureWindowSizePage));
-  ASSERT_NE(GetOverlayWindow(), nullptr);
-
-  content::WebContents* active_web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  gfx::Rect previous_track_bounds =
-      GetOverlayWindow()
-          ->previous_track_controls_view_for_testing()
-          ->GetBoundsInScreen();
-
-  ASSERT_TRUE(content::ExecuteScript(
-      active_web_contents, "setMediaSessionActionHandler('previoustrack');"));
-  ASSERT_TRUE(content::ExecuteScript(active_web_contents, "video.play();"));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_NE(previous_track_bounds,
-            GetOverlayWindow()
-                ->previous_track_controls_view_for_testing()
-                ->GetBoundsInScreen());
+  MoveMouseOverOverlayWindow();
+  AssertControlsVisible(
+      {GetOverlayWindow()->previous_track_controls_view_for_testing()}, false);
 }
 
 // Tests that clicking the Skip Ad button in the Picture-in-Picture window
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index f2d6f3e..bda1631a 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -49,6 +49,9 @@
      */
     public static final String ACCESSIBILITY_TAB_SWITCHER = "accessibility_tab_switcher";
 
+    public static final String ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT =
+            "Chrome.AccountPickerBottomSheet.ShownCount";
+
     /** The language code to override application language with. */
     public static final String APPLICATION_OVERRIDE_LANGUAGE =
             "Chrome.Language.ApplicationOverrideLanguage";
@@ -813,6 +816,7 @@
     static List<String> getKeysInUse() {
         // clang-format off
         return Arrays.asList(
+                ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT,
                 ASSISTANT_LAST_VERSION,
                 ASSISTANT_VOICE_SEARCH_ENABLED,
                 ASSISTANT_VOICE_SEARCH_SUPPORTED,
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 7885a77..1cdcb56 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -442,26 +442,24 @@
 
 // static
 Profile* ProfileManager::GetPrimaryUserProfile() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (IsLoggedIn()) {
+    user_manager::UserManager* manager = user_manager::UserManager::Get();
+    const user_manager::User* user = manager->GetPrimaryUser();
+    if (!user)  // Can be null in unit tests.
+      return nullptr;
+
+    // Note: The ProfileHelper will take care of guest profiles.
+    return chromeos::ProfileHelper::Get()->GetProfileByUserUnsafe(user);
+  }
+#endif
+
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   if (!profile_manager)  // Can be null in unit tests.
     return nullptr;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (!IsLoggedIn()) {
-    return profile_manager->GetActiveUserOrOffTheRecordProfileFromPath(
-        profile_manager->user_data_dir());
-  }
 
-  user_manager::UserManager* manager = user_manager::UserManager::Get();
-  const user_manager::User* user = manager->GetPrimaryUser();
-  if (!user)  // Can be null in unit tests.
-    return nullptr;
-
-  // Note: The ProfileHelper will take care of guest profiles.
-  return chromeos::ProfileHelper::Get()->GetProfileByUserUnsafe(user);
-#else
   return profile_manager->GetActiveUserOrOffTheRecordProfileFromPath(
       profile_manager->user_data_dir());
-#endif
 }
 
 // static
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index d497a08..db4d41c 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -1169,7 +1169,7 @@
 
 // Functionality is not present on ChromeOS.
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest, OpenLinkInProfileEntryPresent) {
+IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest, DISABLED_OpenLinkInProfileEntryPresent) {
   {
     std::unique_ptr<TestRenderViewContextMenu> menu(
         CreateContextMenuMediaTypeNone(GURL("http://www.google.com/"),
diff --git a/chrome/browser/resources/discards/discards_tab.js b/chrome/browser/resources/discards/discards_tab.js
index 0f53157b..61b45fc 100644
--- a/chrome/browser/resources/discards/discards_tab.js
+++ b/chrome/browser/resources/discards/discards_tab.js
@@ -220,6 +220,8 @@
         return pageLifecycleStateFromVisibilityAndFocus();
       case LifecycleUnitState.THROTTLED:
         return pageLifecycleStateFromVisibilityAndFocus() + ' (throttled)';
+      case LifecycleUnitState.FROZEN:
+        return 'frozen';
       case LifecycleUnitState.DISCARDED:
         return 'discarded (' + this.discardReasonToString_(reason) + ')' +
             ((reason === LifecycleUnitDiscardReason.URGENT) ? ' at ' +
diff --git a/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html b/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html
index b8bcc9d..6061042 100644
--- a/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.html
@@ -21,8 +21,7 @@
 
   <div slot="body">
     <dom-repeat id="devicePasswordList" items="[[passwordsToMove]]"
-        class="cr-separators list-with-header"
-        on-rendered-item-count-changed="updateMovingPasswordsCountLabel_">
+        class="cr-separators list-with-header">
       <template>
         <password-list-item entry="[[item]]"
             tabindex$="[[tabIndex]]" focus-row-index="[[index]]"
@@ -30,8 +29,7 @@
             last-focused="{{lastFocused_}}" list-blurred="{{listBlurred_}}"
             should-hide-more-actions-button>
           <cr-checkbox slot="checkbox" id="checkbox" checked="true"
-              data-id$="[[item.deviceId_]]"
-              on-checked-changed="updateMovingPasswordsCountLabel_">
+              data-id$="[[item.deviceId_]]">
           </cr-checkbox>
         </password-list-item>
       </template>
@@ -52,8 +50,9 @@
     <div class="cr-row two-line ">
       <settings-avatar-icon></settings-avatar-icon>
       <div class="flex cr-padded-text">
-        <div id="movingPasswordsCountLabel">[[movingPasswordsCountLabel_]]</div>
-        <div class="secondary">$i18n{passwordMovePasswordsToAccount}</div>
+        <div id="movingPasswordsCountLabel">
+          $i18n{passwordMovePasswordsToAccountDialogFooter}</div>
+        <div class="secondary">[[accountEmail]]</div>
       </div>
     </div>
   </div>
diff --git a/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.js b/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.js
index 2116ff5b..e8b3148 100644
--- a/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.js
+++ b/chrome/browser/resources/settings/autofill_page/password_move_multiple_passwords_to_account_dialog.js
@@ -11,7 +11,6 @@
 import './password_list_item.js';
 
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {MultiStorePasswordUiEntry} from './multi_store_password_ui_entry.js';
@@ -31,11 +30,7 @@
       type: Array,
       value: () => [],
     },
-    /** @private */
-    movingPasswordsCountLabel_: {
-      type: String,
-      value: '',
-    },
+    accountEmail: String,
   },
 
   /** @override */
@@ -67,16 +62,4 @@
   onCancelButtonClick_() {
     this.$.dialog.close();
   },
-
-  updateMovingPasswordsCountLabel_() {
-    const checkboxes = this.$.dialog.querySelectorAll('cr-checkbox');
-    const selectedPasswordsCount =
-        Array.from(checkboxes).filter(box => box.checked).length;
-
-    PluralStringProxyImpl.getInstance()
-        .getPluralString('movePasswordsToAccount', selectedPasswordsCount)
-        .then(label => {
-          this.movingPasswordsCountLabel_ = label;
-        });
-  },
 });
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_device_section.html b/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
index a3cc37b..9d28bc78 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_device_section.html
@@ -98,6 +98,7 @@
 <template is="dom-if" if="[[showMoveMultiplePasswordsDialog_]]" restamp>
   <password-move-multiple-passwords-to-account-dialog
       passwords-to-move="[[allDevicePasswords_]]"
+      account-email="[[accountEmail_]]"
       on-close="onMoveMultiplePasswordsDialogClose_">
   </password-move-multiple-passwords-to-account-dialog>
 </template>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_device_section.js b/chrome/browser/resources/settings/autofill_page/passwords_device_section.js
index ae4df301f..4b18c21 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_device_section.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_device_section.js
@@ -22,6 +22,7 @@
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {IronA11yKeysBehavior} from 'chrome://resources/polymer/v3_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
@@ -119,6 +120,7 @@
       type: Array,
       value: () => [],
       computed: 'computeAllDevicePasswords_(savedPasswords.splices)',
+      observer: 'onAllDevicePasswordsChanged_',
     },
 
     /** @private {!MultiStorePasswordUiEntry} */
@@ -178,7 +180,6 @@
     devicePasswordsLabel_: {
       type: String,
       value: '',
-      computed: 'computeDevicePasswordsLabel_(allDevicePasswords_)',
     },
 
     /** @private */
@@ -269,13 +270,11 @@
 
   /**
    * @private
-   * @return {string}
    */
-  computeDevicePasswordsLabel_() {
-    return this.allDevicePasswords_.length === 1 ?
-        this.i18n('devicePasswordsLinkLabelSingular') :
-        this.i18n(
-            'devicePasswordsLinkLabelPlural', this.allDevicePasswords_.length);
+  async onAllDevicePasswordsChanged_() {
+    this.devicePasswordsLabel_ =
+        await PluralStringProxyImpl.getInstance().getPluralString(
+            'movePasswordsToAccount', this.allDevicePasswords_.length);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/privacy_sandbox/BUILD.gn b/chrome/browser/resources/settings/privacy_sandbox/BUILD.gn
index aefd267..c9bc394 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/resources/settings/privacy_sandbox/BUILD.gn
@@ -24,6 +24,7 @@
 
 js_library("app") {
   deps = [
+    "..:settings",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
diff --git a/chrome/browser/resources/settings/privacy_sandbox/app.html b/chrome/browser/resources/settings/privacy_sandbox/app.html
index f6653a9..18daad3 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/app.html
+++ b/chrome/browser/resources/settings/privacy_sandbox/app.html
@@ -43,6 +43,7 @@
   }
 </style>
 
+<settings-prefs prefs="{{prefs}}"></settings-prefs>
 <div id="page-container">
   <h1 id="page-heading">$i18n{privacySandboxPageHeading}</h1>
   <picture>
@@ -78,6 +79,7 @@
   </div>
   <div class="card">
     <settings-toggle-button id="toggleButton"
+        pref="{{prefs.privacy_sandbox.apis_enabled}}"
         class="hr" label="$i18n{privacySandboxPageHeading}">
     </settings-toggle-button>
     <div class="cr-row continuation">
@@ -87,8 +89,8 @@
         <div class="secondary">$i18n{privacySandboxPageHeading}</div>
     </div>
     <div class="cr-row continuation">
-      <cr-button class="cr-button" id="button" aria-disabled="false"
-          role="button" tabindex="0" on-click="onButtonClick_">
+      <cr-button class="cr-button" id="learnMoreButton" role="button"
+          tabindex="0" on-click="onLearnMoreButtonClick_">
         $i18n{passwordViewDetails}
       </cr-button>
     </div>
diff --git a/chrome/browser/resources/settings/privacy_sandbox/app.js b/chrome/browser/resources/settings/privacy_sandbox/app.js
index 80490c0..2c38edc 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/app.js
+++ b/chrome/browser/resources/settings/privacy_sandbox/app.js
@@ -7,13 +7,26 @@
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+// Those resources are loaded through settings.js as the privacy sandbox page
+// lives outside regular settings, hence can't access those resources directly
+// with |optimize_webui="true"|.
+import{loadTimeData, OpenWindowProxyImpl} from '../settings.js';
+
 Polymer({
   is: 'privacy-sandbox-app',
 
   _template: html`{__html_template__}`,
 
+  properties: {
+    /**
+     * Preferences state.
+     */
+    prefs: Object,
+  },
+
   /** @private */
-  onButtonClick_: function() {
-    // TODO(crbug/1152336): Implement button click.
+  onLearnMoreButtonClick_: function() {
+    OpenWindowProxyImpl.getInstance().openURL(
+        loadTimeData.getString('privacySandboxURL'));
   },
 });
diff --git a/chrome/browser/resources/settings/settings.js b/chrome/browser/resources/settings/settings.js
index e542cb1..29f3218 100644
--- a/chrome/browser/resources/settings/settings.js
+++ b/chrome/browser/resources/settings/settings.js
@@ -15,6 +15,7 @@
 // </if>
 export {ExtensionControlBrowserProxyImpl} from './extension_control_browser_proxy.m.js';
 export {HatsBrowserProxy, HatsBrowserProxyImpl} from './hats_browser_proxy.js';
+export {loadTimeData} from './i18n_setup.js';
 export {LifetimeBrowserProxy, LifetimeBrowserProxyImpl} from './lifetime_browser_proxy.m.js';
 export {MetricsBrowserProxy, MetricsBrowserProxyImpl, PrivacyElementInteractions, SafeBrowsingInteractions, SafetyCheckInteractions} from './metrics_browser_proxy.js';
 export {OnStartupBrowserProxy, OnStartupBrowserProxyImpl} from './on_startup_page/on_startup_browser_proxy.js';
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 967501a..c6034a9 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -1725,7 +1725,8 @@
     password_store->AddCompromisedCredentials(
         password_manager::CompromisedCredentials(
             credential.signon_realm, credential.username, base::Time::Now(),
-            password_manager::CompromiseType::kPhished, false));
+            password_manager::CompromiseType::kPhished,
+            password_manager::IsMuted(false)));
   }
 }
 
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 415bd7e..3837243 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -90,6 +90,9 @@
 
 }  // namespace
 
+ScopedDiceWebSigninInterceptionBubbleHandle::
+    ~ScopedDiceWebSigninInterceptionBubbleHandle() = default;
+
 bool SigninInterceptionHeuristicOutcomeIsSuccess(
     SigninInterceptionHeuristicOutcome outcome) {
   return outcome == SigninInterceptionHeuristicOutcome::kInterceptEnterprise ||
@@ -237,7 +240,7 @@
           SigninInterceptionType::kProfileSwitch, *account_info,
           GetPrimaryAccountInfo(identity_manager_),
           entry->GetProfileThemeColors().profile_highlight_color};
-      delegate_->ShowSigninInterceptionBubble(
+      interception_bubble_handle_ = delegate_->ShowSigninInterceptionBubble(
           web_contents, bubble_parameters,
           base::BindOnce(&DiceWebSigninInterceptor::OnProfileSwitchChoice,
                          base::Unretained(this), entry->GetPath()));
@@ -267,14 +270,17 @@
 void DiceWebSigninInterceptor::CreateBrowserAfterSigninInterception(
     CoreAccountId account_id,
     content::WebContents* intercepted_contents,
+    std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle> bubble_handle,
     bool show_customization_bubble) {
   DCHECK(!session_startup_helper_);
+  DCHECK(bubble_handle);
+  interception_bubble_handle_ = std::move(bubble_handle);
   session_startup_helper_ =
       std::make_unique<DiceInterceptedSessionStartupHelper>(
           profile_, account_id, intercepted_contents);
   session_startup_helper_->Startup(
-      base::Bind(&DiceWebSigninInterceptor::OnNewBrowserCreated,
-                 base::Unretained(this), show_customization_bubble));
+      base::BindOnce(&DiceWebSigninInterceptor::OnNewBrowserCreated,
+                     base::Unretained(this), show_customization_bubble));
 }
 
 void DiceWebSigninInterceptor::Shutdown() {
@@ -295,6 +301,7 @@
   was_interception_ui_displayed_ = false;
   account_info_fetch_start_time_ = base::TimeTicks();
   profile_creation_start_time_ = base::TimeTicks();
+  interception_bubble_handle_.reset();
 }
 
 const ProfileAttributesEntry*
@@ -395,7 +402,7 @@
   Delegate::BubbleParameters bubble_parameters{
       *interception_type, info, GetPrimaryAccountInfo(identity_manager_),
       GetAutogeneratedThemeColors(profile_color).frame_color};
-  delegate_->ShowSigninInterceptionBubble(
+  interception_bubble_handle_ = delegate_->ShowSigninInterceptionBubble(
       web_contents(), bubble_parameters,
       base::BindOnce(&DiceWebSigninInterceptor::OnProfileCreationChoice,
                      base::Unretained(this), info, profile_color));
@@ -423,6 +430,7 @@
     return;
   }
 
+  DCHECK(interception_bubble_handle_);
   profile_creation_start_time_ = base::TimeTicks::Now();
   base::string16 profile_name;
   profile_name = profiles::GetDefaultNameForNewSignedInProfile(account_info);
@@ -445,8 +453,9 @@
     return;
   }
 
-  profile_creation_start_time_ = base::TimeTicks::Now();
+  DCHECK(interception_bubble_handle_);
   DCHECK(!dice_signed_in_profile_creator_);
+  profile_creation_start_time_ = base::TimeTicks::Now();
   // Unretained is fine because the profile creator is owned by this.
   dice_signed_in_profile_creator_ =
       std::make_unique<DiceSignedInProfileCreator>(
@@ -490,13 +499,16 @@
   // Work is done in this profile, the flow continues in the
   // DiceWebSigninInterceptor that is attached to the new profile.
   DiceWebSigninInterceptorFactory::GetForProfile(new_profile)
-      ->CreateBrowserAfterSigninInterception(account_id_, web_contents(),
-                                             show_customization_bubble);
+      ->CreateBrowserAfterSigninInterception(
+          account_id_, web_contents(), std::move(interception_bubble_handle_),
+          show_customization_bubble);
   Reset();
 }
 
 void DiceWebSigninInterceptor::OnNewBrowserCreated(
     bool show_customization_bubble) {
+  DCHECK(interception_bubble_handle_);
+  interception_bubble_handle_.reset();  // Close the bubble now.
   session_startup_helper_.reset();
   if (show_customization_bubble) {
     Browser* browser = chrome::FindBrowserWithProfile(profile_);
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.h b/chrome/browser/signin/dice_web_signin_interceptor.h
index 2f9f9ca..098c608 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.h
+++ b/chrome/browser/signin/dice_web_signin_interceptor.h
@@ -98,6 +98,14 @@
   kMaxValue = kNotDisplayed,
 };
 
+// The ScopedDiceWebSigninInterceptionBubbleHandle closes the signin intercept
+// bubble when it is destroyed, if the bubble is still opened. Note that this
+// handle does not prevent the bubble from being closed for other reasons.
+class ScopedDiceWebSigninInterceptionBubbleHandle {
+ public:
+  virtual ~ScopedDiceWebSigninInterceptionBubbleHandle() = 0;
+};
+
 // Returns whether the heuristic outcome is a success (the signin should be
 // intercepted).
 bool SigninInterceptionHeuristicOutcomeIsSuccess(
@@ -111,11 +119,16 @@
 // enterprise or multi-user case:
 // * MaybeInterceptWebSignin() is called when the new signin happens.
 // * Wait until the account info is downloaded.
-// * Interception UI is shown by the delegate.
+// * Interception UI is shown by the delegate. Keep a handle on the bubble.
 // * If the user approved, a new profile is created and the token is moved from
 //   this profile to the new profile, using DiceSignedInProfileCreator.
 // * At this point, the flow ends in this profile, and continues in the new
-//   profile using DiceInterceptedSessionStartupHelper.
+//   profile using DiceInterceptedSessionStartupHelper to add the account.
+// * When the account is available on the web in the new profile:
+//   - A new browser window is created for the new profile,
+//   - The tab is moved to the new profile,
+//   - The interception bubble is closed by deleting the handle,
+//   - The profile customization bubble is shown.
 class DiceWebSigninInterceptor : public KeyedService,
                                  public content::WebContentsObserver,
                                  public signin::IdentityManager::Observer {
@@ -139,7 +152,15 @@
     // whether the user should continue in a new profile.
     // The callback is never called if the delegate is deleted before it
     // completes.
-    virtual void ShowSigninInterceptionBubble(
+    // May return a nullptr handle if the bubble cannot be shown.
+    // Warning: the handle closes the bubble when it is destroyed ; it is the
+    // responsibility of the caller to keep the handle alive until the bubble
+    // should be closed.
+    // The callback must not be called synchronously if this function returns a
+    // valid handle (because the caller needs to be able to close the bubble
+    // from the callback).
+    virtual std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+    ShowSigninInterceptionBubble(
         content::WebContents* web_contents,
         const BubbleParameters& bubble_parameters,
         base::OnceCallback<void(SigninInterceptionResult)> callback) = 0;
@@ -183,6 +204,8 @@
   void CreateBrowserAfterSigninInterception(
       CoreAccountId account_id,
       content::WebContents* intercepted_contents,
+      std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+          bubble_handle,
       bool show_customization_bubble);
 
   // Returns the outcome of the interception heuristic.
@@ -286,6 +309,9 @@
   // is cancelled if the account info cannot be fetched quickly.
   base::CancelableOnceCallback<void()> on_account_info_update_timeout_;
   std::unique_ptr<DiceSignedInProfileCreator> dice_signed_in_profile_creator_;
+  // Used to retain the interception UI bubble until profile creation completes.
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+      interception_bubble_handle_;
   // Used for metrics:
   bool was_interception_ui_displayed_ = false;
   base::TimeTicks account_info_fetch_start_time_;
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 091d95d..e4265c8cd 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -6,11 +6,13 @@
 
 #include <map>
 
+#include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
@@ -41,18 +43,33 @@
 #include "url/gurl.h"
 
 namespace {
+class FakeDiceWebSigninInterceptorDelegate;
+
+class FakeBubbleHandle : public ScopedDiceWebSigninInterceptionBubbleHandle,
+                         public base::SupportsWeakPtr<FakeBubbleHandle> {
+ public:
+  ~FakeBubbleHandle() override = default;
+};
 
 // Dummy interception delegate that automatically accepts multi user
 // interception.
 class FakeDiceWebSigninInterceptorDelegate
     : public DiceWebSigninInterceptor::Delegate {
  public:
-  void ShowSigninInterceptionBubble(
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+  ShowSigninInterceptionBubble(
       content::WebContents* web_contents,
       const BubbleParameters& bubble_parameters,
       base::OnceCallback<void(SigninInterceptionResult)> callback) override {
     EXPECT_EQ(bubble_parameters.interception_type, expected_interception_type_);
-    std::move(callback).Run(SigninInterceptionResult::kAccepted);
+    auto bubble_handle = std::make_unique<FakeBubbleHandle>();
+    weak_bubble_handle_ = bubble_handle->AsWeakPtr();
+    // The callback must not be called synchronously (see the documentation for
+    // ShowSigninInterceptionBubble).
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  SigninInterceptionResult::kAccepted));
+    return bubble_handle;
   }
   void ShowProfileCustomizationBubble(Browser* browser) override {
     EXPECT_FALSE(customized_browser_)
@@ -66,10 +83,17 @@
     expected_interception_type_ = type;
   }
 
+  bool intercept_bubble_shown() const { return weak_bubble_handle_.get(); }
+
+  bool intercept_bubble_destroyed() const {
+    return weak_bubble_handle_.WasInvalidated();
+  }
+
  private:
   Browser* customized_browser_ = nullptr;
   DiceWebSigninInterceptor::SigninInterceptionType expected_interception_type_ =
       DiceWebSigninInterceptor::SigninInterceptionType::kMultiUser;
+  base::WeakPtr<FakeBubbleHandle> weak_bubble_handle_;
 };
 
 // Waits until a new profile is created.
@@ -252,6 +276,9 @@
   Profile* new_profile =
       InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
   ASSERT_TRUE(new_profile);
+  FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
+      GetInterceptorDelegate(profile());
+  EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
   signin::IdentityManager* new_identity_manager =
       IdentityManagerFactory::GetForProfile(new_profile);
   EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
@@ -285,10 +312,16 @@
 
   CheckHistograms(histogram_tester,
                   SigninInterceptionHeuristicOutcome::kInterceptMultiUser);
+  // Interception bubble is destroyed in the source profile, and was not shown
+  // in the new profile.
+  FakeDiceWebSigninInterceptorDelegate* new_interceptor_delegate =
+      GetInterceptorDelegate(new_profile);
+  EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
+  EXPECT_FALSE(new_interceptor_delegate->intercept_bubble_shown());
+  EXPECT_FALSE(new_interceptor_delegate->intercept_bubble_destroyed());
   // Profile customization UI was shown exactly once in the new profile.
-  EXPECT_EQ(GetInterceptorDelegate(new_profile)->customized_browser(),
-            added_browser);
-  EXPECT_EQ(GetInterceptorDelegate(profile())->customized_browser(), nullptr);
+  EXPECT_EQ(new_interceptor_delegate->customized_browser(), added_browser);
+  EXPECT_EQ(source_interceptor_delegate->customized_browser(), nullptr);
 }
 
 // Tests the complete profile switch flow when the profile is not loaded.
@@ -319,11 +352,14 @@
   int original_tab_count = browser()->tab_strip_model()->count();
 
   // Do the signin interception.
-  GetInterceptorDelegate(profile())->set_expected_interception_type(
+  FakeDiceWebSigninInterceptorDelegate* source_interceptor_delegate =
+      GetInterceptorDelegate(profile());
+  source_interceptor_delegate->set_expected_interception_type(
       DiceWebSigninInterceptor::SigninInterceptionType::kProfileSwitch);
   Profile* new_profile =
       InterceptAndWaitProfileCreation(web_contents, account_info.account_id);
   ASSERT_TRUE(new_profile);
+  EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_shown());
   signin::IdentityManager* new_identity_manager =
       IdentityManagerFactory::GetForProfile(new_profile);
   EXPECT_TRUE(new_identity_manager->HasAccountWithRefreshToken(
@@ -348,9 +384,11 @@
 
   CheckHistograms(histogram_tester,
                   SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch);
+  // Interception bubble was closed.
+  EXPECT_TRUE(source_interceptor_delegate->intercept_bubble_destroyed());
   // Profile customization was not shown.
   EXPECT_EQ(GetInterceptorDelegate(new_profile)->customized_browser(), nullptr);
-  EXPECT_EQ(GetInterceptorDelegate(profile())->customized_browser(), nullptr);
+  EXPECT_EQ(source_interceptor_delegate->customized_browser(), nullptr);
 }
 
 // Tests the complete profile switch flow when the profile is already loaded.
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
index 0ae69ac..a12a04a5 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
@@ -37,7 +37,7 @@
 class MockDiceWebSigninInterceptorDelegate
     : public DiceWebSigninInterceptor::Delegate {
  public:
-  MOCK_METHOD(void,
+  MOCK_METHOD(std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>,
               ShowSigninInterceptionBubble,
               (content::WebContents * web_contents,
                const BubbleParameters& bubble_parameters,
@@ -456,6 +456,7 @@
           [&delegate_callback](
               base::OnceCallback<void(SigninInterceptionResult)> callback) {
             delegate_callback = std::move(callback);
+            return nullptr;
           })));
   MaybeIntercept(account_info.account_id);
   testing::Mock::VerifyAndClearExpectations(mock_delegate());
@@ -504,8 +505,10 @@
         .WillOnce(testing::WithArg<2>(testing::Invoke(
             [](base::OnceCallback<void(SigninInterceptionResult)> callback) {
               std::move(callback).Run(SigninInterceptionResult::kDeclined);
+              return nullptr;
             })));
     MaybeIntercept(account_info.account_id);
+    EXPECT_EQ(interceptor()->is_interception_in_progress(), false);
     histogram_tester.ExpectUniqueSample(
         "Signin.Intercept.HeuristicOutcome",
         SigninInterceptionHeuristicOutcome::kInterceptEnterprise, i + 1);
diff --git a/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc b/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
index b3bfd42..03284e0 100644
--- a/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
+++ b/chrome/browser/signin/process_dice_header_delegate_impl_unittest.cc
@@ -40,11 +40,13 @@
     : public DiceWebSigninInterceptor::Delegate {
  public:
   ~TestDiceWebSigninInterceptorDelegate() override = default;
-  void ShowSigninInterceptionBubble(
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+  ShowSigninInterceptionBubble(
       content::WebContents* web_contents,
       const BubbleParameters& bubble_parameters,
       base::OnceCallback<void(SigninInterceptionResult)> callback) override {
     std::move(callback).Run(SigninInterceptionResult::kDeclined);
+    return nullptr;
   }
   void ShowProfileCustomizationBubble(Browser* browser) override {}
 };
diff --git a/chrome/browser/signin/services/android/BUILD.gn b/chrome/browser/signin/services/android/BUILD.gn
index f2359be..426e654b 100644
--- a/chrome/browser/signin/services/android/BUILD.gn
+++ b/chrome/browser/signin/services/android/BUILD.gn
@@ -20,6 +20,7 @@
     "java/src/org/chromium/chrome/browser/signin/services/IdentityServicesProvider.java",
     "java/src/org/chromium/chrome/browser/signin/services/ProfileDataCache.java",
     "java/src/org/chromium/chrome/browser/signin/services/ProfileDownloader.java",
+    "java/src/org/chromium/chrome/browser/signin/services/SigninHelper.java",
     "java/src/org/chromium/chrome/browser/signin/services/SigninManager.java",
     "java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java",
     "java/src/org/chromium/chrome/browser/signin/services/SigninPreferencesManager.java",
@@ -28,6 +29,7 @@
   ]
   deps = [
     ":java_resources",
+    "$google_play_services_package:google_play_services_auth_base_java",
     "//base:base_java",
     "//base:jni_java",
     "//chrome/browser/preferences:java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninHelper.java
similarity index 94%
rename from chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java
rename to chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninHelper.java
index 5150c35..7c137827 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninHelper.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninHelper.java
@@ -2,7 +2,7 @@
 // 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;
+package org.chromium.chrome.browser.signin.services;
 
 import android.accounts.Account;
 import android.content.Context;
@@ -19,9 +19,6 @@
 import org.chromium.base.Log;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.task.AsyncTask;
-import org.chromium.chrome.browser.signin.services.SigninManager;
-import org.chromium.chrome.browser.signin.services.SigninManager.SignInCallback;
-import org.chromium.chrome.browser.signin.services.SigninPreferencesManager;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.AccountTrackerService;
 import org.chromium.components.signin.AccountUtils;
@@ -56,11 +53,10 @@
     public static final class SystemAccountChangeEventChecker
             implements SigninHelper.AccountChangeEventChecker {
         @Override
-        public List<String> getAccountChangeEvents(
-                Context context, int index, String accountName) {
+        public List<String> getAccountChangeEvents(Context context, int index, String accountName) {
             try {
-                List<AccountChangeEvent> list = GoogleAuthUtil.getAccountChangeEvents(
-                        context, index, accountName);
+                List<AccountChangeEvent> list =
+                        GoogleAuthUtil.getAccountChangeEvents(context, index, accountName);
                 List<String> result = new ArrayList<>(list.size());
                 for (AccountChangeEvent e : list) {
                     if (e.getChangeType() == GoogleAuthUtil.CHANGE_TYPE_ACCOUNT_RENAMED_TO) {
@@ -89,7 +85,7 @@
      * Please use SigninHelperProvider to get SigninHelper instance instead of creating it
      * manually.
      */
-    SigninHelper(SigninManager signinManager, AccountTrackerService accountTrackerService,
+    public SigninHelper(SigninManager signinManager, AccountTrackerService accountTrackerService,
             SigninPreferencesManager signinPreferencesManager) {
         mSigninManager = signinManager;
         mAccountTrackerService = accountTrackerService;
@@ -192,7 +188,7 @@
         final Account account = AccountUtils.createAccountFromName(newName);
 
         mSigninManager.signinAndEnableSync(
-                SigninAccessPoint.ACCOUNT_RENAMED, account, new SignInCallback() {
+                SigninAccessPoint.ACCOUNT_RENAMED, account, new SigninManager.SignInCallback() {
                     @Override
                     public void onSignInComplete() {
                         validateAccountsInternal(true);
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java
index cbd355a7..f470b24 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java
@@ -33,6 +33,15 @@
                 promoAction, AccountConsistencyPromoAction.MAX);
     }
 
+    /**
+     * Logs AccountPickerBottomSheet shown count histograms.
+     */
+    public static void logAccountConsistencyPromoShownCount(String histogram) {
+        RecordHistogram.recordExactLinearHistogram(histogram,
+                SigninPreferencesManager.getInstance().getAccountPickerBottomSheetShownCount(),
+                100);
+    }
+
     @VisibleForTesting
     @NativeMethods
     public interface Natives {
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninPreferencesManager.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninPreferencesManager.java
index d8b7dea..171e0e09 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninPreferencesManager.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninPreferencesManager.java
@@ -188,4 +188,18 @@
     public String getLegacySyncAccountEmail() {
         return mManager.readString(ChromePreferenceKeys.SIGNIN_LEGACY_SYNC_ACCOUNT_EMAIL, null);
     }
+
+    /**
+     * Increments the shown count for the account picker bottom sheet.
+     */
+    public void incrementAccountPickerBottomSheetShownCount() {
+        mManager.incrementInt(ChromePreferenceKeys.ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT);
+    }
+
+    /**
+     * Returns the number of times account picker bottom sheet has already been shown.
+     */
+    public int getAccountPickerBottomSheetShownCount() {
+        return mManager.readInt(ChromePreferenceKeys.ACCOUNT_PICKER_BOTTOM_SHEET_SHOWN_COUNT);
+    }
 }
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
index 58e8eb0..2879967 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
@@ -107,11 +107,6 @@
 
   subresource_filter::mojom::ActivationLevel effective_activation_level =
       initial_activation_level;
-  if (activated_via_devtools_) {
-    effective_activation_level =
-        subresource_filter::mojom::ActivationLevel::kEnabled;
-    *decision = subresource_filter::ActivationDecision::FORCED_ACTIVATION;
-  }
 
   if (profile_context_->ads_intervention_manager()->ShouldActivate(
           navigation_handle)) {
@@ -161,14 +156,6 @@
                                : nullptr;
 }
 
-void ChromeSubresourceFilterClient::ToggleForceActivationInCurrentWebContents(
-    bool force_activation) {
-  if (!activated_via_devtools_ && force_activation)
-    subresource_filter::ContentSubresourceFilterThrottleManager::LogAction(
-        subresource_filter::SubresourceFilterAction::kForcedActivationEnabled);
-  activated_via_devtools_ = force_activation;
-}
-
 void ChromeSubresourceFilterClient::ShowUI(const GURL& url) {
 #if defined(OS_ANDROID)
   InfoBarService* infobar_service =
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
index b893848..0ef6322 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
@@ -58,11 +58,6 @@
   GetSafeBrowsingDatabaseManager() override;
   void OnReloadRequested() override;
 
-  // Should be called by devtools in response to a protocol command to enable ad
-  // blocking in this WebContents. Should only persist while devtools is
-  // attached.
-  void ToggleForceActivationInCurrentWebContents(bool force_activation);
-
  private:
   void ShowUI(const GURL& url);
 
@@ -78,11 +73,6 @@
   std::unique_ptr<subresource_filter::ProfileInteractionManager>
       profile_interaction_manager_;
 
-  // Corresponds to a devtools command which triggers filtering on all page
-  // loads. We must be careful to ensure this boolean does not persist after the
-  // devtools window is closed, which should be handled by the devtools system.
-  bool activated_via_devtools_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeSubresourceFilterClient);
 };
 
diff --git a/chrome/browser/subresource_filter/subresource_filter_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
index cf0ab237..bb0dc310 100644
--- a/chrome/browser/subresource_filter/subresource_filter_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
@@ -182,58 +182,6 @@
   EXPECT_TRUE(GetSettingsManager()->GetSiteActivationFromMetadata(url));
 }
 
-TEST_F(SubresourceFilterTest, ToggleForceActivation) {
-  base::HistogramTester histogram_tester;
-  const GURL url("https://example.test/");
-
-  // Navigate initially, should be no activation.
-  SimulateNavigateAndCommit(url, main_rfh());
-  EXPECT_TRUE(CreateAndNavigateDisallowedSubframe(main_rfh()));
-  EXPECT_FALSE(GetSettingsManager()->GetSiteActivationFromMetadata(url));
-
-  // Simulate opening devtools and forcing activation.
-  GetClient()->ToggleForceActivationInCurrentWebContents(true);
-  histogram_tester.ExpectBucketCount(
-      kSubresourceFilterActionsHistogram,
-      subresource_filter::SubresourceFilterAction::kForcedActivationEnabled, 1);
-
-  SimulateNavigateAndCommit(url, main_rfh());
-  EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
-
-  histogram_tester.ExpectBucketCount(
-      kSubresourceFilterActionsHistogram,
-      subresource_filter::SubresourceFilterAction::kUIShown, 1);
-
-  EXPECT_TRUE(GetSettingsManager()->GetSiteActivationFromMetadata(url));
-  histogram_tester.ExpectBucketCount(
-      "SubresourceFilter.PageLoad.ActivationDecision",
-      subresource_filter::ActivationDecision::FORCED_ACTIVATION, 1);
-
-  // Simulate closing devtools.
-  GetClient()->ToggleForceActivationInCurrentWebContents(false);
-
-  SimulateNavigateAndCommit(url, main_rfh());
-  EXPECT_TRUE(CreateAndNavigateDisallowedSubframe(main_rfh()));
-  histogram_tester.ExpectBucketCount(
-      kSubresourceFilterActionsHistogram,
-      subresource_filter::SubresourceFilterAction::kForcedActivationEnabled, 1);
-}
-
-TEST_F(SubresourceFilterTest, ToggleOffForceActivation_AfterCommit) {
-  base::HistogramTester histogram_tester;
-  GetClient()->ToggleForceActivationInCurrentWebContents(true);
-  const GURL url("https://example.test/");
-  SimulateNavigateAndCommit(url, main_rfh());
-  GetClient()->ToggleForceActivationInCurrentWebContents(false);
-
-  // Resource should be disallowed, since navigation commit had activation.
-  EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
-
-  histogram_tester.ExpectBucketCount(
-      kSubresourceFilterActionsHistogram,
-      subresource_filter::SubresourceFilterAction::kUIShown, 1);
-}
-
 enum class AdBlockOnAbusiveSitesTest { kEnabled, kDisabled };
 
 TEST_F(SubresourceFilterTest, NotifySafeBrowsing) {
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.cc b/chrome/browser/ui/passwords/manage_passwords_test.cc
index e2b439a..2dcd2053 100644
--- a/chrome/browser/ui/passwords/manage_passwords_test.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_test.cc
@@ -155,7 +155,8 @@
   // This is an unrelated compromised credential that should still be fixed.
   password_manager::CompromisedCredentials compromised(
       "https://somesite.com/", ASCIIToUTF16(kTestUsername), base::Time(),
-      password_manager::CompromiseType::kLeaked, false);
+      password_manager::CompromiseType::kLeaked,
+      password_manager::IsMuted(false));
   password_store->AddCompromisedCredentials(compromised);
   SetupPendingPassword();
   GetController()->SavePassword(password_form_.username_value,
@@ -174,10 +175,12 @@
   // This is an unrelated compromised credential that should still be fixed.
   password_manager::CompromisedCredentials some_compromised(
       "https://somesite.com/", ASCIIToUTF16(kTestUsername), base::Time(),
-      password_manager::CompromiseType::kLeaked, false);
+      password_manager::CompromiseType::kLeaked,
+      password_manager::IsMuted(false));
   password_manager::CompromisedCredentials current_compromised(
       password_form_.signon_realm, password_form_.username_value, base::Time(),
-      password_manager::CompromiseType::kLeaked, false);
+      password_manager::CompromiseType::kLeaked,
+      password_manager::IsMuted(false));
   password_store->AddCompromisedCredentials(some_compromised);
   password_store->AddCompromisedCredentials(current_compromised);
   SetupPendingPassword();
@@ -236,7 +239,8 @@
 
   password_manager::CompromisedCredentials compromised(
       password_form_.signon_realm, password_form_.username_value, base::Time(),
-      password_manager::CompromiseType::kLeaked, false);
+      password_manager::CompromiseType::kLeaked,
+      password_manager::IsMuted(false));
   fetcher_.set_compromised({compromised});
 
   fetcher_.NotifyFetchCompleted();
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 417b9930..1f5e337 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -212,9 +212,10 @@
 }
 
 CompromisedCredentials CreateCompromised(const PasswordForm& form) {
-  return CompromisedCredentials(
-      form.signon_realm, form.username_value, base::Time(),
-      password_manager::CompromiseType::kLeaked, false);
+  return CompromisedCredentials(form.signon_realm, form.username_value,
+                                base::Time(),
+                                password_manager::CompromiseType::kLeaked,
+                                password_manager::IsMuted(false));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
index 5064d892..2a30ffc 100644
--- a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
+++ b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.cc
@@ -13,15 +13,16 @@
 
 DiceWebSigninInterceptorDelegate::~DiceWebSigninInterceptorDelegate() = default;
 
-void DiceWebSigninInterceptorDelegate::ShowSigninInterceptionBubble(
+std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+DiceWebSigninInterceptorDelegate::ShowSigninInterceptionBubble(
     content::WebContents* web_contents,
     const BubbleParameters& bubble_parameters,
     base::OnceCallback<void(SigninInterceptionResult)> callback) {
   if (!web_contents) {
     std::move(callback).Run(SigninInterceptionResult::kNotDisplayed);
-    return;
+    return nullptr;
   }
-  ShowSigninInterceptionBubbleInternal(
+  return ShowSigninInterceptionBubbleInternal(
       chrome::FindBrowserWithWebContents(web_contents), bubble_parameters,
       std::move(callback));
 }
diff --git a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h
index 3d020e0..b796032 100644
--- a/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h
+++ b/chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h
@@ -22,7 +22,8 @@
   ~DiceWebSigninInterceptorDelegate() override;
 
   // DiceWebSigninInterceptor::Delegate
-  void ShowSigninInterceptionBubble(
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+  ShowSigninInterceptionBubble(
       content::WebContents* web_contents,
       const BubbleParameters& bubble_parameters,
       base::OnceCallback<void(SigninInterceptionResult)> callback) override;
@@ -30,7 +31,8 @@
 
  private:
   // Implemented in dice_web_signin_interception_bubble_view.cc
-  void ShowSigninInterceptionBubbleInternal(
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+  ShowSigninInterceptionBubbleInternal(
       Browser* browser,
       const BubbleParameters& bubble_parameters,
       base::OnceCallback<void(SigninInterceptionResult)> callback);
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.cc b/chrome/browser/ui/thumbnails/thumbnail_image.cc
index bfaa104e..3a8e8433 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.cc
@@ -4,10 +4,8 @@
 
 #include "chrome/browser/ui/thumbnails/thumbnail_image.h"
 
-#include <algorithm>
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
@@ -17,12 +15,15 @@
 #include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/skia_util.h"
 
-ThumbnailImage::Subscription::Subscription(
-    scoped_refptr<ThumbnailImage> thumbnail)
-    : thumbnail_(std::move(thumbnail)) {}
+void ThumbnailImage::Observer::OnThumbnailImageAvailable(
+    gfx::ImageSkia thumbnail_image) {}
 
-ThumbnailImage::Subscription::~Subscription() {
-  thumbnail_->HandleSubscriptionDestroyed(this);
+void ThumbnailImage::Observer::OnCompressedThumbnailDataAvailable(
+    CompressedThumbnailData thumbnail_data) {}
+
+base::Optional<gfx::Size> ThumbnailImage::Observer::GetThumbnailSizeHint()
+    const {
+  return base::nullopt;
 }
 
 ThumbnailImage::Delegate::~Delegate() {
@@ -45,17 +46,29 @@
     delegate_->thumbnail_ = nullptr;
 }
 
-std::unique_ptr<ThumbnailImage::Subscription> ThumbnailImage::Subscribe() {
-  // Use explicit new since Subscription constructor is private.
-  auto subscription =
-      base::WrapUnique(new Subscription(base::WrapRefCounted(this)));
-  subscribers_.insert(subscribers_.end(), subscription.get());
+void ThumbnailImage::AddObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(observer);
+  if (!observers_.HasObserver(observer)) {
+    const bool is_first_observer = !observers_.might_have_observers();
+    observers_.AddObserver(observer);
+    if (is_first_observer && delegate_)
+      delegate_->ThumbnailImageBeingObservedChanged(true);
+  }
+}
 
-  // Notify |delegate_| if this is the first subscriber.
-  if (subscribers_.size() == 1)
-    delegate_->ThumbnailImageBeingObservedChanged(true);
+void ThumbnailImage::RemoveObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(observer);
+  if (observers_.HasObserver(observer)) {
+    observers_.RemoveObserver(observer);
+    if (delegate_ && !observers_.might_have_observers())
+      delegate_->ThumbnailImageBeingObservedChanged(false);
+  }
+}
 
-  return subscription;
+bool ThumbnailImage::HasObserver(const Observer* observer) const {
+  return observers_.HasObserver(observer);
 }
 
 void ThumbnailImage::AssignSkBitmap(SkBitmap bitmap) {
@@ -126,22 +139,18 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (async_operation_finished_callback_)
     async_operation_finished_callback_.Run();
-
-  for (Subscription* subscription : subscribers_) {
-    auto size_hint = subscription->size_hint_;
-    if (subscription->uncompressed_image_callback_)
-      subscription->uncompressed_image_callback_.Run(
-          size_hint ? CropPreviewImage(image, *size_hint) : image);
+  for (auto& observer : observers_) {
+    auto size_hint = observer.GetThumbnailSizeHint();
+    observer.OnThumbnailImageAvailable(
+        size_hint ? CropPreviewImage(image, *size_hint) : image);
   }
 }
 
 void ThumbnailImage::NotifyCompressedDataObservers(
     CompressedThumbnailData data) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (Subscription* subscription : subscribers_) {
-    if (subscription->compressed_image_callback_)
-      subscription->compressed_image_callback_.Run(data);
-  }
+  for (auto& observer : observers_)
+    observer.OnCompressedThumbnailDataAvailable(data);
 }
 
 // static
@@ -199,17 +208,3 @@
   source_image.bitmap()->extractSubset(&cropped, gfx::RectToSkIRect(clip_rect));
   return gfx::ImageSkia::CreateFrom1xBitmap(cropped);
 }
-
-void ThumbnailImage::HandleSubscriptionDestroyed(Subscription* subscription) {
-  // The order of |subscribers_| does not matter. We can simply swap
-  // |subscription| in |subscribers_| with the last element, then pop it
-  // off the back.
-  auto it = std::find(subscribers_.begin(), subscribers_.end(), subscription);
-  DCHECK(it != subscribers_.end());
-  std::swap(*it, *(subscribers_.end() - 1));
-  subscribers_.pop_back();
-
-  // If that was the last subscriber, tell |delegate_|.
-  if (subscribers_.empty())
-    delegate_->ThumbnailImageBeingObservedChanged(false);
-}
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.h b/chrome/browser/ui/thumbnails/thumbnail_image.h
index fce916a..9b67c4f 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.h
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.h
@@ -5,12 +5,10 @@
 #ifndef CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
 #define CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
 
-#include <memory>
 #include <utility>
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -31,27 +29,16 @@
   using CompressedThumbnailData =
       scoped_refptr<base::RefCountedData<std::vector<uint8_t>>>;
 
-  class Subscription {
+  // Observes uncompressed and/or compressed versions of the thumbnail image as
+  // they are available.
+  class Observer : public base::CheckedObserver {
    public:
-    Subscription() = delete;
-    ~Subscription();
+    // Receives uncompressed thumbnail image data. Default is no-op.
+    virtual void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image);
 
-    using UncompressedImageCallback =
-        base::RepeatingCallback<void(gfx::ImageSkia)>;
-    using CompressedImageCallback =
-        base::RepeatingCallback<void(CompressedThumbnailData)>;
-
-    // Set callbacks to receive image data. Subscribers are not allowed
-    // to unsubscribe (by destroying |this|) from the callback. If
-    // necessary, post a task to destroy it soon after.
-
-    void SetUncompressedImageCallback(UncompressedImageCallback callback) {
-      uncompressed_image_callback_ = std::move(callback);
-    }
-
-    void SetCompressedImageCallback(CompressedImageCallback callback) {
-      compressed_image_callback_ = std::move(callback);
-    }
+    // Receives compressed thumbnail image data. Default is no-op.
+    virtual void OnCompressedThumbnailDataAvailable(
+        CompressedThumbnailData thumbnail_data);
 
     // Provides a desired aspect ratio and minimum size that the observer will
     // accept. If not specified, or if available thumbnail data is smaller in
@@ -67,20 +54,7 @@
     // image passed to OnThumbnailImageAvailable fits the needs of the observer
     // for display purposes, without the observer having to further crop the
     // image. The default is unspecified.
-    void SetSizeHint(const base::Optional<gfx::Size>& size_hint) {
-      size_hint_ = size_hint;
-    }
-
-   private:
-    friend class ThumbnailImage;
-
-    explicit Subscription(scoped_refptr<ThumbnailImage> thumbnail);
-
-    scoped_refptr<ThumbnailImage> thumbnail_;
-    base::Optional<gfx::Size> size_hint_;
-
-    UncompressedImageCallback uncompressed_image_callback_;
-    CompressedImageCallback compressed_image_callback_;
+    virtual base::Optional<gfx::Size> GetThumbnailSizeHint() const;
   };
 
   // Represents the endpoint
@@ -103,14 +77,9 @@
 
   bool has_data() const { return data_.get(); }
 
-  // Subscribe to thumbnail updates. See |Subscription| to set a
-  // callback and conigure additional options.
-  //
-  // Even if a callback is not set, the subscription influences
-  // thumbnail capture. It should be destroyed when updates are not
-  // needed. It is designed to be stored in base::Optional, created and
-  // destroyed as needed.
-  std::unique_ptr<Subscription> Subscribe();
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+  bool HasObserver(const Observer* observer) const;
 
   // Sets the SkBitmap data and notifies observers with the resulting image.
   void AssignSkBitmap(SkBitmap bitmap);
@@ -161,18 +130,11 @@
   static gfx::ImageSkia CropPreviewImage(const gfx::ImageSkia& source_image,
                                          const gfx::Size& minimum_size);
 
-  void HandleSubscriptionDestroyed(Subscription* subscription);
-
   Delegate* delegate_;
 
   CompressedThumbnailData data_;
 
-  // Subscriptions are inserted on |Subscribe()| calls and removed when
-  // they are destroyed via callback. The order of subscriber
-  // notification doesn't matter, so don't maintain any ordering. Since
-  // the number of subscribers for a given thumbnail is expected to be
-  // small, doing a linear search to remove a subscriber is fine.
-  std::vector<Subscription*> subscribers_;
+  base::ObserverList<Observer> observers_;
 
   // Called when an asynchronous operation (such as encoding image data upon
   // assignment or decoding image data for observers) finishes or fails.
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc b/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
index 8533b63..93b7ed7c 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
@@ -9,7 +9,6 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
 #include "base/test/task_environment.h"
@@ -21,47 +20,81 @@
 constexpr int kTestBitmapWidth = 200;
 constexpr int kTestBitmapHeight = 123;
 
-template <typename... T>
-base::RepeatingCallback<void(T...)> IgnoreArgs(
-    base::RepeatingCallback<void()> cb) {
-  auto helper = [](T...) {};
-  return base::BindRepeating(helper).Then(std::move(cb));
-}
-
-class CallbackWaiter {
+// Waits for thumbnail images (or compressed data) and can report how many
+// images it has received.
+class TestThumbnailImageObserver : public ThumbnailImage::Observer {
  public:
-  CallbackWaiter() {
-    callback_ = base::BindRepeating(&CallbackWaiter::HandleCallback,
-                                    weak_ptr_factory_.GetWeakPtr());
+  // Wait for the uncompressed thumbnail image.
+  void WaitForImage() {
+    if (new_image_count_ > last_image_count_) {
+      last_image_count_ = new_image_count_;
+      return;
+    }
+
+    // Need a fresh loop since we may have quit out of the last one.
+    run_loop_ = std::make_unique<base::RunLoop>();
+    waiting_for_image_ = true;
+    run_loop_->Run();
   }
 
-  base::RepeatingClosure callback() { return callback_; }
-
-  bool called() const { return called_; }
-
-  void Reset() { called_ = false; }
-
-  void Wait() {
-    if (called_)
+  // Wait for compressed thumbnail data.
+  void WaitForCompressedData() {
+    if (new_compressed_count_ > last_compressed_count_) {
+      last_compressed_count_ = new_compressed_count_;
       return;
+    }
 
-    base::RunLoop run_loop;
-    quit_closure_ = run_loop.QuitClosure();
-    run_loop.Run();
+    // Need a fresh loop since we may have quit out of the last one.
+    run_loop_ = std::make_unique<base::RunLoop>();
+    waiting_for_data_ = true;
+    run_loop_->Run();
+  }
+
+  int new_image_count() const { return new_image_count_; }
+  gfx::ImageSkia thumbnail_image() const { return thumbnail_image_; }
+  int new_compressed_count() const { return new_compressed_count_; }
+  ThumbnailImage::CompressedThumbnailData compressed_data() const {
+    return compressed_data_;
+  }
+
+  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer>* scoped_observer() {
+    return &scoped_observer_;
   }
 
  private:
-  void HandleCallback() {
-    if (quit_closure_)
-      std::move(quit_closure_).Run();
-    called_ = true;
+  // ThumbnailImage::Observer:
+  void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image) override {
+    ++new_image_count_;
+    thumbnail_image_ = thumbnail_image;
+    if (waiting_for_image_) {
+      last_image_count_ = new_image_count_;
+      run_loop_->Quit();
+      waiting_for_image_ = false;
+    }
   }
 
-  base::RepeatingClosure callback_;
-  base::OnceClosure quit_closure_;
-  bool called_ = false;
+  void OnCompressedThumbnailDataAvailable(
+      ThumbnailImage::CompressedThumbnailData thumbnail_data) override {
+    ++new_compressed_count_;
+    compressed_data_ = thumbnail_data;
+    if (waiting_for_data_) {
+      last_compressed_count_ = new_compressed_count_;
+      run_loop_->Quit();
+      waiting_for_data_ = false;
+    }
+  }
 
-  base::WeakPtrFactory<CallbackWaiter> weak_ptr_factory_{this};
+  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer> scoped_observer_{
+      this};
+  int new_image_count_ = 0;
+  int last_image_count_ = 0;
+  gfx::ImageSkia thumbnail_image_;
+  int new_compressed_count_ = 0;
+  int last_compressed_count_ = 0;
+  ThumbnailImage::CompressedThumbnailData compressed_data_;
+  bool waiting_for_image_ = false;
+  bool waiting_for_data_ = false;
+  std::unique_ptr<base::RunLoop> run_loop_;
 };
 
 }  // anonymous namespace
@@ -95,192 +128,155 @@
   DISALLOW_COPY_AND_ASSIGN(ThumbnailImageTest);
 };
 
-using Subscription = ThumbnailImage::Subscription;
-
-TEST_F(ThumbnailImageTest, AddRemoveSubscriber) {
+TEST_F(ThumbnailImageTest, Add_Remove_Observer) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
   EXPECT_FALSE(is_being_observed());
-
-  std::unique_ptr<Subscription> subscription = image->Subscribe();
+  TestThumbnailImageObserver observer;
+  image->AddObserver(&observer);
+  EXPECT_TRUE(image->HasObserver(&observer));
   EXPECT_TRUE(is_being_observed());
-
-  subscription.reset();
+  image->RemoveObserver(&observer);
+  EXPECT_FALSE(image->HasObserver(&observer));
   EXPECT_FALSE(is_being_observed());
 }
 
-TEST_F(ThumbnailImageTest, AddRemoveMultipleObservers) {
+TEST_F(ThumbnailImageTest, Add_Remove_MultipleObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
   EXPECT_FALSE(is_being_observed());
-
-  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
+  TestThumbnailImageObserver observer;
+  TestThumbnailImageObserver observer2;
+  image->AddObserver(&observer);
+  EXPECT_TRUE(image->HasObserver(&observer));
   EXPECT_TRUE(is_being_observed());
-
-  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
+  image->AddObserver(&observer2);
+  EXPECT_TRUE(image->HasObserver(&observer2));
   EXPECT_TRUE(is_being_observed());
-
-  subscription1.reset();
+  image->RemoveObserver(&observer);
+  EXPECT_FALSE(image->HasObserver(&observer));
+  EXPECT_TRUE(image->HasObserver(&observer2));
   EXPECT_TRUE(is_being_observed());
-
-  subscription2.reset();
+  image->RemoveObserver(&observer2);
+  EXPECT_FALSE(image->HasObserver(&observer2));
   EXPECT_FALSE(is_being_observed());
 }
 
-TEST_F(ThumbnailImageTest, AssignSkBitmapNotifiesObservers) {
+TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-
-  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
-  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
-
-  CallbackWaiter waiter1;
-  subscription1->SetUncompressedImageCallback(
-      IgnoreArgs<gfx::ImageSkia>(waiter1.callback()));
-
-  CallbackWaiter waiter2;
-  subscription2->SetUncompressedImageCallback(
-      IgnoreArgs<gfx::ImageSkia>(waiter2.callback()));
+  TestThumbnailImageObserver observer;
+  TestThumbnailImageObserver observer2;
+  observer.scoped_observer()->Add(image.get());
+  observer2.scoped_observer()->Add(image.get());
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
+  observer.WaitForImage();
+  observer2.WaitForImage();
+  EXPECT_EQ(1, observer.new_image_count());
+  EXPECT_EQ(1, observer2.new_image_count());
+  EXPECT_FALSE(observer.thumbnail_image().isNull());
+  EXPECT_FALSE(observer2.thumbnail_image().isNull());
+  EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
+            observer.thumbnail_image().size());
 }
 
 TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesObserversAgain) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-
-  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
-  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
-
-  CallbackWaiter waiter1;
-  subscription1->SetUncompressedImageCallback(
-      IgnoreArgs<gfx::ImageSkia>(waiter1.callback()));
-
-  CallbackWaiter waiter2;
-  subscription2->SetUncompressedImageCallback(
-      IgnoreArgs<gfx::ImageSkia>(waiter2.callback()));
+  TestThumbnailImageObserver observer;
+  TestThumbnailImageObserver observer2;
+  observer.scoped_observer()->Add(image.get());
+  observer2.scoped_observer()->Add(image.get());
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
-
-  waiter1.Reset();
-  waiter2.Reset();
-
+  observer.WaitForImage();
+  observer2.WaitForImage();
   image->AssignSkBitmap(bitmap);
-
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
+  observer.WaitForImage();
+  observer2.WaitForImage();
+  EXPECT_EQ(2, observer.new_image_count());
+  EXPECT_EQ(2, observer2.new_image_count());
+  EXPECT_FALSE(observer.thumbnail_image().isNull());
+  EXPECT_FALSE(observer2.thumbnail_image().isNull());
+  EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
+            observer.thumbnail_image().size());
 }
 
 TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesCompressedObservers) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-
-  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
-  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
-
-  CallbackWaiter waiter1;
-  subscription1->SetCompressedImageCallback(
-      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter1.callback()));
-
-  CallbackWaiter waiter2;
-  subscription2->SetCompressedImageCallback(
-      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter2.callback()));
+  TestThumbnailImageObserver observer;
+  TestThumbnailImageObserver observer2;
+  observer.scoped_observer()->Add(image.get());
+  observer2.scoped_observer()->Add(image.get());
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
-  image->AssignSkBitmap(bitmap);
+  auto compressed = Compress(bitmap);
 
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
+  image->AssignSkBitmap(bitmap);
+  observer.WaitForCompressedData();
+  observer2.WaitForCompressedData();
+  EXPECT_EQ(1, observer.new_compressed_count());
+  EXPECT_EQ(1, observer2.new_compressed_count());
+  EXPECT_TRUE(observer.compressed_data());
+  EXPECT_TRUE(observer2.compressed_data());
+  EXPECT_EQ(compressed, observer.compressed_data()->data);
+  EXPECT_EQ(compressed, observer2.compressed_data()->data);
 }
 
 TEST_F(ThumbnailImageTest, AssignSkBitmap_NotifiesCompressedObserversAgain) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-
-  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
-  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
-
-  CallbackWaiter waiter1;
-  subscription1->SetCompressedImageCallback(
-      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter1.callback()));
-
-  CallbackWaiter waiter2;
-  subscription2->SetCompressedImageCallback(
-      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter2.callback()));
+  TestThumbnailImageObserver observer;
+  TestThumbnailImageObserver observer2;
+  observer.scoped_observer()->Add(image.get());
+  observer2.scoped_observer()->Add(image.get());
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
-  image->AssignSkBitmap(bitmap);
-
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
-
-  waiter1.Reset();
-  waiter2.Reset();
+  auto compressed = Compress(bitmap);
 
   image->AssignSkBitmap(bitmap);
-
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
+  observer.WaitForCompressedData();
+  observer2.WaitForCompressedData();
+  image->AssignSkBitmap(bitmap);
+  observer.WaitForCompressedData();
+  observer2.WaitForCompressedData();
+  EXPECT_EQ(2, observer.new_compressed_count());
+  EXPECT_EQ(2, observer2.new_compressed_count());
+  EXPECT_TRUE(observer.compressed_data());
+  EXPECT_TRUE(observer2.compressed_data());
+  EXPECT_EQ(compressed, observer.compressed_data()->data);
+  EXPECT_EQ(compressed, observer2.compressed_data()->data);
 }
 
 TEST_F(ThumbnailImageTest, RequestThumbnailImage) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
-
-  std::unique_ptr<Subscription> subscription1 = image->Subscribe();
-
-  CallbackWaiter waiter1;
-  subscription1->SetUncompressedImageCallback(
-      IgnoreArgs<gfx::ImageSkia>(waiter1.callback()));
+  TestThumbnailImageObserver observer;
+  observer.scoped_observer()->Add(image.get());
 
   SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  waiter1.Wait();
-  EXPECT_TRUE(waiter1.called());
-  waiter1.Reset();
+  observer.WaitForImage();
 
-  std::unique_ptr<Subscription> subscription2 = image->Subscribe();
-
-  CallbackWaiter waiter2;
-  subscription2->SetUncompressedImageCallback(
-      IgnoreArgs<gfx::ImageSkia>(waiter2.callback()));
-
+  TestThumbnailImageObserver observer2;
+  observer2.scoped_observer()->Add(image.get());
   image->RequestThumbnailImage();
-  waiter1.Wait();
-  waiter2.Wait();
-  EXPECT_TRUE(waiter1.called());
-  EXPECT_TRUE(waiter2.called());
+  observer.WaitForImage();
+  observer2.WaitForImage();
+  EXPECT_EQ(2, observer.new_image_count());
+  EXPECT_EQ(1, observer2.new_image_count());
+  EXPECT_FALSE(observer2.thumbnail_image().isNull());
+  EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
+            observer2.thumbnail_image().size());
 }
 
 TEST_F(ThumbnailImageTest, RequestCompressedThumbnailData) {
   auto image = base::MakeRefCounted<ThumbnailImage>(this);
+  TestThumbnailImageObserver observer;
+  observer.scoped_observer()->Add(image.get());
 
-  std::unique_ptr<Subscription> subscription = image->Subscribe();
-
-  CallbackWaiter waiter;
-  subscription->SetCompressedImageCallback(
-      IgnoreArgs<ThumbnailImage::CompressedThumbnailData>(waiter.callback()));
-
-  SkBitmap bitmap = CreateBitmap(kTestBitmapWidth, kTestBitmapHeight);
+  SkBitmap bitmap = CreateBitmap(kTestBitmapHeight, kTestBitmapHeight);
   image->AssignSkBitmap(bitmap);
-  waiter.Wait();
-  EXPECT_TRUE(waiter.called());
-  waiter.Reset();
+  observer.WaitForImage();
 
+  const int count_before_request = observer.new_compressed_count();
   image->RequestCompressedThumbnailData();
-  waiter.Wait();
-  EXPECT_TRUE(waiter.called());
+  EXPECT_EQ(count_before_request + 1, observer.new_compressed_count());
 }
diff --git a/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc b/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc
index 6ecc30d0..3e1ce62 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_tab_helper_browsertest.cc
@@ -29,30 +29,37 @@
 
 namespace {
 
-class ThumbnailWaiter {
+class ThumbnailWaiter : public ThumbnailImage::Observer {
  public:
   ThumbnailWaiter() = default;
-  ~ThumbnailWaiter() = default;
+  ~ThumbnailWaiter() override = default;
 
   base::Optional<gfx::ImageSkia> WaitForThumbnail(ThumbnailImage* thumbnail) {
-    std::unique_ptr<ThumbnailImage::Subscription> subscription =
-        thumbnail->Subscribe();
-    subscription->SetUncompressedImageCallback(base::BindRepeating(
-        &ThumbnailWaiter::ThumbnailImageCallback, base::Unretained(this)));
-    thumbnail->RequestThumbnailImage();
+    DCHECK(!thumbnail_);
+    thumbnail_ = thumbnail;
+    scoped_observer_.Add(thumbnail);
+    thumbnail_->RequestThumbnailImage();
     run_loop_.Run();
     return image_;
   }
 
  protected:
-  void ThumbnailImageCallback(gfx::ImageSkia thumbnail_image) {
-    image_ = std::move(thumbnail_image);
-    run_loop_.Quit();
+  // ThumbnailImage::Observer:
+  void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image) override {
+    if (thumbnail_) {
+      scoped_observer_.Remove(thumbnail_);
+      thumbnail_ = nullptr;
+      image_ = thumbnail_image;
+      run_loop_.Quit();
+    }
   }
 
  private:
   base::RunLoop run_loop_;
+  ThumbnailImage* thumbnail_ = nullptr;
   base::Optional<gfx::ImageSkia> image_;
+  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer> scoped_observer_{
+      this};
 };
 
 }  // anonymous namespace
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 45629d2..6779068 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -480,7 +480,6 @@
   resize_handle_view_ =
       AddChildView(&view_holder_, std::move(resize_handle_view));
 #endif
-  UpdateControlsVisibility(false);
 }
 
 void OverlayWindowViews::OnRootViewReady() {
@@ -496,6 +495,9 @@
   for (std::unique_ptr<views::View>& child : view_holder_)
     contents_view->AddChildView(std::move(child));
   view_holder_.clear();
+
+  // Don't show the controls until the mouse hovers over the window.
+  UpdateControlsVisibility(false);
 }
 
 void OverlayWindowViews::UpdateLayerBoundsWithLetterboxing(
@@ -626,7 +628,7 @@
       visible_controls_views[0]->SetPosition(
           gfx::Point(mid_window_x - kSecondaryControlSize.width() / 2,
                      secondary_control_y));
-      return;
+      break;
     }
     case 2: {
       /* | ----- [ ] [ ] ----- | */
@@ -638,7 +640,7 @@
       visible_controls_views[1]->SetSize(kSecondaryControlSize);
       visible_controls_views[1]->SetPosition(
           gfx::Point(mid_window_x + kControlMargin / 2, secondary_control_y));
-      return;
+      break;
     }
     case 3: {
       /* | --- [ ] [ ] [ ] --- | */
@@ -669,7 +671,7 @@
             mid_window_x + kSecondaryControlSize.width() / 2 + kControlMargin,
             secondary_control_y));
       }
-      return;
+      break;
     }
     case 4: {
       /* | - [ ] [ ] [ ] [ ] - | */
@@ -692,10 +694,15 @@
       visible_controls_views[3]->SetPosition(gfx::Point(
           mid_window_x + kControlMargin * 3 / 2 + kSecondaryControlSize.width(),
           secondary_control_y));
-      return;
+      break;
     }
+    default:
+      NOTREACHED();
   }
-  DCHECK(false);
+
+  // This will actually update the visibility of a control that was just added
+  // or removed, see SetPlayPauseButtonVisibility(), etc.
+  UpdateControlsVisibility(AreControlsVisible());
 }
 
 gfx::Rect OverlayWindowViews::CalculateControlsBounds(int x,
@@ -779,7 +786,11 @@
 }
 
 void OverlayWindowViews::SetSkipAdButtonVisibility(bool is_visible) {
+  if (show_skip_ad_button_ == is_visible)
+    return;
+
   show_skip_ad_button_ = is_visible;
+  UpdateControlsBounds();
 }
 
 void OverlayWindowViews::SetNextTrackButtonVisibility(bool is_visible) {
@@ -1032,6 +1043,11 @@
   return controls_scrim_view_->layer()->visible();
 }
 
+bool OverlayWindowViews::IsLayoutPendingForTesting() const {
+  return update_controls_bounds_timer_ &&
+         update_controls_bounds_timer_->IsRunning();
+}
+
 ui::Layer* OverlayWindowViews::GetControlsScrimLayer() {
   return controls_scrim_view_->layer();
 }
@@ -1108,8 +1124,8 @@
   return skip_ad_controls_view_;
 }
 
-gfx::Point OverlayWindowViews::back_to_tab_image_position_for_testing() const {
-  return back_to_tab_controls_view_->origin();
+views::View* OverlayWindowViews::back_to_tab_controls_for_testing() const {
+  return back_to_tab_controls_view_;
 }
 
 gfx::Point OverlayWindowViews::close_image_position_for_testing() const {
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h
index 96fe145..de1b130 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.h
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -84,11 +84,15 @@
   // visible.
   bool AreControlsVisible() const;
 
+  // Determines whether a layout of the window controls has been scheduled but
+  // is not done yet.
+  bool IsLayoutPendingForTesting() const;
+
   views::PlaybackImageButton* play_pause_controls_view_for_testing() const;
   views::TrackImageButton* next_track_controls_view_for_testing() const;
   views::TrackImageButton* previous_track_controls_view_for_testing() const;
   views::SkipAdLabelButton* skip_ad_controls_view_for_testing() const;
-  gfx::Point back_to_tab_image_position_for_testing() const;
+  views::View* back_to_tab_controls_for_testing() const;
   gfx::Point close_image_position_for_testing() const;
   gfx::Point resize_handle_position_for_testing() const;
   OverlayWindowViews::PlaybackState playback_state_for_testing() const;
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc b/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
index 0b5d15b4..1ceb903 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
@@ -6,6 +6,7 @@
 #include <utility>
 
 #include "chrome/browser/ui/views/overlay/overlay_window_views.h"
+#include "chrome/browser/ui/views/overlay/track_image_button.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "content/public/browser/picture_in_picture_window_controller.h"
@@ -276,3 +277,40 @@
   overlay_window().OnNativeWidgetMove();
   EXPECT_EQ(gfx::Size(500, 500), overlay_window().GetMaximumSize());
 }
+
+// Tests that Next Track button bounds are updated right away when window
+// controls are hidden.
+TEST_F(OverlayWindowViewsTest, NextTrackButtonAddedWhenControlsHidden) {
+  ASSERT_FALSE(overlay_window().AreControlsVisible());
+  ASSERT_TRUE(overlay_window()
+                  .next_track_controls_view_for_testing()
+                  ->size()
+                  .IsEmpty());
+
+  const auto origin_before_layout =
+      overlay_window().next_track_controls_view_for_testing()->origin();
+
+  overlay_window().SetNextTrackButtonVisibility(true);
+  EXPECT_NE(overlay_window().next_track_controls_view_for_testing()->origin(),
+            origin_before_layout);
+  EXPECT_FALSE(overlay_window().IsLayoutPendingForTesting());
+}
+
+// Tests that Previous Track button bounds are updated right away when window
+// controls are hidden.
+TEST_F(OverlayWindowViewsTest, PreviousTrackButtonAddedWhenControlsHidden) {
+  ASSERT_FALSE(overlay_window().AreControlsVisible());
+  ASSERT_TRUE(overlay_window()
+                  .previous_track_controls_view_for_testing()
+                  ->size()
+                  .IsEmpty());
+
+  const auto origin_before_layout =
+      overlay_window().previous_track_controls_view_for_testing()->origin();
+
+  overlay_window().SetPreviousTrackButtonVisibility(true);
+  EXPECT_NE(
+      overlay_window().previous_track_controls_view_for_testing()->origin(),
+      origin_before_layout);
+  EXPECT_FALSE(overlay_window().IsLayoutPendingForTesting());
+}
diff --git a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc
index d44416be..5ab072d 100644
--- a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc
+++ b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc
@@ -10,11 +10,11 @@
 #include "base/callback_helpers.h"
 #include "base/check.h"
 #include "base/feature_list.h"
+#include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -37,40 +37,55 @@
 
 }  // namespace
 
-DiceWebSigninInterceptionBubbleView::ScopedBrowserListObserver::
-    ScopedBrowserListObserver(BrowserListObserver* owner)
-    : owner_(owner) {
-  DCHECK(owner_);
-  BrowserList::AddObserver(owner_);
-}
-
-DiceWebSigninInterceptionBubbleView::ScopedBrowserListObserver::
-    ~ScopedBrowserListObserver() {
-  BrowserList::RemoveObserver(owner_);
-}
-
 DiceWebSigninInterceptionBubbleView::~DiceWebSigninInterceptionBubbleView() {
   // Cancel if the bubble is destroyed without user interaction.
   if (callback_) {
     RecordInterceptionResult(bubble_parameters_, profile_,
                              SigninInterceptionResult::kIgnored);
+    // The callback may synchronously delete a handle, which would attempt to
+    // close this bubble while it is being destroyed. Invalidate the handles now
+    // to prevent this.
+    weak_factory_.InvalidateWeakPtrs();
     std::move(callback_).Run(SigninInterceptionResult::kIgnored);
   }
 }
 
 // static
-void DiceWebSigninInterceptionBubbleView::CreateBubble(
+std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+DiceWebSigninInterceptionBubbleView::CreateBubble(
     Profile* profile,
     views::View* anchor_view,
     const DiceWebSigninInterceptor::Delegate::BubbleParameters&
         bubble_parameters,
     base::OnceCallback<void(SigninInterceptionResult)> callback) {
+  auto interception_bubble =
+      base::WrapUnique(new DiceWebSigninInterceptionBubbleView(
+          profile, anchor_view, bubble_parameters, std::move(callback)));
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle> handle =
+      interception_bubble->GetHandle();
   // The widget is owned by the views system.
   views::Widget* widget = views::BubbleDialogDelegateView::CreateBubble(
-      new DiceWebSigninInterceptionBubbleView(
-          profile, anchor_view, bubble_parameters, std::move(callback)));
+      std::move(interception_bubble));
   // TODO(droger): Delay showing the bubble until the web view is loaded.
   widget->Show();
+  return handle;
+}
+
+DiceWebSigninInterceptionBubbleView::ScopedHandle::~ScopedHandle() {
+  if (!bubble_)
+    return;  // The bubble was already closed, do nothing.
+  views::Widget* widget = bubble_->GetWidget();
+  if (!widget)
+    return;
+  widget->CloseWithReason(
+      bubble_->HasAccepted() ? views::Widget::ClosedReason::kAcceptButtonClicked
+                             : views::Widget::ClosedReason::kUnspecified);
+}
+
+DiceWebSigninInterceptionBubbleView::ScopedHandle::ScopedHandle(
+    base::WeakPtr<DiceWebSigninInterceptionBubbleView> bubble)
+    : bubble_(std::move(bubble)) {
+  DCHECK(bubble_);
 }
 
 // static
@@ -116,6 +131,10 @@
   }
 }
 
+bool DiceWebSigninInterceptionBubbleView::HasAccepted() const {
+  return has_accepted_;
+}
+
 DiceWebSigninInterceptionBubbleView::DiceWebSigninInterceptionBubbleView(
     Profile* profile,
     views::View* anchor_view,
@@ -128,6 +147,7 @@
       bubble_parameters_(bubble_parameters),
       callback_(std::move(callback)) {
   DCHECK(profile_);
+  DCHECK(callback_);
   set_close_on_deactivate(false);
 
   // Create the web view in the native bubble.
@@ -154,36 +174,30 @@
   SetLayoutManager(std::make_unique<views::FillLayout>());
 }
 
+std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+DiceWebSigninInterceptionBubbleView::GetHandle() const {
+  return std::make_unique<ScopedHandle>(weak_factory_.GetWeakPtr());
+}
+
 void DiceWebSigninInterceptionBubbleView::OnWebUIUserChoice(bool accept) {
+  has_accepted_ = accept;
   SigninInterceptionResult result = accept
                                         ? SigninInterceptionResult::kAccepted
                                         : SigninInterceptionResult::kDeclined;
   RecordInterceptionResult(bubble_parameters_, profile_, result);
   std::move(callback_).Run(result);
-
-  // Only close the dialog when the user declined. If the user accepted the
-  // dialog displays a spinner until the new browser is created.
-  if (accept) {
-    browser_list_observer_ = std::make_unique<ScopedBrowserListObserver>(this);
-  } else {
+  if (!accept) {
+    // Only close the dialog when the user declined. If the user accepted the
+    // dialog displays a spinner until the handle is released.
     GetWidget()->CloseWithReason(
         views::Widget::ClosedReason::kCancelButtonClicked);
   }
 }
 
-void DiceWebSigninInterceptionBubbleView::OnBrowserAdded(Browser* browser) {
-  // This function is only called when the user already accepted.
-  // Close the bubble when any new browser is created, not necessarily the one
-  // related to the signin interception ; this is a good-enough approximation.
-  DCHECK(browser_list_observer_);
-  browser_list_observer_.reset();
-  GetWidget()->CloseWithReason(
-      views::Widget::ClosedReason::kAcceptButtonClicked);
-}
-
 // DiceWebSigninInterceptorDelegate --------------------------------------------
 
-void DiceWebSigninInterceptorDelegate::ShowSigninInterceptionBubbleInternal(
+std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+DiceWebSigninInterceptorDelegate::ShowSigninInterceptionBubbleInternal(
     Browser* browser,
     const DiceWebSigninInterceptor::Delegate::BubbleParameters&
         bubble_parameters,
@@ -194,6 +208,6 @@
                                  ->toolbar_button_provider()
                                  ->GetAvatarToolbarButton();
   DCHECK(anchor_view);
-  DiceWebSigninInterceptionBubbleView::CreateBubble(
+  return DiceWebSigninInterceptionBubbleView::CreateBubble(
       browser->profile(), anchor_view, bubble_parameters, std::move(callback));
 }
diff --git a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.h b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.h
index bd8ef75e..43f22b8 100644
--- a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.h
+++ b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.h
@@ -8,9 +8,10 @@
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
 #include "base/callback.h"
+#include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/signin/dice_web_signin_interceptor.h"
-#include "chrome/browser/ui/browser_list_observer.h"
 
 namespace views {
 class View;
@@ -21,8 +22,7 @@
 // Bubble shown as part of Dice web signin interception. This bubble is
 // implemented as a WebUI page rendered inside a native bubble.
 class DiceWebSigninInterceptionBubbleView
-    : public views::BubbleDialogDelegateView,
-      public BrowserListObserver {
+    : public views::BubbleDialogDelegateView {
  public:
   ~DiceWebSigninInterceptionBubbleView() override;
 
@@ -31,12 +31,16 @@
   DiceWebSigninInterceptionBubbleView& operator=(
       const DiceWebSigninInterceptionBubbleView& other) = delete;
 
-  static void CreateBubble(
-      Profile* profile,
-      views::View* anchor_view,
-      const DiceWebSigninInterceptor::Delegate::BubbleParameters&
-          bubble_parameters,
-      base::OnceCallback<void(SigninInterceptionResult)> callback);
+  // Warning: the bubble is closed when the handle is destroyed ; it is the
+  // responsibility of the caller to keep the handle alive until the bubble
+  // should be closed.
+  static std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle>
+  CreateBubble(Profile* profile,
+               views::View* anchor_view,
+               const DiceWebSigninInterceptor::Delegate::BubbleParameters&
+                   bubble_parameters,
+               base::OnceCallback<void(SigninInterceptionResult)> callback)
+      WARN_UNUSED_RESULT;
 
   // Record metrics about the result of the signin interception.
   static void RecordInterceptionResult(
@@ -45,6 +49,9 @@
       Profile* profile,
       SigninInterceptionResult result);
 
+  // Returns true if the user has accepted the interception.
+  bool HasAccepted() const;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(DiceWebSigninInterceptionBubbleBrowserTest,
                            BubbleClosed);
@@ -53,19 +60,19 @@
   FRIEND_TEST_ALL_PREFIXES(DiceWebSigninInterceptionBubbleBrowserTest,
                            BubbleAccepted);
 
-  // base::ScopedObservation does not work for BrowserList because its
-  // AddObserver/RemoveObserver methods are static.
-  class ScopedBrowserListObserver {
+  // Closes the bubble when `ScopedHandle` is destroyed. Does nothing if the
+  // bubble has been already closed.
+  class ScopedHandle : public ScopedDiceWebSigninInterceptionBubbleHandle {
    public:
-    explicit ScopedBrowserListObserver(BrowserListObserver* owner);
-    ~ScopedBrowserListObserver();
+    explicit ScopedHandle(
+        base::WeakPtr<DiceWebSigninInterceptionBubbleView> bubble);
+    ~ScopedHandle() override;
 
-    ScopedBrowserListObserver(const ScopedBrowserListObserver&) = delete;
-    ScopedBrowserListObserver& operator=(const ScopedBrowserListObserver&) =
-        delete;
+    ScopedHandle& operator=(const ScopedHandle&) = delete;
+    ScopedHandle(const ScopedHandle&) = delete;
 
    private:
-    BrowserListObserver* owner_;
+    base::WeakPtr<DiceWebSigninInterceptionBubbleView> bubble_;
   };
 
   DiceWebSigninInterceptionBubbleView(
@@ -75,20 +82,23 @@
           bubble_parameters,
       base::OnceCallback<void(SigninInterceptionResult)> callback);
 
+  // Gets a handle on the bubble. Warning: the bubble is closed when the handle
+  // is destroyed ; it is the responsibility of the caller to keep the handle
+  // alive until the bubble should be closed.
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle> GetHandle()
+      const;
+
   // This bubble has no native buttons. The user accepts or cancels through this
   // method, which is called by the inner web UI.
   void OnWebUIUserChoice(bool accept);
 
-  // BrowserListObserver:
-  void OnBrowserAdded(Browser* browser) override;
-
   Profile* profile_;
+  bool has_accepted_ = false;
   DiceWebSigninInterceptor::Delegate::BubbleParameters bubble_parameters_;
   base::OnceCallback<void(SigninInterceptionResult)> callback_;
 
-  // Once the user accepted, a spinner is shown until the new browser is
-  // created, and then the bubble is closed.
-  std::unique_ptr<ScopedBrowserListObserver> browser_list_observer_;
+  // Last member in the class: pointers are invalidated before other fields.
+  base::WeakPtrFactory<DiceWebSigninInterceptionBubbleView> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PROFILES_DICE_WEB_SIGNIN_INTERCEPTION_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_browsertest.cc b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_browsertest.cc
index eda6462..365f523 100644
--- a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/callback_helpers.h"
 #include "base/optional.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/ui/browser.h"
@@ -25,9 +26,9 @@
 
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
-    DiceWebSigninInterceptionBubbleView::CreateBubble(
+    bubble_handle_ = DiceWebSigninInterceptionBubbleView::CreateBubble(
         browser()->profile(), GetAvatarButton(), GetTestBubbleParameters(),
-        base::OnceCallback<void(SigninInterceptionResult)>());
+        base::DoNothing());
   }
 
   // Returns the avatar button, which is the anchor view for the interception
@@ -59,6 +60,7 @@
   }
 
   base::Optional<SigninInterceptionResult> callback_result_;
+  std::unique_ptr<ScopedDiceWebSigninInterceptionBubbleHandle> bubble_handle_;
 };
 
 IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptionBubbleBrowserTest,
@@ -141,16 +143,21 @@
   widget->Show();
   EXPECT_FALSE(callback_result_.has_value());
 
+  // Take a handle on the bubble, to close it later.
+  bubble_handle_ = bubble->GetHandle();
+
   views::test::WidgetClosingObserver closing_observer(widget);
+  EXPECT_FALSE(bubble->HasAccepted());
   // Simulate clicking Accept in the WebUI.
   bubble->OnWebUIUserChoice(/*accept=*/true);
   ASSERT_TRUE(callback_result_.has_value());
   EXPECT_EQ(callback_result_, SigninInterceptionResult::kAccepted);
+  EXPECT_TRUE(bubble->HasAccepted());
 
   // Widget was not closed yet.
   ASSERT_FALSE(closing_observer.widget_closed());
-  // Simulate a new browser being created by the interception.
-  CreateGuestBrowser();
+  // Simulate completion of the interception process.
+  bubble_handle_.reset();
   // Widget will close now.
   closing_observer.Wait();
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 4d9a74d..d8a7bec 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -36,6 +36,7 @@
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/native_theme/themed_vector_icon.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/md_text_button.h"
@@ -547,6 +548,23 @@
   return gfx::ImageSkia();
 }
 
+const base::string16 ProfileMenuViewBase::GetAccessibleMenuName(
+    const base::string16& title,
+    const base::string16& subtitle) {
+  if (title.empty()) {
+    if (subtitle.empty())
+      return GetAccessibleWindowTitle();
+
+    return subtitle;
+  } else {
+    if (subtitle.empty())
+      return title;
+
+    return l10n_util::GetStringFUTF16(IDS_CONCAT_TWO_STRINGS_WITH_COMMA, title,
+                                      subtitle);
+  }
+}
+
 void ProfileMenuViewBase::SetProfileIdentityInfo(
     const base::string16& profile_name,
     SkColor profile_background_color,
@@ -574,10 +592,7 @@
 
   // Use the profile identity info to label the entire menu, for accessibility
   // users to get the user account as context information when they open it.
-  const base::string16& accessible_menu_name =
-      title.empty() ? (subtitle.empty() ? GetAccessibleWindowTitle() : subtitle)
-                    : title;
-  GetViewAccessibility().OverrideName(accessible_menu_name);
+  GetViewAccessibility().OverrideName(GetAccessibleMenuName(title, subtitle));
 
   if (!new_design) {
     if (!profile_name.empty()) {
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.h b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
index 774b4f5..bed6a00 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
@@ -190,6 +190,9 @@
 
   void UpdateSyncInfoContainerBackground();
 
+  const base::string16 GetAccessibleMenuName(const base::string16& title,
+                                             const base::string16& subtitle);
+
   Browser* const browser_;
 
   views::Button* const anchor_button_;
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index 5df0a2a1..8757384 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -396,11 +396,12 @@
 // Maintains a set of thumbnails to watch, ensuring the capture count on the
 // associated WebContents stays nonzero until a valid thumbnail has been
 // captured.
-class TabHoverCardBubbleView::ThumbnailObserver {
+class TabHoverCardBubbleView::ThumbnailObserver
+    : public ThumbnailImage::Observer {
  public:
   explicit ThumbnailObserver(TabHoverCardBubbleView* hover_card)
       : hover_card_(hover_card) {}
-  ~ThumbnailObserver() = default;
+  ~ThumbnailObserver() override = default;
 
   // Begin watching the specified thumbnail image for updates. Ideally, should
   // trigger the associated WebContents to load (if not loaded already) and
@@ -410,17 +411,13 @@
     if (current_image_ == thumbnail_image)
       return;
 
-    subscription_.reset();
+    scoped_observation_.Reset();
     current_image_ = std::move(thumbnail_image);
-    if (!current_image_)
-      return;
 
-    subscription_ = current_image_->Subscribe();
-    subscription_->SetSizeHint(TabStyle::GetPreviewImageSize());
-    subscription_->SetUncompressedImageCallback(base::BindRepeating(
-        &ThumbnailObserver::ThumbnailImageCallback, base::Unretained(this)));
-
-    current_image_->RequestThumbnailImage();
+    if (current_image_) {
+      scoped_observation_.Observe(current_image_.get());
+      current_image_->RequestThumbnailImage();
+    }
   }
 
   // Returns the current (most recent) thumbnail being watched.
@@ -428,13 +425,18 @@
     return current_image_;
   }
 
-  void ThumbnailImageCallback(gfx::ImageSkia preview_image) {
+  base::Optional<gfx::Size> GetThumbnailSizeHint() const override {
+    return TabStyle::GetPreviewImageSize();
+  }
+
+  void OnThumbnailImageAvailable(gfx::ImageSkia preview_image) override {
     hover_card_->OnThumbnailImageAvailable(std::move(preview_image));
   }
 
   scoped_refptr<ThumbnailImage> current_image_;
-  std::unique_ptr<ThumbnailImage::Subscription> subscription_;
   TabHoverCardBubbleView* const hover_card_;
+  base::ScopedObservation<ThumbnailImage, ThumbnailImage::Observer>
+      scoped_observation_{this};
 };
 
 TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
index 007e40d..6e291145 100644
--- a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
@@ -167,7 +167,8 @@
         "test.com",
         base::ASCIIToUTF16("test" +
                            base::NumberToString(test_credential_counter_++)),
-        base::Time(), password_manager::CompromiseType::kLeaked, false));
+        base::Time(), password_manager::CompromiseType::kLeaked,
+        password_manager::IsMuted(false)));
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 186b8f67..d65f2e37e 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -978,6 +978,8 @@
        IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT_AND_DEVICE},
       {"passwordMovePasswordsToAccount",
        IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT},
+      {"passwordMovePasswordsToAccountDialogFooter",
+       IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_FOOTER},
       {"passwordMovePasswordsToAccountDialogTitle",
        IDS_SETTINGS_PASSWORD_MOVE_PASSWORDS_TO_ACCOUNT_DIALOG_TITLE},
       {"passwordMoveToAccountDialogTitle",
@@ -1450,6 +1452,8 @@
       {"privacySandboxPageHeading", IDS_SETTINGS_PRIVACY_SANDBOX_PAGE_HEADING},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
+
+  html_source->AddString("privacySandboxURL", chrome::kPrivacySandboxURL);
 }
 
 void AddSafetyCheckStrings(content::WebUIDataSource* html_source) {
diff --git a/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc b/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc
index aa733a93..161644f9 100644
--- a/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc
+++ b/chrome/browser/ui/webui/tab_strip/thumbnail_tracker.cc
@@ -15,17 +15,14 @@
 
 // Handles requests for a given tab's thumbnail and watches for thumbnail
 // updates for the lifetime of the tab.
-class ThumbnailTracker::ContentsData : public content::WebContentsObserver {
+class ThumbnailTracker::ContentsData : public content::WebContentsObserver,
+                                       public ThumbnailImage::Observer {
  public:
   ContentsData(ThumbnailTracker* parent, content::WebContents* contents)
       : content::WebContentsObserver(contents), parent_(parent) {
     thumbnail_ = parent_->thumbnail_getter_.Run(contents);
-    if (!thumbnail_)
-      return;
-
-    subscription_ = thumbnail_->Subscribe();
-    subscription_->SetCompressedImageCallback(base::BindRepeating(
-        &ContentsData::ThumbnailImageCallback, base::Unretained(this)));
+    if (thumbnail_)
+      observer_.Add(thumbnail_.get());
   }
 
   void RequestThumbnail() {
@@ -38,7 +35,7 @@
     // We must un-observe each ThumbnailImage when the WebContents it came from
     // closes.
     if (thumbnail_) {
-      subscription_.reset();
+      observer_.Remove(thumbnail_.get());
       thumbnail_.reset();
     }
 
@@ -46,14 +43,16 @@
     parent_->ContentsClosed(web_contents());
   }
 
- private:
-  void ThumbnailImageCallback(CompressedThumbnailData image) {
-    parent_->ThumbnailUpdated(web_contents(), image);
+  // ThumbnailImage::Observer:
+  void OnCompressedThumbnailDataAvailable(
+      CompressedThumbnailData thumbnail_image) override {
+    parent_->ThumbnailUpdated(web_contents(), thumbnail_image);
   }
 
+ private:
   ThumbnailTracker* parent_;
   scoped_refptr<ThumbnailImage> thumbnail_;
-  std::unique_ptr<ThumbnailImage::Subscription> subscription_;
+  ScopedObserver<ThumbnailImage, ThumbnailImage::Observer> observer_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ContentsData);
 };
diff --git a/chrome/browser/ui/webui/test_data_source.cc b/chrome/browser/ui/webui/test_data_source.cc
index 50a7f5c..4c8a72b 100644
--- a/chrome/browser/ui/webui/test_data_source.cc
+++ b/chrome/browser/ui/webui/test_data_source.cc
@@ -42,6 +42,8 @@
   custom_paths_ = {
       {"/chai.js", "third_party/chaijs/chai.js"},
       {"/mocha.js", "third_party/mocha/mocha.js"},
+      {"/test_loader.html", "ui/webui/resources/html/test_loader.html"},
+      {"/test_loader.js", "ui/webui/resources/js/test_loader.js"},
   };
 }
 
@@ -63,14 +65,16 @@
 }
 
 std::string TestDataSource::GetMimeType(const std::string& path) {
-  if (base::EndsWith(path, ".html", base::CompareCase::INSENSITIVE_ASCII) ||
+  std::string clean_path = GetURLForPath(path).path();
+  if (base::EndsWith(clean_path, ".html",
+                     base::CompareCase::INSENSITIVE_ASCII) ||
       base::StartsWith(GetURLForPath(path).query(), kModuleQuery,
                        base::CompareCase::INSENSITIVE_ASCII)) {
     // Direct request for HTML, or autogenerated HTML response for module query.
     return "text/html";
   }
   // The test data source currently only serves HTML and JS.
-  CHECK(base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
+  CHECK(base::EndsWith(clean_path, ".js", base::CompareCase::INSENSITIVE_ASCII))
       << "Tried to read file with unexpected type from test data source: "
       << path;
   return "application/javascript";
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 9e305f7..faa1be0 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1608163120-124e90d18fac2bedecb2b27b978f1959d49c27fb.profdata
+chrome-linux-master-1608206305-4389a6a6a359fb8abf452a6f4bc1a98be145b6cd.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index b0dc505..9d1d645 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1608163120-6c9e93d828b8e2586093ed8c8e6e7f26f6a53c36.profdata
+chrome-mac-master-1608206305-43a406c5421ed694ad09e180106f1be95f3f52fa.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 7e2a3213..732900f 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1608141458-c308516ff1ef658b0966cc17b391babeaf39cbb5.profdata
+chrome-win32-master-1608163120-4986bcffa849ffffd428f29082c0e0792a369cda.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 95a4794..7f9fd1e 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1608152110-c6b8168a74d771ecb9159dac1f934f7c3a6ae962.profdata
+chrome-win64-master-1608173994-6a2e9c3f805f7667eca640b2b557f764d57c9690.profdata
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index e85f2f0..e5d4c11 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -238,6 +238,9 @@
     "https://support.google.com/chrome/?p=settings_privacy";
 #endif
 
+const char kPrivacySandboxURL[] =
+    "https://www.chromium.org/Home/chromium-privacy/privacy-sandbox";
+
 const char kRemoveNonCWSExtensionURL[] =
     "https://support.google.com/chrome/?p=ui_remove_non_cws_extensions";
 
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 462c10d..6ad80524 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -207,6 +207,9 @@
 // "Learn more" URL for the Privacy section under Options.
 extern const char kPrivacyLearnMoreURL[];
 
+// "Learn more" URL for the privacy sandbox.
+extern const char kPrivacySandboxURL[];
+
 // The URL for the Learn More link of the non-CWS bubble.
 extern const char kRemoveNonCWSExtensionURL[];
 
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index b8379e7..8425272f 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -540,7 +540,6 @@
       // Avoid any race conditions from having the browser tell subframes that
       // they're prerendering.
       new prerender::PrerenderHelper(render_frame,
-                                     prerender_helper->prerender_mode(),
                                      prerender_helper->histogram_prefix());
     }
   }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 86b3d861..5462da8 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6541,6 +6541,7 @@
       "//third_party/pywebsocket3/src/mod_pywebsocket/",
       "//third_party/tlslite/",
       "//tools/metrics/histograms/enums.xml",
+      "//ui/webui/resources/html/test_loader.html",
       "//ui/webui/resources/js/",
       "$root_out_dir/test_case.html",
       "$root_out_dir/test_case.html.mock-http-headers",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/MockChangeEventChecker.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/MockChangeEventChecker.java
index 097c446..9ead390 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/MockChangeEventChecker.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/MockChangeEventChecker.java
@@ -6,7 +6,7 @@
 
 import android.content.Context;
 
-import org.chromium.chrome.browser.signin.SigninHelper;
+import org.chromium.chrome.browser.signin.services.SigninHelper;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 837e4cd..96e909ed 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -99,6 +99,7 @@
       "sandboxstatus_browsertest.js",
       "settings/a11y/v3_a11y_browsertest.js",
       "settings/cr_settings_v3_browsertest.js",
+      "settings/privacy_sandbox_browsertest.js",
       "settings/settings_idle_load_v3_browsertest.js",
       "tab_search/tab_search_browsertest.js",
       "text_defaults_browsertest.js",
diff --git a/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js
index ec3f7c8b..85d54278 100644
--- a/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js
@@ -35,15 +35,7 @@
     class extends CrComponentsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_components/managed_footnote_test.m.js';
-  }
-
-  /** @override */
-  get extraLibraries() {
-    return [
-      '//third_party/mocha/mocha.js',
-      '//chrome/test/data/webui/mocha_adapter.js',
-    ];
+    return 'chrome://test/test_loader.html?module=cr_components/managed_footnote_test.m.js';
   }
 };
 
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js
index 8cd05ea..d201dbb 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_v3_browsertest.js
@@ -20,10 +20,7 @@
 
   /** @override */
   get extraLibraries() {
-    return [
-      '//third_party/mocha/mocha.js',
-      '//chrome/test/data/webui/mocha_adapter.js',
-    ];
+    return [];
   }
 
   /** @override */
@@ -36,7 +33,7 @@
 var CrElementsButtonV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_button_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_button_tests.m.js';
   }
 };
 
@@ -49,7 +46,7 @@
     class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_container_shadow_behavior_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_container_shadow_behavior_test.m.js';
   }
 };
 
@@ -62,7 +59,7 @@
 var CrElementsDialogV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_dialog_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_dialog_test.m.js';
   }
 };
 
@@ -74,7 +71,7 @@
 var CrElementsDrawerV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_drawer_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_drawer_tests.m.js';
   }
 };
 
@@ -93,7 +90,7 @@
 var CrElementsExpandButtonV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_expand_button_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_expand_button_tests.m.js';
   }
 };
 
@@ -106,7 +103,7 @@
     class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/find_shortcut_behavior_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/find_shortcut_behavior_test.m.js';
   }
 };
 
@@ -119,7 +116,7 @@
     class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_fingerprint_progress_arc_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_fingerprint_progress_arc_tests.m.js';
   }
 
   /** @override */
@@ -144,7 +141,7 @@
 var CrElementsIconButtonV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_icon_button_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_icon_button_tests.m.js';
   }
 };
 
@@ -156,7 +153,7 @@
 var CrElementsLazyRenderV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_lazy_render_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_lazy_render_tests.m.js';
   }
 };
 
@@ -168,7 +165,7 @@
 var CrElementsLinkRowV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_link_row_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_link_row_tests.m.js';
   }
 };
 
@@ -180,7 +177,7 @@
 var CrElementsRadioButtonV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_radio_button_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_radio_button_test.m.js';
   }
 };
 
@@ -192,7 +189,7 @@
 var CrElementsRadioGroupV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_radio_group_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_radio_group_test.m.js';
   }
 };
 
@@ -204,7 +201,7 @@
 var CrElementsScrollableBehaviorV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_scrollable_behavior_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_scrollable_behavior_tests.m.js';
   }
 };
 
@@ -216,7 +213,7 @@
 var CrElementsSearchFieldV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_search_field_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_search_field_tests.m.js';
   }
 };
 
@@ -229,7 +226,7 @@
 var CrElementsSearchableDropDownV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_searchable_drop_down_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_searchable_drop_down_tests.m.js';
   }
 };
 
@@ -242,7 +239,7 @@
 var CrElementsSliderV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_slider_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_slider_test.m.js';
   }
 };
 
@@ -254,7 +251,7 @@
 var CrElementsSplitterV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_splitter_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_splitter_test.js';
   }
 };
 
@@ -266,7 +263,7 @@
 var CrElementsToastV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_toast_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toast_test.m.js';
   }
 };
 
@@ -278,7 +275,7 @@
 var CrElementsToastManagerV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_toast_manager_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toast_manager_test.m.js';
   }
 };
 
@@ -290,7 +287,7 @@
 var CrElementsViewManagerV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_view_manager_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_view_manager_test.m.js';
   }
 };
 
@@ -302,7 +299,7 @@
 var CrElementsPolicyIndicatorV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_policy_indicator_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_policy_indicator_tests.m.js';
   }
 };
 
@@ -329,7 +326,7 @@
     class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_policy_indicator_behavior_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_policy_indicator_behavior_tests.m.js';
   }
 };
 
@@ -341,7 +338,7 @@
 var CrElementsLottieV3Test = class extends CrElementsV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_lottie_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_lottie_tests.m.js';
   }
 
   /** @override */
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_v3_focus_test.js b/chrome/test/data/webui/cr_elements/cr_elements_v3_focus_test.js
index 3c3378b..1c1bb89 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_v3_focus_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_v3_focus_test.js
@@ -18,10 +18,7 @@
 
   /** @override */
   get extraLibraries() {
-    return [
-      '//third_party/mocha/mocha.js',
-      '//chrome/test/data/webui/mocha_adapter.js',
-    ];
+    return [];
   }
 
   /** @override */
@@ -36,6 +33,16 @@
   get browsePreload() {
     return 'chrome://test?module=cr_elements/cr_action_menu_test.m.js';
   }
+
+  /** @override */
+  get extraLibraries() {
+    return [
+      // TODO(dpapad): Figure out why this test fails if test_loader.html is
+      // used instead.
+      '//third_party/mocha/mocha.js',
+      '//chrome/test/data/webui/mocha_adapter.js',
+    ];
+  }
 };
 
 TEST_F('CrElementsActionMenuV3Test', 'All', function() {
@@ -46,7 +53,7 @@
 var CrElementsCheckboxV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_checkbox_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_checkbox_test.m.js';
   }
 };
 
@@ -58,7 +65,7 @@
 var CrElementsExpandButtonV3FocusTest = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_expand_button_focus_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_expand_button_focus_tests.m.js';
   }
 };
 
@@ -70,7 +77,7 @@
 var CrElementsIconButtonV3FocusTest = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_icon_button_focus_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_icon_button_focus_tests.m.js';
   }
 };
 
@@ -82,7 +89,7 @@
 var CrElementsInputV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_input_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_input_test.m.js';
   }
 };
 
@@ -101,7 +108,7 @@
     class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_profile_avatar_selector_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_profile_avatar_selector_tests.m.js';
   }
 };
 
@@ -113,7 +120,7 @@
 var CrElementsTabsV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_tabs_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_tabs_test.m.js';
   }
 };
 
@@ -125,7 +132,7 @@
 var CrElementsToggleV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_toggle_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toggle_test.m.js';
   }
 };
 
@@ -137,7 +144,7 @@
 var CrElementsToolbarSearchFieldV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_toolbar_search_field_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toolbar_search_field_tests.m.js';
   }
 };
 
@@ -150,7 +157,7 @@
 var IronListFocusV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/iron_list_focus_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/iron_list_focus_test.m.js';
   }
 };
 
@@ -163,7 +170,7 @@
 var CrElementsGridFocusTest = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_grid_focus_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_grid_focus_test.js';
   }
 };
 
@@ -176,7 +183,7 @@
 var CrElementsMenuSelectorFocusTest = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_menu_selector_focus_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_menu_selector_focus_test.js';
   }
 };
 
@@ -189,7 +196,7 @@
 var CrElementsToolbarFocusV3Test = class extends CrElementsV3FocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_elements/cr_toolbar_focus_tests.m.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toolbar_focus_tests.m.js';
   }
 };
 
diff --git a/chrome/test/data/webui/cr_focus_row_behavior_v3_interactive_test.js b/chrome/test/data/webui/cr_focus_row_behavior_v3_interactive_test.js
index 3d57ac22..01422f2 100644
--- a/chrome/test/data/webui/cr_focus_row_behavior_v3_interactive_test.js
+++ b/chrome/test/data/webui/cr_focus_row_behavior_v3_interactive_test.js
@@ -15,7 +15,7 @@
 var CrFocusRowBehaviorV3Test = class extends PolymerInteractiveUITest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=cr_focus_row_behavior_test.m.js';
+    return 'chrome://test/test_loader.html?module=cr_focus_row_behavior_test.m.js';
   }
 
   /** @override */
@@ -25,10 +25,7 @@
 
   /** @override */
   get extraLibraries() {
-    return [
-      '//third_party/mocha/mocha.js',
-      '//chrome/test/data/webui/mocha_adapter.js',
-    ];
+    return [];
   }
 };
 
diff --git a/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js b/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js
index f377f3d..10d493d 100644
--- a/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js
+++ b/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js
@@ -24,20 +24,12 @@
   get webuiHost() {
     return 'dummyurl';
   }
-
-  /** @override */
-  get extraLibraries() {
-    return [
-      '//third_party/mocha/mocha.js',
-      '//chrome/test/data/webui/mocha_adapter.js',
-    ];
-  }
 };
 
 var CrModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/cr_test.js';
+    return 'chrome://test/test_loader.html?module=js/cr_test.js';
   }
 };
 
@@ -56,7 +48,7 @@
 var PromiseResolverModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/promise_resolver_test.js';
+    return 'chrome://test/test_loader.html?module=js/promise_resolver_test.js';
   }
 };
 
@@ -67,7 +59,7 @@
 var ParseHtmlSubsetModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/parse_html_subset_test.js';
+    return 'chrome://test/test_loader.html?module=js/parse_html_subset_test.js';
   }
 };
 
@@ -79,7 +71,7 @@
     class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/parse_html_subset_trusted_types_test.js';
+    return 'chrome://test/test_loader.html?module=js/parse_html_subset_trusted_types_test.js';
   }
 };
 
@@ -90,7 +82,7 @@
 var UtilModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/util_test.js';
+    return 'chrome://test/test_loader.html?module=js/util_test.js';
   }
 };
 
@@ -101,7 +93,7 @@
 var LoadTimeDataModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/load_time_data_test.js';
+    return 'chrome://test/test_loader.html?module=js/load_time_data_test.js';
   }
 };
 
@@ -112,7 +104,7 @@
 var I18nBehaviorModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/i18n_behavior_test.js';
+    return 'chrome://test/test_loader.html?module=js/i18n_behavior_test.js';
   }
 };
 
@@ -123,7 +115,7 @@
 var ColorUtilsModuleTest = class extends WebUIResourceModuleAsyncTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=js/color_utils_test.js';
+    return 'chrome://test/test_loader.html?module=js/color_utils_test.js';
   }
 };
 
diff --git a/chrome/test/data/webui/resources/webui_resources_v3_browsertest.js b/chrome/test/data/webui/resources/webui_resources_v3_browsertest.js
index a85d860..e3c010a 100644
--- a/chrome/test/data/webui/resources/webui_resources_v3_browsertest.js
+++ b/chrome/test/data/webui/resources/webui_resources_v3_browsertest.js
@@ -23,10 +23,7 @@
 
   /** @override */
   get extraLibraries() {
-    return [
-      '//third_party/mocha/mocha.js',
-      '//chrome/test/data/webui/mocha_adapter.js',
-    ];
+    return [];
   }
 };
 
@@ -35,7 +32,7 @@
     class extends WebUIResourcesV3BrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=resources/list_property_update_behavior_tests.m.js';
+    return 'chrome://test/test_loader.html?module=resources/list_property_update_behavior_tests.m.js';
   }
 };
 
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index ce5ea14..d22dbd8f 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -132,6 +132,7 @@
     #":prefs_test_cases",
     #":prefs_tests",
     ":privacy_page_test",
+    ":privacy_sandbox_test",
 
     #":protocol_handlers_tests",
     ":recent_site_permissions_test",
@@ -336,6 +337,16 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
+js_library("privacy_sandbox_test") {
+  deps = [
+    ":test_open_window_proxy",
+    "..:chai_assert",
+    "//chrome/browser/resources/settings:settings",
+    "//chrome/browser/resources/settings/privacy_sandbox:app",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
 js_library("recent_site_permissions_test") {
   deps = [
     ":test_site_settings_prefs_browser_proxy",
diff --git a/chrome/test/data/webui/settings/passwords_device_section_test.js b/chrome/test/data/webui/settings/passwords_device_section_test.js
index 976a870c..e5ae5e48 100644
--- a/chrome/test/data/webui/settings/passwords_device_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_device_section_test.js
@@ -4,7 +4,6 @@
 
 /** @fileoverview Runs the Polymer tests for the PasswordsDeviceSection page. */
 
-import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {MultiStorePasswordUiEntry, PasswordManagerImpl, Router, routes, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {createMultiStorePasswordEntry, createPasswordEntry, PasswordDeviceSectionElementFactory} from 'chrome://test/settings/passwords_and_autofill_fake_data.js';
@@ -366,36 +365,4 @@
     assertTrue(firstPasswordItem.$.moreActionsButton.hidden);
   });
 
-  // Testing moving multiple password dialog footer reflects how many passwords
-  // are going to be moved.
-  test('moveMultiplePasswordsDialogFooter', async function() {
-    const deviceEntry1 = createMultiStorePasswordEntry(
-        {url: 'goo.gl', username: 'bart1', deviceId: 41});
-    const deviceEntry2 = createMultiStorePasswordEntry(
-        {url: 'goo.gl', username: 'bart2', deviceId: 54});
-    const moveMultipleDialog = elementFactory.createMoveMultiplePasswordsDialog(
-        [deviceEntry1, deviceEntry2]);
-    flush();
-
-    let expectedCountLabel =
-        await PluralStringProxyImpl.getInstance().getPluralString(
-            'movePasswordsToAccount', 2);
-    let countLabel =
-        moveMultipleDialog.$$('#movingPasswordsCountLabel').textContent.trim();
-    expectEquals(expectedCountLabel, countLabel);
-
-    // Uncheck the first entry
-    const firstPasswordItem = moveMultipleDialog.$$('password-list-item');
-    firstPasswordItem.querySelector('cr-checkbox').click();
-
-    // The footer should have been updated that only one item is selected now.
-    expectedCountLabel =
-        await PluralStringProxyImpl.getInstance().getPluralString(
-            'movePasswordsToAccount', 1);
-    countLabel =
-        moveMultipleDialog.$$('#movingPasswordsCountLabel').textContent.trim();
-    expectEquals(expectedCountLabel, countLabel);
-  });
-
-
 });
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_browsertest.js b/chrome/test/data/webui/settings/privacy_sandbox_browsertest.js
new file mode 100644
index 0000000..981da47
--- /dev/null
+++ b/chrome/test/data/webui/settings/privacy_sandbox_browsertest.js
@@ -0,0 +1,33 @@
+// 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 Tests for the privacy sandbox UI. */
+
+// Polymer BrowserTest fixture.
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
+
+GEN('#include "chrome/common/chrome_features.h"');
+GEN('#include "content/public/test/browser_test.h"');
+
+// eslint-disable-next-line no-var
+var PrivacySandboxTest = class extends PolymerTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/test_loader.html?module=settings/privacy_sandbox_test.js';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return [];
+  }
+
+  /** @override */
+  get featureList() {
+    return {enabled: ['features::kPrivacySandboxSettings']};
+  }
+};
+
+TEST_F('PrivacySandboxTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_test.js b/chrome/test/data/webui/settings/privacy_sandbox_test.js
new file mode 100644
index 0000000..6ac16ee
--- /dev/null
+++ b/chrome/test/data/webui/settings/privacy_sandbox_test.js
@@ -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.
+
+import 'chrome://settings/privacy_sandbox/app.js';
+
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {loadTimeData, OpenWindowProxyImpl} from 'chrome://settings/settings.js';
+
+import {assertEquals} from '../chai_assert.js';
+
+import {TestOpenWindowProxy} from './test_open_window_proxy.js';
+
+suite('PrivacySandbox', function() {
+  /** @type {PrivacySandboxAppElement} */
+  let page;
+
+  /** @type {?TestOpenWindowProxy} */
+  let openWindowProxy = null;
+
+  setup(async function() {
+    openWindowProxy = new TestOpenWindowProxy();
+    OpenWindowProxyImpl.instance_ = openWindowProxy;
+
+    document.body.innerHTML = '';
+    page = /** @type {!PrivacySandboxAppElement} */
+        (document.createElement('privacy-sandbox-app'));
+    document.body.appendChild(page);
+  });
+
+  test('learnMoreTest', async function() {
+    // User clicks the "Learn more" button.
+    page.$$('#learnMoreButton').click();
+    // Ensure the browser proxy call is done.
+    assertEquals(
+        loadTimeData.getString('privacySandboxURL'),
+        await openWindowProxy.whenCalled('openURL'));
+  });
+});
diff --git a/chromecast/media/cma/backend/proxy/BUILD.gn b/chromecast/media/cma/backend/proxy/BUILD.gn
index 9f60d7d..f7903e0 100644
--- a/chromecast/media/cma/backend/proxy/BUILD.gn
+++ b/chromecast/media/cma/backend/proxy/BUILD.gn
@@ -58,7 +58,8 @@
     "//third_party/protobuf:protobuf_full",
   ]
 
-  if (chromecast_branding == "public") {
+  if (!enable_chromium_runtime_cast_renderer ||
+      chromecast_branding == "public") {
     sources += [ "audio_channel_broker_simple.cc" ]
   }
 }
diff --git a/chromeos/components/camera_app_ui/camera_app_helper.mojom b/chromeos/components/camera_app_ui/camera_app_helper.mojom
index b659e2e3..4ad3525 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper.mojom
+++ b/chromeos/components/camera_app_ui/camera_app_helper.mojom
@@ -52,20 +52,20 @@
   REGULAR = 3,  // None of the above.
 };
 
-// Interface for monitoring window state.
+// Interface for monitoring window states.
 interface WindowStateMonitor {
-  // Updates when the window state is changed.
-  OnWindowStateChanged(WindowStateType state);
+  // Updates when any of the window states are changed.
+  OnWindowStateChanged(array<WindowStateType> states);
 };
 
 // Interface for window controlling.
 interface WindowStateController {
   // Adds |monitor| for window state changes.
   AddMonitor(pending_remote<WindowStateMonitor> monitor)
-      => (WindowStateType state);
+      => (array<WindowStateType> states);
 
-  // Gets current |state| of the window.
-  GetWindowState() => (WindowStateType state);
+  // Gets current |states| of the window.
+  GetWindowState() => (array<WindowStateType> states);
 
   // Minimize the window.
   Minimize() => ();
diff --git a/chromeos/components/camera_app_ui/camera_app_window_state_controller.cc b/chromeos/components/camera_app_ui/camera_app_window_state_controller.cc
index d19174a..4fe29e4 100644
--- a/chromeos/components/camera_app_ui/camera_app_window_state_controller.cc
+++ b/chromeos/components/camera_app_ui/camera_app_window_state_controller.cc
@@ -8,9 +8,27 @@
 
 namespace chromeos {
 
+namespace {
+
+bool IsRestored(views::Widget* widget) {
+  if (ash::TabletMode::Get()->InTabletMode()) {
+    return !widget->IsMinimized();
+  }
+  return !widget->IsMinimized() && !widget->IsMaximized() &&
+         !widget->IsFullscreen();
+}
+
+std::vector<CameraAppWindowStateController::WindowStateType> ToVector(
+    const base::flat_set<CameraAppWindowStateController::WindowStateType>& s) {
+  return std::vector<CameraAppWindowStateController::WindowStateType>(s.begin(),
+                                                                      s.end());
+}
+
+}  // namespace
+
 CameraAppWindowStateController::CameraAppWindowStateController(
     views::Widget* widget)
-    : widget_(widget), window_state_(GetCurrentWindowState()) {
+    : widget_(widget), window_states_(GetCurrentWindowStates()) {
   widget_->AddObserver(this);
 }
 
@@ -30,16 +48,16 @@
   auto remote = mojo::Remote<chromeos_camera::mojom::WindowStateMonitor>(
       std::move(monitor));
   monitors_.push_back(std::move(remote));
-  std::move(callback).Run(window_state_);
+  std::move(callback).Run(ToVector(window_states_));
 }
 
 void CameraAppWindowStateController::GetWindowState(
     GetWindowStateCallback callback) {
-  std::move(callback).Run(window_state_);
+  std::move(callback).Run(ToVector(window_states_));
 }
 
 void CameraAppWindowStateController::Minimize(MinimizeCallback callback) {
-  if (GetCurrentWindowState() == WindowStateType::MINIMIZED) {
+  if (widget_->IsMinimized()) {
     std::move(callback).Run();
     return;
   }
@@ -48,15 +66,7 @@
 }
 
 void CameraAppWindowStateController::Restore(RestoreCallback callback) {
-  auto current_state = GetCurrentWindowState();
-
-  // In tablet mode, it won't do anything when calling restore() for windows
-  // which are already maximized. Therefore, for maximized windows, trigger the
-  // callback immediately.
-  if (current_state == WindowStateType::REGULAR ||
-      (ash::TabletMode::Get()->InTabletMode() &&
-       (current_state == WindowStateType::MAXIMIZED ||
-        current_state == WindowStateType::FULLSCREEN))) {
+  if (IsRestored(widget_)) {
     std::move(callback).Run();
     return;
   }
@@ -65,7 +75,7 @@
 }
 
 void CameraAppWindowStateController::Maximize(MaximizeCallback callback) {
-  if (GetCurrentWindowState() == WindowStateType::MAXIMIZED) {
+  if (widget_->IsMaximized()) {
     std::move(callback).Run();
     return;
   }
@@ -74,7 +84,7 @@
 }
 
 void CameraAppWindowStateController::Fullscreen(FullscreenCallback callback) {
-  if (GetCurrentWindowState() == WindowStateType::FULLSCREEN) {
+  if (widget_->IsFullscreen()) {
     std::move(callback).Run();
     return;
   }
@@ -113,47 +123,51 @@
 }
 
 void CameraAppWindowStateController::OnWindowStateChanged() {
-  auto prev_state = window_state_;
-  window_state_ = GetCurrentWindowState();
+  auto trigger_callbacks = [](std::queue<base::OnceClosure>* callbacks) {
+    while (!callbacks->empty()) {
+      std::move(callbacks->front()).Run();
+      callbacks->pop();
+    }
+  };
 
-  std::queue<base::OnceClosure>* callbacks;
-  switch (window_state_) {
-    case WindowStateType::MINIMIZED:
-      callbacks = &minimize_callbacks_;
-      break;
-    case WindowStateType::REGULAR:
-      callbacks = &restore_callbacks_;
-      break;
-    case WindowStateType::MAXIMIZED:
-      callbacks = &maximize_callbacks_;
-      break;
-    case WindowStateType::FULLSCREEN:
-      callbacks = &fullscreen_callbacks_;
-      break;
+  if (widget_->IsMinimized()) {
+    trigger_callbacks(&minimize_callbacks_);
   }
-  while (!callbacks->empty()) {
-    std::move(callbacks->front()).Run();
-    callbacks->pop();
+  if (IsRestored(widget_)) {
+    trigger_callbacks(&restore_callbacks_);
+  }
+  if (widget_->IsMaximized()) {
+    trigger_callbacks(&maximize_callbacks_);
+  }
+  if (widget_->IsFullscreen()) {
+    trigger_callbacks(&fullscreen_callbacks_);
   }
 
-  if (prev_state != window_state_) {
+  auto prev_states = window_states_;
+  window_states_ = GetCurrentWindowStates();
+  if (prev_states != window_states_) {
     for (const auto& monitor : monitors_) {
-      monitor->OnWindowStateChanged(window_state_);
+      monitor->OnWindowStateChanged(ToVector(window_states_));
     }
   }
 }
 
-CameraAppWindowStateController::WindowStateType
-CameraAppWindowStateController::GetCurrentWindowState() {
+base::flat_set<CameraAppWindowStateController::WindowStateType>
+CameraAppWindowStateController::GetCurrentWindowStates() {
+  base::flat_set<CameraAppWindowStateController::WindowStateType> states;
   if (widget_->IsMinimized()) {
-    return WindowStateType::MINIMIZED;
-  } else if (widget_->IsMaximized()) {
-    return WindowStateType::MAXIMIZED;
-  } else if (widget_->IsFullscreen()) {
-    return WindowStateType::FULLSCREEN;
-  } else {
-    return WindowStateType::REGULAR;
+    states.insert(WindowStateType::MINIMIZED);
   }
+  if (widget_->IsMaximized()) {
+    states.insert(WindowStateType::MAXIMIZED);
+  }
+  if (widget_->IsFullscreen()) {
+    states.insert(WindowStateType::FULLSCREEN);
+  }
+  if (IsRestored(widget_)) {
+    states.insert(WindowStateType::REGULAR);
+  }
+  return states;
 }
 
 }  // namespace chromeos
diff --git a/chromeos/components/camera_app_ui/camera_app_window_state_controller.h b/chromeos/components/camera_app_ui/camera_app_window_state_controller.h
index 82c90eb..5d1d7da 100644
--- a/chromeos/components/camera_app_ui/camera_app_window_state_controller.h
+++ b/chromeos/components/camera_app_ui/camera_app_window_state_controller.h
@@ -8,6 +8,7 @@
 #include <queue>
 #include <vector>
 
+#include "base/containers/flat_set.h"
 #include "chromeos/components/camera_app_ui/camera_app_helper.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -55,10 +56,10 @@
 
  private:
   void OnWindowStateChanged();
-  WindowStateType GetCurrentWindowState();
+  base::flat_set<WindowStateType> GetCurrentWindowStates();
 
   views::Widget* widget_;
-  WindowStateType window_state_;
+  base::flat_set<WindowStateType> window_states_;
   mojo::ReceiverSet<chromeos_camera::mojom::WindowStateController> receivers_;
   std::vector<mojo::Remote<WindowStateMonitor>> monitors_;
   std::queue<base::OnceClosure> minimize_callbacks_;
diff --git a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
index 8acb824..7e74c5a 100644
--- a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
+++ b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
@@ -163,6 +163,7 @@
       <include name="IDR_CAMERA_BARCODE_CHEVRON_DOWN_SVG" file="images/barcode_chevron_down.svg" type="BINDATA" />
       <include name="IDR_CAMERA_BARCODE_CHEVRON_UP_SVG" file="images/barcode_chevron_up.svg" type="BINDATA" />
       <include name="IDR_CAMERA_BARCODE_COPY_SVG" file="images/barcode_copy.svg" type="BINDATA" />
+      <include name="IDR_CAMERA_BARCODE_SCAN_BOX_BORDER_MASK_SVG" file="images/barcode_scan_box_border_mask.svg" type="BINDATA" />
       <include name="IDR_CAMERA_BARCODE_TOGGLE_OFF_SVG" file="images/barcode_toggle_off.svg" type="BINDATA" />
       <include name="IDR_CAMERA_BARCODE_TOGGLE_ON_SVG" file="images/barcode_toggle_on.svg" type="BINDATA" />
       <include name="IDR_CAMERA_BARCODE_URL_SVG" file="images/barcode_url.svg" type="BINDATA" />
diff --git a/chromeos/components/camera_app_ui/resources/css/main.css b/chromeos/components/camera_app_ui/resources/css/main.css
index eeda5ca3..924262b 100644
--- a/chromeos/components/camera_app_ui/resources/css/main.css
+++ b/chromeos/components/camera_app_ui/resources/css/main.css
@@ -206,6 +206,7 @@
     rgba(0, 0, 0, 0) 0,
     rgba(0, 0, 0, 1) var(--fade-padding) calc(100% - var(--fade-padding)),
     rgba(0, 0, 0, 0) 100%);
+  display: block;
   left: calc(var(--left-line) * 2);
   overflow: auto;
   padding: 5px 0;
@@ -1508,6 +1509,52 @@
   visibility: visible;
 }
 
+/* The container of scan box for layout and positioning. */
+.barcode-scan-box {
+  --border-distance: 8px;
+  --border-width: 4px;
+  --inner-border-radius: 16px;
+  /* Use padding-top instead of height to make a responsive square, since the
+   * percentage in padding-top is relative to the width of the containing
+   * block. */
+  padding-top: calc(100% / 3);
+  visibility: hidden;
+  width: calc(100% / 3);
+  z-index: 30;
+}
+
+body.photo.scan-barcode .barcode-scan-box {
+  visibility: visible;
+}
+
+/* The inner scan box with a translucent overlay. */
+.barcode-scan-box::before {
+  --offset: calc(var(--border-width) + var(--border-distance));
+  border-radius: var(--inner-border-radius);
+  bottom: var(--offset);
+  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.6);
+  content: '';
+  left: var(--offset);
+  position: absolute;
+  right: var(--offset);
+  top: var(--offset);
+  z-index: -1;  /* This need to be lower than the corner borders */
+}
+
+/* The corner borders of the scan box. */
+.barcode-scan-box::after {
+  -webkit-mask: url(/images/barcode_scan_box_border_mask.svg);
+  border: var(--border-width) solid white;
+  border-radius: calc(var(--inner-border-radius) + var(--border-distance));
+  box-sizing: border-box;
+  content: '';
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+
 /* TODO(b/172879638): Tune the position and layout after we finalized the
  * responsive window design. */
 .barcode-chip-container {
@@ -1651,6 +1698,7 @@
   position: absolute;
   visibility: hidden;
   width: 256px;
+  z-index: 50;
 }
 
 .snackbar.animate {
diff --git a/chromeos/components/camera_app_ui/resources/images/barcode_scan_box_border_mask.svg b/chromeos/components/camera_app_ui/resources/images/barcode_scan_box_border_mask.svg
new file mode 100644
index 0000000..7b4a56bc9
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/images/barcode_scan_box_border_mask.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
+  <rect x="0" y="0" width="25" height="25" fill="#000000"/>
+  <rect x="75" y="0" width="25" height="25" fill="#000000"/>
+  <rect x="0" y="75" width="25" height="25" fill="#000000"/>
+  <rect x="75" y="75" width="25" height="25" fill="#000000"/>
+</svg>
diff --git a/chromeos/components/camera_app_ui/resources/images/images.gni b/chromeos/components/camera_app_ui/resources/images/images.gni
index 4b03b27..d0f394d 100644
--- a/chromeos/components/camera_app_ui/resources/images/images.gni
+++ b/chromeos/components/camera_app_ui/resources/images/images.gni
@@ -6,6 +6,7 @@
   "barcode_chevron_down.svg",
   "barcode_chevron_up.svg",
   "barcode_copy.svg",
+  "barcode_scan_box_border_mask.svg",
   "barcode_toggle_off.svg",
   "barcode_toggle_on.svg",
   "barcode_url.svg",
diff --git a/chromeos/components/camera_app_ui/resources/js/window_controller/mojo_window_controller.js b/chromeos/components/camera_app_ui/resources/js/window_controller/mojo_window_controller.js
index 94d728d..6b5656ea 100644
--- a/chromeos/components/camera_app_ui/resources/js/window_controller/mojo_window_controller.js
+++ b/chromeos/components/camera_app_ui/resources/js/window_controller/mojo_window_controller.js
@@ -23,10 +23,10 @@
     this.windowStateController_ = null;
 
     /**
-     * Current window state.
-     * @type {?chromeosCamera.mojom.WindowStateType}
+     * Current window states.
+     * @type {!Array<!chromeosCamera.mojom.WindowStateType>}
      */
-    this.windowState_ = null;
+    this.windowStates_ = [];
   }
 
   /** @override */
@@ -35,12 +35,12 @@
 
     const windowMonitorCallbackRouter =
         new chromeosCamera.mojom.WindowStateMonitorCallbackRouter();
-    windowMonitorCallbackRouter.onWindowStateChanged.addListener((state) => {
-      this.windowState_ = state;
+    windowMonitorCallbackRouter.onWindowStateChanged.addListener((states) => {
+      this.windowStates_ = states;
     });
-    const {state} = await this.windowStateController_.addMonitor(
+    const {states} = await this.windowStateController_.addMonitor(
         windowMonitorCallbackRouter.$.bindNewPipeAndPassRemote());
-    this.windowState_ = state;
+    this.windowStates_ = states;
   }
 
   /** @override */
@@ -85,14 +85,16 @@
 
   /** @override */
   isMinimized() {
-    return this.windowState_ === chromeosCamera.mojom.WindowStateType.MINIMIZED;
+    return this.windowStates_.includes(
+        chromeosCamera.mojom.WindowStateType.MINIMIZED);
   }
 
   /** @override */
   isFullscreenOrMaximized() {
-    return this.windowState_ ===
-        chromeosCamera.mojom.WindowStateType.FULLSCREEN ||
-        this.windowState_ === chromeosCamera.mojom.WindowStateType.MAXIMIZED;
+    return this.windowStates_.includes(
+               chromeosCamera.mojom.WindowStateType.FULLSCREEN) ||
+        this.windowStates_.includes(
+            chromeosCamera.mojom.WindowStateType.MAXIMIZED);
   }
 
   /** @override */
diff --git a/chromeos/components/camera_app_ui/resources/views/main.html b/chromeos/components/camera_app_ui/resources/views/main.html
index b48e657..5ad81bb 100644
--- a/chromeos/components/camera_app_ui/resources/views/main.html
+++ b/chromeos/components/camera_app_ui/resources/views/main.html
@@ -75,6 +75,7 @@
                     data="/images/camera_focus_aim.svg"
                     tabindex="-1" hidden></object>
           </div>
+          <div class="barcode-scan-box centered-overlay"></div>
           <div class="snackbar"></div>
           <div id="camera-mode" class="centered-overlay"></div>
         </div>
diff --git a/components/autofill/core/browser/autofill_regex_constants.cc b/components/autofill/core/browser/autofill_regex_constants.cc
index fe3fb99f..342d20f 100644
--- a/components/autofill/core/browser/autofill_regex_constants.cc
+++ b/components/autofill/core/browser/autofill_regex_constants.cc
@@ -18,7 +18,10 @@
     "province|region|other"
     "|provincia"       // es
     "|bairro|suburb";  // pt-BR, pt-PT
-const char kAddressNameIgnoredRe[] = "address.*nickname|address.*label";
+const char kAddressNameIgnoredRe[] =
+    "address.*nickname|address.*label"
+    "|adres ([İi]sim|başlığı|adı)"  // tr
+    "|identificação do endereço";   // pt-BR, pt-PT
 const char kCompanyRe[] =
     "company|business|organization|organisation"
     "|(?<!con)firma|firmenname"  // de-DE
@@ -52,29 +55,29 @@
 const char kAddressLine1Re[] =
     "^address$|address[_-]?line(one)?|address1|addr1|street"
     "|(?:shipping|billing)address$"
-    "|strasse|straße|hausnummer|housenumber"           // de-DE
-    "|house.?name"                                     // en-GB
-    "|direccion|dirección"                             // es
-    "|adresse"                                         // fr-FR
-    "|indirizzo"                                       // it-IT
-    "|^住所$|住所1"                                    // ja-JP
-    "|morada|((?<!do |de )endereço)"                   // pt-BR, pt-PT
-    "|Адрес"                                           // ru
-    "|地址"                                            // zh-CN
-    "|(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)"  // tr
-    "|^주소.?$|주소.?1";                               // ko-KR
+    "|strasse|straße|hausnummer|housenumber"  // de-DE
+    "|house.?name"                            // en-GB
+    "|direccion|dirección"                    // es
+    "|adresse"                                // fr-FR
+    "|indirizzo"                              // it-IT
+    "|^住所$|住所1"                           // ja-JP
+    "|morada|((?<!do |de )endereço)"          // pt-BR, pt-PT
+    "|Адрес"                                  // ru
+    "|地址"                                   // zh-CN
+    "|(\\b|_)adres(?! tarifi)(\\b|_)"         // tr
+    "|^주소.?$|주소.?1";                      // ko-KR
 const char kAddressLine1LabelRe[] =
     "(^\\W*address)"
     "|(address\\W*$)"
     "|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|"
     "recipient|home|work|office|school|business|mail)[\\s\\-]+address"
     "|address\\s+(of|for|to|from)"
-    "|adresse"                                         // fr-FR
-    "|indirizzo"                                       // it-IT
-    "|住所"                                            // ja-JP
-    "|地址"                                            // zh-CN
-    "|(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)"  // tr
-    "|주소";                                           // ko-KR
+    "|adresse"                         // fr-FR
+    "|indirizzo"                       // it-IT
+    "|住所"                            // ja-JP
+    "|地址"                            // zh-CN
+    "|(\\b|_)adres(?! tarifi)(\\b|_)"  // tr
+    "|주소";                           // ko-KR
 const char kAddressLine2Re[] =
     "address[_-]?line(2|two)|address2|addr2|street|suite|unit"
     "|adresszusatz|ergänzende.?angaben"        // de-DE
@@ -307,7 +310,6 @@
 /////////////////////////////////////////////////////////////////////////////
 const char kNameIgnoredRe[] =
     "user.?name|user.?id|nickname|maiden name|title|prefix|suffix"
-    "|adres başlığınız"                 // tr
     "|vollständiger.?name"              // de-DE
     "|用户名"                           // zh-CN
     "|(?:사용자.?)?아이디|사용자.?ID";  // ko-KR
diff --git a/components/autofill/core/browser/form_parsing/address_field_unittest.cc b/components/autofill/core/browser/form_parsing/address_field_unittest.cc
index 07fada1..c4cc35f 100644
--- a/components/autofill/core/browser/form_parsing/address_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/address_field_unittest.cc
@@ -282,4 +282,10 @@
   ClassifyAndVerify(/*parsed=*/true, LanguageCode("tr"));
 }
 
+// Tests that address name is not misclassified as address.
+TEST_F(AddressFieldTest, NotParseAddressName) {
+  AddTextFormFieldData("address", "Adres Başlığı", UNKNOWN_TYPE);
+  ClassifyAndVerify(/*parsed=*/false, LanguageCode("tr"));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/name_field.cc b/components/autofill/core/browser/form_parsing/name_field.cc
index f0c76f94..32ef678 100644
--- a/components/autofill/core/browser/form_parsing/name_field.cc
+++ b/components/autofill/core/browser/form_parsing/name_field.cc
@@ -133,9 +133,15 @@
   const std::vector<MatchingPattern>& name_ignored_patterns =
       PatternProvider::GetInstance().GetMatchPatterns("NAME_IGNORED",
                                                       page_language);
+  const std::vector<MatchingPattern>& address_name_ignored_patterns =
+      PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED",
+                                                      page_language);
   bool should_ignore =
       ParseField(scanner, UTF8ToUTF16(kNameIgnoredRe), name_ignored_patterns,
-                 nullptr, {log_manager, "kNameIgnoredRe"});
+                 nullptr, {log_manager, "kNameIgnoredRe"}) ||
+      ParseField(scanner, UTF8ToUTF16(kAddressNameIgnoredRe),
+                 address_name_ignored_patterns, nullptr,
+                 {log_manager, "kAddressNameIgnoredRe"});
   scanner->Rewind();
   if (should_ignore)
     return nullptr;
@@ -186,6 +192,9 @@
   const std::vector<MatchingPattern>& name_ignored_patterns =
       PatternProvider::GetInstance().GetMatchPatterns("NAME_IGNORED",
                                                       page_language);
+  const std::vector<MatchingPattern>& address_name_ignored_patterns =
+      PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED",
+                                                      page_language);
   const std::vector<MatchingPattern>& first_name_patterns =
       PatternProvider::GetInstance().GetMatchPatterns("FIRST_NAME",
                                                       page_language);
@@ -217,7 +226,10 @@
     if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kNameIgnoredRe),
                             MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH,
                             name_ignored_patterns, nullptr,
-                            {log_manager, "kNameIgnoredRe"})) {
+                            {log_manager, "kNameIgnoredRe"}) ||
+        ParseFieldSpecifics(scanner, UTF8ToUTF16(kAddressNameIgnoredRe),
+                            MATCH_DEFAULT, address_name_ignored_patterns,
+                            nullptr, {log_manager, "kAddressNameIgnoredRe"})) {
       continue;
     }
 
@@ -333,6 +345,9 @@
   const std::vector<MatchingPattern>& name_ignored_patterns =
       PatternProvider::GetInstance().GetMatchPatterns("NAME_IGNORED",
                                                       page_language);
+  const std::vector<MatchingPattern>& address_name_ignored_patterns =
+      PatternProvider::GetInstance().GetMatchPatterns("ADDRESS_NAME_IGNORED",
+                                                      page_language);
   const std::vector<MatchingPattern>& first_name_patterns =
       PatternProvider::GetInstance().GetMatchPatterns("FIRST_NAME",
                                                       page_language);
@@ -366,7 +381,10 @@
     if (ParseFieldSpecifics(scanner, UTF8ToUTF16(kNameIgnoredRe),
                             MATCH_DEFAULT | MATCH_SELECT | MATCH_SEARCH,
                             name_ignored_patterns, nullptr,
-                            {log_manager, "kNameIgnoredRe"})) {
+                            {log_manager, "kNameIgnoredRe"}) ||
+        ParseFieldSpecifics(scanner, UTF8ToUTF16(kAddressNameIgnoredRe),
+                            MATCH_DEFAULT, address_name_ignored_patterns,
+                            nullptr, {log_manager, "kAddressNameIgnoredRe"})) {
       continue;
     }
 
diff --git a/components/autofill/core/browser/form_parsing/name_field_unittest.cc b/components/autofill/core/browser/form_parsing/name_field_unittest.cc
index eebe67a..3559c65 100644
--- a/components/autofill/core/browser/form_parsing/name_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/name_field_unittest.cc
@@ -547,4 +547,19 @@
   }
 }
 
+// Tests that address name is not misclassified as name.
+TEST_F(NameFieldTest, NotAddressName) {
+  FormFieldData field;
+  field.form_control_type = "text";
+
+  field.label = base::UTF8ToUTF16("Identificação do Endereço");
+  field.name = base::UTF8ToUTF16("name");
+  field.unique_renderer_id = MakeFieldRendererId();
+  list_.push_back(std::make_unique<AutofillField>(field));
+
+  AutofillScanner scanner(list_);
+  field_ = Parse(&scanner);
+  ASSERT_EQ(nullptr, field_.get());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
index 848cb2e..eee8f473 100644
--- a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
+++ b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
@@ -219,6 +219,26 @@
         "match_field_attributes": 3,
         "match_field_input_types": 1
       }
+    ],
+    "tr": [
+      {
+        "pattern_identifier": "tr_address_name_ignored_preserving",
+        "positive_pattern": "adres ([İi]sim|başlığı|adı)",
+        "positive_score": 1.1,
+        "negative_pattern": null,
+        "match_field_attributes": 3,
+        "match_field_input_types": 1
+      }
+    ],
+    "pt": [
+      {
+        "pattern_identifier": "pt_address_name_ignored_preserving",
+        "positive_pattern": "identificação do endereço",
+        "positive_score": 1.1,
+        "negative_pattern": null,
+        "match_field_attributes": 3,
+        "match_field_input_types": 1
+      }
     ]
   },
   "COMPANY": {
@@ -449,7 +469,7 @@
     "tr": [
       {
         "pattern_identifier": "tr_address_line_1_preserving",
-        "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)",
+        "positive_pattern": "(\\b|_)adres(?! tarifi)(\\b|_)",
         "positive_score": 1.1,
         "negative_pattern": null,
         "match_field_attributes": 3,
@@ -457,7 +477,7 @@
       },
       {
         "pattern_identifier": "tr_address_line_1_label_preserving",
-        "positive_pattern": "(\\b|_)adres(?! (başlığı(nız)?|tarifi))(\\b|_)",
+        "positive_pattern": "(\\b|_)adres(?! tarifi)(\\b|_)",
         "positive_score": 1.1,
         "negative_pattern": null,
         "match_field_attributes": 1,
@@ -2028,16 +2048,6 @@
         "positive_score": 0.9,
         "negative_pattern": null,
         "match_field_attributes": 3,
-        "match_field_input_types": 137 
-      }
-    ],
-    "tr": [
-      {
-        "pattern_identifier": "tr_name_ignored_preserving",
-        "positive_pattern": "adres başlığınız",
-        "positive_score": 0.9,
-        "negative_pattern": null,
-        "match_field_attributes": 3,
         "match_field_input_types": 137
       }
     ],
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index ada7439..84511c13 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -925,7 +925,9 @@
       observer.OnClientSettingsChanged(settings_);
     }
   }
-
+  if (response_proto.has_script_store_config()) {
+    GetService()->SetScriptStoreConfig(response_proto.script_store_config());
+  }
   std::vector<std::unique_ptr<Script>> scripts;
   for (const auto& script_proto : response_proto.scripts()) {
     ProtocolUtils::AddScript(script_proto, &scripts);
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 4c0b419..96405022 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -1167,6 +1167,26 @@
   EXPECT_EQ(ACTION_APPLIED, processed_actions_capture[1].status());
 }
 
+TEST_F(ControllerTest, SetScriptStoreConfig) {
+  // A single script, and its corresponding bundle info.
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "script");
+  script_response.mutable_script_store_config()->set_bundle_path("bundle/path");
+  script_response.mutable_script_store_config()->set_bundle_version(12);
+  SetupScripts(script_response);
+
+  ScriptStoreConfig script_store_config;
+  std::vector<ProcessedActionProto> processed_actions_capture;
+  EXPECT_CALL(*mock_service_, SetScriptStoreConfig(_))
+      .WillOnce(SaveArg<0>(&script_store_config));
+
+  Start("http://a.example.com/path");
+  controller_->GetUserActions();
+
+  EXPECT_THAT(script_store_config.bundle_path(), Eq("bundle/path"));
+  EXPECT_THAT(script_store_config.bundle_version(), Eq(12));
+}
+
 TEST_F(ControllerTest, InitialDataUrlDoesNotChange) {
   const std::string deeplink_url("http://initialurl.com/path");
   Start(deeplink_url);
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 40702397..5ace64c 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -123,10 +123,15 @@
     const std::string& global_payload,
     const std::string& script_payload,
     const ClientContextProto& client_context,
-    const std::map<std::string, std::string>& script_parameters) {
+    const std::map<std::string, std::string>& script_parameters,
+    const base::Optional<ScriptStoreConfig>& script_store_config) {
   ScriptActionRequestProto request_proto;
   InitialScriptActionsRequestProto* initial_request_proto =
       request_proto.mutable_initial_request();
+  if (script_store_config.has_value()) {
+    *initial_request_proto->mutable_script_store_config() =
+        *script_store_config;
+  }
   InitialScriptActionsRequestProto::QueryProto* query =
       initial_request_proto->mutable_query();
   query->add_script_path(script_path);
diff --git a/components/autofill_assistant/browser/protocol_utils.h b/components/autofill_assistant/browser/protocol_utils.h
index 7642f1f..231ae8c 100644
--- a/components/autofill_assistant/browser/protocol_utils.h
+++ b/components/autofill_assistant/browser/protocol_utils.h
@@ -45,7 +45,8 @@
       const std::string& global_payload,
       const std::string& script_payload,
       const ClientContextProto& client_context,
-      const std::map<std::string, std::string>& script_parameters);
+      const std::map<std::string, std::string>& script_parameters,
+      const base::Optional<ScriptStoreConfig>& script_store_config);
 
   // Create request to get next sequence of actions for a script.
   static std::string CreateNextScriptActionsRequest(
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index f6aecd2..c736407 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -126,10 +126,14 @@
   std::map<std::string, std::string> parameters = {{"key_a", "value_a"},
                                                    {"key_b", "value_b"}};
   ScriptActionRequestProto request;
+  ScriptStoreConfig config;
+  config.set_bundle_path("bundle/path");
+  config.set_bundle_version(12);
   EXPECT_TRUE(
       request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
           "script_path", GURL("http://example.com/"), "global_payload",
-          "script_payload", client_context_proto_, parameters)));
+          "script_payload", client_context_proto_, parameters,
+          base::Optional<ScriptStoreConfig>(config))));
 
   const InitialScriptActionsRequestProto& initial = request.initial_request();
   EXPECT_THAT(initial.query().script_path(), ElementsAre("script_path"));
@@ -139,6 +143,8 @@
   AssertClientContext(request.client_context());
   EXPECT_EQ("global_payload", request.global_payload());
   EXPECT_EQ("script_payload", request.script_payload());
+  EXPECT_EQ("bundle/path", initial.script_store_config().bundle_path());
+  EXPECT_EQ(12, initial.script_store_config().bundle_version());
 }
 
 TEST_F(ProtocolUtilsTest, CreateNextScriptActionsRequest) {
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index c77ec61cd..ef836189 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -123,6 +123,11 @@
   optional ScriptTimeoutError script_timeout_error = 2;
 
   optional ClientSettingsProto client_settings = 3;
+
+  // The ScriptStoreConfig to use with the initial GetActions request.
+  optional ScriptStoreConfig script_store_config = 6;
+
+  reserved 4, 5;
 }
 
 // Overlay image to be drawn on top of full overlays.
@@ -375,6 +380,13 @@
   }
 }
 
+// Message specifying how to retrieve the scripts. If bundle_path is set, it
+// will read that bundle with the associated version.
+message ScriptStoreConfig {
+  optional string bundle_path = 2;
+  optional int64 bundle_version = 3;
+}
+
 // Initial request to get a script's actions.
 message InitialScriptActionsRequestProto {
   message QueryProto {
@@ -388,6 +400,9 @@
   optional QueryProto query = 3;
 
   repeated ScriptParameterProto script_parameters = 2;
+
+  // Specify a bundle and version to run.
+  optional ScriptStoreConfig script_store_config = 6;
 }
 
 message RoundtripTimingStats {
diff --git a/components/autofill_assistant/browser/service/lite_service.cc b/components/autofill_assistant/browser/service/lite_service.cc
index f072214..6238c94 100644
--- a/components/autofill_assistant/browser/service/lite_service.cc
+++ b/components/autofill_assistant/browser/service/lite_service.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "components/autofill_assistant/browser/service/lite_service.h"
-#include "components/autofill_assistant/browser/service/lite_service_util.h"
 
 #include <algorithm>
 #include <string>
@@ -13,6 +12,7 @@
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/autofill_assistant/browser/protocol_utils.h"
+#include "components/autofill_assistant/browser/service/lite_service_util.h"
 #include "net/http/http_status_code.h"
 
 namespace autofill_assistant {
@@ -78,7 +78,7 @@
       ProtocolUtils::CreateInitialScriptActionsRequest(
           trigger_script_path_, GURL(), /* global_payload = */ std::string(),
           /* script_payload = */ std::string(), EmptyClientContext().AsProto(),
-          /* script_parameters = */ {}),
+          /* script_parameters = */ {}, /* bundle_info */ base::nullopt),
       base::BindOnce(&LiteService::OnGetActions, weak_ptr_factory_.GetWeakPtr(),
                      std::move(callback)));
 }
diff --git a/components/autofill_assistant/browser/service/mock_service.h b/components/autofill_assistant/browser/service/mock_service.h
index 05d1a388..42be97ce 100644
--- a/components/autofill_assistant/browser/service/mock_service.h
+++ b/components/autofill_assistant/browser/service/mock_service.h
@@ -70,6 +70,9 @@
                     ResponseCallback& callback));
 
   MOCK_CONST_METHOD0(IsLiteService, bool());
+
+  MOCK_METHOD1(SetScriptStoreConfig,
+               void(const ScriptStoreConfig& script_store_config));
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service.h b/components/autofill_assistant/browser/service/service.h
index ca413cf..6b1f577 100644
--- a/components/autofill_assistant/browser/service/service.h
+++ b/components/autofill_assistant/browser/service/service.h
@@ -51,6 +51,9 @@
       const RoundtripTimingStats& timing_stats,
       ResponseCallback callback) = 0;
 
+  virtual void SetScriptStoreConfig(
+      const ScriptStoreConfig& script_store_config) {}
+
  protected:
   Service() = default;
 };
diff --git a/components/autofill_assistant/browser/service/service_impl.cc b/components/autofill_assistant/browser/service/service_impl.cc
index 1aa89ea..93069455 100644
--- a/components/autofill_assistant/browser/service/service_impl.cc
+++ b/components/autofill_assistant/browser/service/service_impl.cc
@@ -61,6 +61,11 @@
 
 ServiceImpl::~ServiceImpl() {}
 
+void ServiceImpl::SetScriptStoreConfig(
+    const ScriptStoreConfig& script_store_config) {
+  script_store_config_ = script_store_config;
+}
+
 void ServiceImpl::GetScriptsForUrl(const GURL& url,
                                    const TriggerContext& trigger_context,
                                    ResponseCallback callback) {
@@ -89,7 +94,8 @@
       script_action_server_url_,
       ProtocolUtils::CreateInitialScriptActionsRequest(
           script_path, url, global_payload, script_payload,
-          client_context_->AsProto(), trigger_context.GetParameters()),
+          client_context_->AsProto(), trigger_context.GetParameters(),
+          script_store_config_),
       std::move(callback));
 }
 
diff --git a/components/autofill_assistant/browser/service/service_impl.h b/components/autofill_assistant/browser/service/service_impl.h
index 64b53f2..a690b270 100644
--- a/components/autofill_assistant/browser/service/service_impl.h
+++ b/components/autofill_assistant/browser/service/service_impl.h
@@ -75,6 +75,9 @@
       const RoundtripTimingStats& timing_stats,
       ResponseCallback callback) override;
 
+  void SetScriptStoreConfig(
+      const ScriptStoreConfig& script_store_config) override;
+
  private:
   // The request sender responsible for communicating with a remote endpoint.
   std::unique_ptr<ServiceRequestSender> request_sender_;
@@ -86,6 +89,10 @@
   // The client context to send to the backend.
   std::unique_ptr<ClientContext> client_context_;
 
+  // The script store config used for GetActions request. This is set by the
+  // controller, obtained from the GetScriptsForUrl's response.
+  base::Optional<ScriptStoreConfig> script_store_config_;
+
   base::WeakPtrFactory<ServiceImpl> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill_assistant/browser/service/service_impl_unittest.cc b/components/autofill_assistant/browser/service/service_impl_unittest.cc
index c95f4637..fa3a81d 100644
--- a/components/autofill_assistant/browser/service/service_impl_unittest.cc
+++ b/components/autofill_assistant/browser/service/service_impl_unittest.cc
@@ -4,12 +4,11 @@
 
 #include "components/autofill_assistant/browser/service/service_impl.h"
 
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
 #include "components/autofill_assistant/browser/mock_client_context.h"
 #include "components/autofill_assistant/browser/service/mock_service_request_sender.h"
 #include "components/autofill_assistant/browser/service/service.h"
-
-#include "base/test/gmock_callback_support.h"
-#include "base/test/mock_callback.h"
 #include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -17,8 +16,10 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::DoAll;
 using ::testing::NiceMock;
 using ::testing::Return;
+using ::testing::SaveArg;
 
 namespace {
 
@@ -80,6 +81,41 @@
       std::string("fake_script_payload"), mock_response_callback_.Get());
 }
 
+TEST_F(ServiceImplTest, GetActionsForwardsScriptStoreConfig) {
+  EXPECT_CALL(*mock_client_context_, Update).Times(1);
+
+  ScriptActionRequestProto expected_get_actions;
+  ScriptStoreConfig* config = expected_get_actions.mutable_initial_request()
+                                  ->mutable_script_store_config();
+  config->set_bundle_path("bundle/path");
+  config->set_bundle_version(12);
+
+  std::string get_actions_request;
+  EXPECT_CALL(*mock_request_sender_,
+              OnSendRequest(GURL(kActionServerUrl), _, _))
+      .WillOnce(SaveArg<1>(&get_actions_request));
+
+  ScriptStoreConfig set_config;
+  set_config.set_bundle_path("bundle/path");
+  set_config.set_bundle_version(12);
+  service_->SetScriptStoreConfig(set_config);
+
+  TriggerContextImpl trigger_context;
+  service_->GetActions(
+      std::string("fake_script_path"), GURL("https://www.example.com"),
+      trigger_context, std::string("fake_global_payload"),
+      std::string("fake_script_payload"), mock_response_callback_.Get());
+
+  ScriptActionRequestProto get_actions_request_proto;
+  EXPECT_TRUE(get_actions_request_proto.ParseFromString(get_actions_request));
+  EXPECT_EQ("bundle/path", get_actions_request_proto.initial_request()
+                               .script_store_config()
+                               .bundle_path());
+  EXPECT_EQ(12, get_actions_request_proto.initial_request()
+                    .script_store_config()
+                    .bundle_version());
+}
+
 TEST_F(ServiceImplTest, GetNextActions) {
   EXPECT_CALL(*mock_client_context_, Update).Times(1);
   EXPECT_CALL(*mock_request_sender_,
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
index 9f29583a..21ee879 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
@@ -75,12 +75,6 @@
     const GURL& deeplink_url,
     std::unique_ptr<TriggerContext> trigger_context) {
   deeplink_url_ = deeplink_url;
-  if (!url_utils::IsInDomainOrSubDomain(GetCurrentURL(), deeplink_url_)) {
-    LOG(ERROR) << "Trigger script requested for domain other than the deeplink";
-    Stop(Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_FAILED_NAVIGATE);
-    return;
-  }
-
   trigger_context_ = std::make_unique<TriggerContextImpl>(
       ExtractDebugScriptParameters(*trigger_context),
       trigger_context->experiment_ids());
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
index 8552f31f..67f8d39 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
@@ -42,7 +42,6 @@
 };
 
 const char kFakeDeepLink[] = "https://example.com/q?data=test";
-const char kFakeSubdomainDeepLink[] = "https://b.example.com";
 const char kFakeServerUrl[] =
     "https://www.fake.backend.com/trigger_script_server";
 
@@ -143,39 +142,6 @@
   NiceMock<MockDynamicTriggerConditions>* mock_dynamic_trigger_conditions_;
 };
 
-TEST_F(TriggerScriptCoordinatorTest, StartSucceedsForCorrectDomain) {
-  SimulateNavigateToUrl(GURL(kFakeDeepLink));
-  EXPECT_CALL(*mock_request_sender_, OnSendRequest).Times(1);
-  coordinator_->Start(GURL(kFakeDeepLink),
-                      std::make_unique<TriggerContextImpl>());
-}
-
-TEST_F(TriggerScriptCoordinatorTest, StartSucceedsWhileNoCommittedURL) {
-  content::WebContentsTester::For(web_contents())->SetLastCommittedURL(GURL());
-  EXPECT_CALL(*mock_request_sender_, OnSendRequest).Times(1);
-  coordinator_->Start(GURL(kFakeDeepLink),
-                      std::make_unique<TriggerContextImpl>());
-}
-
-TEST_F(TriggerScriptCoordinatorTest, StartSucceedsForSubDomain) {
-  SimulateNavigateToUrl(GURL(kFakeSubdomainDeepLink));
-  EXPECT_CALL(*mock_request_sender_, OnSendRequest).Times(1);
-  coordinator_->Start(GURL(kFakeDeepLink),
-                      std::make_unique<TriggerContextImpl>());
-}
-
-TEST_F(TriggerScriptCoordinatorTest, StartFailsForDifferentDomain) {
-  SimulateNavigateToUrl(GURL("https://different.com/example"));
-  EXPECT_CALL(*mock_request_sender_, OnSendRequest).Times(0);
-  EXPECT_CALL(mock_observer_,
-              OnTriggerScriptFinished(Metrics::LiteScriptFinishedState::
-                                          LITE_SCRIPT_PROMPT_FAILED_NAVIGATE));
-  coordinator_->Start(GURL(kFakeDeepLink),
-                      std::make_unique<TriggerContextImpl>());
-  AssertRecordedFinishedState(
-      Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_FAILED_NAVIGATE);
-}
-
 TEST_F(TriggerScriptCoordinatorTest, StartSendsOnlyApprovedFields) {
   std::map<std::string, std::string> input_script_params{
       {"keyA", "valueA"},
@@ -568,18 +534,21 @@
               OnUpdate(mock_web_controller_, _))
       .Times(0);
   SimulateWebContentsVisibilityChanged(content::Visibility::HIDDEN);
+  // Note: in reality, it should be impossible to navigate on hidden tabs.
   SimulateNavigateToUrl(GURL("https://example.different.com"));
+  SimulateNavigateToUrl(GURL("https://also-not-supported.com"));
 
-  // However, when the tab becomes visible again, resuming the trigger script
-  // will fail if the last committed URL is still on a non-supported domain.
-  EXPECT_CALL(
-      mock_observer_,
-      OnTriggerScriptFinished(
-          Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_FAILED_NAVIGATE))
+  EXPECT_CALL(*mock_request_sender_, OnSendRequest(GURL(kFakeServerUrl), _, _))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, /* response = */ ""));
+  // However, when the tab becomes visible again, the trigger script is
+  // restarted and thus fails if the tab is still on an unsupported domain.
+  EXPECT_CALL(mock_observer_, OnTriggerScriptFinished(
+                                  Metrics::LiteScriptFinishedState::
+                                      LITE_SCRIPT_NO_TRIGGER_SCRIPT_AVAILABLE))
       .Times(1);
   SimulateWebContentsVisibilityChanged(content::Visibility::VISIBLE);
-  AssertRecordedFinishedState(
-      Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_FAILED_NAVIGATE);
+  AssertRecordedFinishedState(Metrics::LiteScriptFinishedState::
+                                  LITE_SCRIPT_NO_TRIGGER_SCRIPT_AVAILABLE);
 }
 
 TEST_F(TriggerScriptCoordinatorTest, BottomSheetClosedWithSwipe) {
diff --git a/components/discardable_memory/client/client_discardable_shared_memory_manager.cc b/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
index 332af99..24b1b24 100644
--- a/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
+++ b/components/discardable_memory/client/client_discardable_shared_memory_manager.cc
@@ -112,10 +112,8 @@
     DCHECK(!is_locked());
     return;
   }
-  if (is_locked())
-    manager_->UnlockSpan(span_.get());
 
-  manager_->ReleaseMemory(this, std::move(span_));
+  manager_->UnlockAndReleaseMemory(this, std::move(span_));
 }
 
 bool ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Lock() {
@@ -537,9 +535,15 @@
   return span->shared_memory()->Unlock(offset, length);
 }
 
-void ClientDiscardableSharedMemoryManager::ReleaseMemory(
+void ClientDiscardableSharedMemoryManager::UnlockAndReleaseMemory(
     DiscardableMemoryImpl* memory,
     std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
+  memory->manager_->lock_.AssertAcquired();
+  // lock_.AssertAcquired();
+  if (memory->is_locked()) {
+    UnlockSpan(span.get());
+  }
+
   DCHECK(span);
   auto removed = allocated_memory_.erase(memory);
   DCHECK_EQ(removed, 1u);
diff --git a/components/discardable_memory/client/client_discardable_shared_memory_manager.h b/components/discardable_memory/client/client_discardable_shared_memory_manager.h
index 36d96fe..f929349 100644
--- a/components/discardable_memory/client/client_discardable_shared_memory_manager.h
+++ b/components/discardable_memory/client/client_discardable_shared_memory_manager.h
@@ -114,6 +114,7 @@
   bool is_purge_scheduled_ GUARDED_BY(lock_) = false;
 
  private:
+  friend class TestClientDiscardableSharedMemoryManager;
   class DiscardableMemoryImpl : public base::DiscardableMemory {
    public:
     DiscardableMemoryImpl(
@@ -174,8 +175,9 @@
   // Releases all unlocked memory that was last locked at least |min_age| ago.
   void PurgeUnlockedMemory(base::TimeDelta min_age);
   void ReleaseFreeMemoryImpl();
-  void ReleaseMemory(DiscardableMemoryImpl* memory,
-                     std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
+  void UnlockAndReleaseMemory(
+      DiscardableMemoryImpl* memory,
+      std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
   void ReleaseSpan(std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
diff --git a/components/discardable_memory/common/discardable_shared_memory_heap.cc b/components/discardable_memory/common/discardable_shared_memory_heap.cc
index 2ae86cb..ceda5c0f 100644
--- a/components/discardable_memory/common/discardable_shared_memory_heap.cc
+++ b/components/discardable_memory/common/discardable_shared_memory_heap.cc
@@ -10,15 +10,21 @@
 #include <utility>
 
 #include "base/bits.h"
+#include "base/feature_list.h"
 #include "base/format_macros.h"
 #include "base/memory/aligned_memory.h"
 #include "base/memory/discardable_shared_memory.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_dump_manager.h"
 
 namespace discardable_memory {
+
+const base::Feature kReleaseDiscardableFreeListPages{
+    "ReleaseDiscardableFreeListPages", base::FEATURE_DISABLED_BY_DEFAULT};
+
 namespace {
 
 bool IsInFreeList(DiscardableSharedMemoryHeap::Span* span) {
@@ -142,6 +148,25 @@
   // First add length of |span| to |num_free_blocks_|.
   num_free_blocks_ += span->length_;
 
+  if (base::FeatureList::IsEnabled(kReleaseDiscardableFreeListPages)) {
+    SCOPED_UMA_HISTOGRAM_SHORT_TIMER("Memory.Discardable.FreeListReleaseTime");
+    // Release as much memory as possible before putting it into the freelists
+    // in order to reduce their size. Getting this memory back is still much
+    // cheaper than an IPC, while also saving us space in the freelists.
+    //
+    // The "+ 1" in the offset is for the SharedState that's at the start of
+    // the DiscardableSharedMemory. See DiscardableSharedMemory for details on
+    // what this is used for. We don't want to remove it, so we offset by an
+    // extra page.
+    size_t offset = (1 + span->start_) * base::GetPageSize() -
+                    reinterpret_cast<size_t>(span->shared_memory()->memory());
+    // Since we always offset by at least one page because of the SharedState,
+    // our offset should never be 0.
+    DCHECK_GT(offset, 0u);
+    span->shared_memory()->ReleaseMemoryIfPossible(
+        offset, span->length_ * base::GetPageSize());
+  }
+
   // Merge with previous span if possible.
   auto prev_it = spans_.find(span->start_ - 1);
   if (prev_it != spans_.end() && IsInFreeList(prev_it->second)) {
@@ -250,6 +275,29 @@
   return num_free_blocks_ * block_size_;
 }
 
+base::Optional<size_t> DiscardableSharedMemoryHeap::GetResidentSize() const {
+  size_t resident_size = 0;
+  // Each member of |free_spans_| is a LinkedList of Spans. We need to iterate
+  // over each of these.
+  for (const base::LinkedList<Span>& span_list : free_spans_) {
+    for (base::LinkNode<Span>* curr = span_list.head(); curr != span_list.end();
+         curr = curr->next()) {
+      Span* free_span = curr->value();
+      // A given span over a piece of Shared Memory (which we will call
+      // |shared_memory|) has Span::start_ initialized to a value equivalent
+      // to reinterpret_cast<shared_memory->memory()) / block_size_.
+      void* mem = reinterpret_cast<void*>(free_span->start() * block_size_);
+      base::Optional<size_t> resident_in_span =
+          base::trace_event::ProcessMemoryDump::CountResidentBytes(
+              mem, free_span->length() * base::GetPageSize());
+      if (!resident_in_span)
+        return base::nullopt;
+      resident_size += resident_in_span.value();
+    }
+  }
+  return resident_size;
+}
+
 bool DiscardableSharedMemoryHeap::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
@@ -275,6 +323,12 @@
     total_dump->AddScalar("virtual_size",
                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
                           total_size);
+    auto resident_size = GetResidentSize();
+    if (resident_size) {
+      total_dump->AddScalar("resident_size",
+                            base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                            resident_size.value());
+    }
   } else {
     // This iterates over all the memory allocated by the heap, and calls
     // |OnMemoryDump| for each. It does not contain any information about the
@@ -292,6 +346,7 @@
     std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
   DCHECK(!IsInFreeList(span.get()));
   size_t index = std::min(span->length_, base::size(free_spans_)) - 1;
+
   free_spans_[index].Append(span.release());
 }
 
diff --git a/components/discardable_memory/common/discardable_shared_memory_heap.h b/components/discardable_memory/common/discardable_shared_memory_heap.h
index efa9e05..d4de3fe8 100644
--- a/components/discardable_memory/common/discardable_shared_memory_heap.h
+++ b/components/discardable_memory/common/discardable_shared_memory_heap.h
@@ -150,6 +150,8 @@
   void ReleaseMemory(const base::DiscardableSharedMemory* shared_memory,
                      size_t size);
 
+  base::Optional<size_t> GetResidentSize() const;
+
   // Dumps memory statistics about a memory segment for chrome://tracing.
   void OnMemoryDump(const base::DiscardableSharedMemory* shared_memory,
                     size_t size,
diff --git a/components/neterror/resources/BUILD.gn b/components/neterror/resources/BUILD.gn
index 34307cd..267d5b99 100644
--- a/components/neterror/resources/BUILD.gn
+++ b/components/neterror/resources/BUILD.gn
@@ -11,6 +11,9 @@
     "//components/security_interstitials/core/common/resources:interstitial_mobile_nav",
     "//ui/webui/resources/js:load_time_data",
   ]
+  if (is_ios) {
+    deps += [ ":error_page_controller_ios" ]
+  }
 }
 
 js_library("neterror") {
@@ -22,3 +25,8 @@
 
 js_library("offline") {
 }
+
+if (is_ios) {
+  js_library("error_page_controller_ios") {
+  }
+}
diff --git a/components/neterror/resources/error_page_controller_ios.js b/components/neterror/resources/error_page_controller_ios.js
new file mode 100644
index 0000000..209c9fb
--- /dev/null
+++ b/components/neterror/resources/error_page_controller_ios.js
@@ -0,0 +1,50 @@
+// 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.
+
+window.errorPageController = {
+  // Execute a button click to download page later.
+  downloadButtonClick: function() {},
+
+  // Execute a click on the reload button.
+  reloadButtonClick: function() {},
+
+  // Execute a "Details" button click.
+  detailsButtonClick: function() {},
+
+  // Execute a "Diagnose Errors" button click.
+  diagnoseErrorsButtonClick: function() {},
+
+  // ???
+  launchOfflineItem: function() {},
+  savePageForLater: function() {},
+  cancelSavePage: function() {},
+  listVisibilityChange: function() {},
+
+  // Track easter egg plays and high scores.
+  trackEasterEgg: function() {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'errorPageController.trackEasterEgg'});
+  },
+
+  updateEasterEggHighScore: function(highScore) {
+    __gCrWeb.message.invokeOnHost({
+      'command': 'errorPageController.updateEasterEggHighScore',
+      'highScore': highScore.toString()
+    });
+  },
+
+  resetEasterEggHighScore: function() {
+    __gCrWeb.message.invokeOnHost(
+        {'command': 'errorPageController.resetEasterEggHighScore'});
+  }
+};
+
+// Create a __gCrWeb binding of initializeEasterEggHighScore so it can be
+// called using JS messaging.
+__gCrWeb.errorPageController = {};
+__gCrWeb['errorPageController'] = __gCrWeb.errorPageController;
+__gCrWeb.errorPageController['initializeEasterEggHighScore'] = function(
+    highscore) {
+  initializeEasterEggHighScore(highscore);
+};
diff --git a/components/neterror/resources/neterror.html b/components/neterror/resources/neterror.html
index d3cbee72..0fea0137 100644
--- a/components/neterror/resources/neterror.html
+++ b/components/neterror/resources/neterror.html
@@ -13,6 +13,9 @@
   <script src="neterror.js"></script>
   <script src="../../../components/security_interstitials/core/common/resources/interstitial_mobile_nav.js"></script>
   <script src="offline.js"></script>
+  <if expr="is_ios">
+    <script src="error_page_controller_ios.js"></script>
+  </if>
 </head>
 <body id="t" style="font-family: $i18n{fontfamily}; font-size: $i18n{fontsize}">
   <div id="main-frame-error" class="interstitial-wrapper">
diff --git a/components/no_state_prefetch/common/prerender_url_loader_throttle.cc b/components/no_state_prefetch/common/prerender_url_loader_throttle.cc
index 96aaf92..e5538c6 100644
--- a/components/no_state_prefetch/common/prerender_url_loader_throttle.cc
+++ b/components/no_state_prefetch/common/prerender_url_loader_throttle.cc
@@ -38,14 +38,10 @@
 }  // namespace
 
 PrerenderURLLoaderThrottle::PrerenderURLLoaderThrottle(
-    prerender::mojom::PrerenderMode mode,
     const std::string& histogram_prefix,
     mojo::PendingRemote<prerender::mojom::PrerenderCanceler> canceler)
     : histogram_prefix_(histogram_prefix), canceler_(std::move(canceler)) {
   DCHECK(canceler_);
-  // TODO(https://crbug.com/755921): Remove the `mode` param. This is now kept
-  // just for validation check until mojom::PrerenderMode is completely removed.
-  DCHECK_EQ(mode, mojom::PrerenderMode::kPrefetchOnly);
 }
 
 PrerenderURLLoaderThrottle::~PrerenderURLLoaderThrottle() {
diff --git a/components/no_state_prefetch/common/prerender_url_loader_throttle.h b/components/no_state_prefetch/common/prerender_url_loader_throttle.h
index 5ecca87..d6199da2 100644
--- a/components/no_state_prefetch/common/prerender_url_loader_throttle.h
+++ b/components/no_state_prefetch/common/prerender_url_loader_throttle.h
@@ -11,7 +11,6 @@
 #include "base/sequenced_task_runner.h"
 #include "base/timer/timer.h"
 #include "components/no_state_prefetch/common/prerender_canceler.mojom.h"
-#include "components/no_state_prefetch/common/prerender_types.mojom.h"
 #include "net/base/request_priority.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
@@ -23,7 +22,6 @@
       public base::SupportsWeakPtr<PrerenderURLLoaderThrottle> {
  public:
   PrerenderURLLoaderThrottle(
-      prerender::mojom::PrerenderMode mode,
       const std::string& histogram_prefix,
       mojo::PendingRemote<prerender::mojom::PrerenderCanceler> canceler);
   ~PrerenderURLLoaderThrottle() override;
diff --git a/components/no_state_prefetch/renderer/prerender_helper.cc b/components/no_state_prefetch/renderer/prerender_helper.cc
index de57da94..09330e1 100644
--- a/components/no_state_prefetch/renderer/prerender_helper.cc
+++ b/components/no_state_prefetch/renderer/prerender_helper.cc
@@ -6,7 +6,7 @@
 
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
-#include "components/no_state_prefetch//common/prerender_url_loader_throttle.h"
+#include "components/no_state_prefetch/common/prerender_url_loader_throttle.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
@@ -18,14 +18,11 @@
 namespace prerender {
 
 PrerenderHelper::PrerenderHelper(content::RenderFrame* render_frame,
-                                 prerender::mojom::PrerenderMode prerender_mode,
                                  const std::string& histogram_prefix)
     : content::RenderFrameObserver(render_frame),
       content::RenderFrameObserverTracker<PrerenderHelper>(render_frame),
-      prerender_mode_(prerender_mode),
       histogram_prefix_(histogram_prefix),
       start_time_(base::TimeTicks::Now()) {
-  DCHECK_NE(prerender_mode_, prerender::mojom::PrerenderMode::kNoPrerender);
 }
 
 PrerenderHelper::~PrerenderHelper() = default;
@@ -42,13 +39,12 @@
   if (!prerender_helper)
     return nullptr;
 
-  mojo::PendingRemote<prerender::mojom::PrerenderCanceler> canceler;
+  mojo::PendingRemote<mojom::PrerenderCanceler> canceler;
   render_frame->GetBrowserInterfaceBroker()->GetInterface(
       canceler.InitWithNewPipeAndPassReceiver());
 
   auto throttle = std::make_unique<PrerenderURLLoaderThrottle>(
-      prerender_helper->prerender_mode(), prerender_helper->histogram_prefix(),
-      std::move(canceler));
+      prerender_helper->histogram_prefix(), std::move(canceler));
   prerender_helper->AddThrottle(*throttle);
   return throttle;
 }
@@ -56,25 +52,19 @@
 // static.
 bool PrerenderHelper::IsPrerendering(const content::RenderFrame* render_frame) {
   return PrerenderHelper::GetPrerenderMode(render_frame) !=
-         prerender::mojom::PrerenderMode::kNoPrerender;
+         mojom::PrerenderMode::kNoPrerender;
 }
 
 // static.
-prerender::mojom::PrerenderMode PrerenderHelper::GetPrerenderMode(
+mojom::PrerenderMode PrerenderHelper::GetPrerenderMode(
     const content::RenderFrame* render_frame) {
   PrerenderHelper* helper = PrerenderHelper::Get(render_frame);
   if (!helper)
-    return prerender::mojom::PrerenderMode::kNoPrerender;
-
-  DCHECK_NE(helper->prerender_mode_,
-            prerender::mojom::PrerenderMode::kNoPrerender);
-  return helper->prerender_mode_;
+    return mojom::PrerenderMode::kNoPrerender;
+  return mojom::PrerenderMode::kPrefetchOnly;
 }
 
 void PrerenderHelper::DidFinishDocumentLoad() {
-  if (prerender_mode_ != prerender::mojom::PrerenderMode::kPrefetchOnly)
-    return;
-
   parsed_time_ = base::TimeTicks::Now();
   prefetch_finished_ = true;
   if (prefetch_count_ == 0)
@@ -90,11 +80,9 @@
   // sending the "prefetch finished" signal until they are destroyed. This is
   // important since that signal tells the browser that it can tear down this
   // renderer which could interrupt subresource prefetching.
-  if (prerender_mode_ == prerender::mojom::PrerenderMode::kPrefetchOnly) {
-    prefetch_count_++;
-    throttle.set_destruction_closure(base::BindOnce(
-        &PrerenderHelper::OnThrottleDestroyed, weak_factory_.GetWeakPtr()));
-  }
+  prefetch_count_++;
+  throttle.set_destruction_closure(base::BindOnce(
+      &PrerenderHelper::OnThrottleDestroyed, weak_factory_.GetWeakPtr()));
 }
 
 void PrerenderHelper::OnThrottleDestroyed() {
@@ -111,7 +99,7 @@
   UMA_HISTOGRAM_MEDIUM_TIMES("Prerender.NoStatePrefetchRendererParseTime",
                              parsed_time_ - start_time_);
 
-  mojo::Remote<prerender::mojom::PrerenderCanceler> canceler;
+  mojo::Remote<mojom::PrerenderCanceler> canceler;
   render_frame()->GetBrowserInterfaceBroker()->GetInterface(
       canceler.BindNewPipeAndPassReceiver());
   canceler->CancelPrerenderForNoStatePrefetch();
diff --git a/components/no_state_prefetch/renderer/prerender_helper.h b/components/no_state_prefetch/renderer/prerender_helper.h
index 1aba2aa6..e8e138f 100644
--- a/components/no_state_prefetch/renderer/prerender_helper.h
+++ b/components/no_state_prefetch/renderer/prerender_helper.h
@@ -27,7 +27,6 @@
       public content::RenderFrameObserverTracker<PrerenderHelper> {
  public:
   PrerenderHelper(content::RenderFrame* render_frame,
-                  prerender::mojom::PrerenderMode prerender_mode,
                   const std::string& histogram_prefix);
 
   ~PrerenderHelper() override;
@@ -40,12 +39,9 @@
   // Returns true if |render_frame| is currently prerendering.
   static bool IsPrerendering(const content::RenderFrame* render_frame);
 
-  static prerender::mojom::PrerenderMode GetPrerenderMode(
+  static mojom::PrerenderMode GetPrerenderMode(
       const content::RenderFrame* render_frame);
 
-  prerender::mojom::PrerenderMode prerender_mode() const {
-    return prerender_mode_;
-  }
   std::string histogram_prefix() const { return histogram_prefix_; }
 
  private:
@@ -57,7 +53,6 @@
   void OnThrottleDestroyed();
   void SendPrefetchFinished();
 
-  const prerender::mojom::PrerenderMode prerender_mode_;
   std::string histogram_prefix_;
 
   int prefetch_count_ = 0;
diff --git a/components/no_state_prefetch/renderer/prerender_render_frame_observer.cc b/components/no_state_prefetch/renderer/prerender_render_frame_observer.cc
index 7888156..6391f92 100644
--- a/components/no_state_prefetch/renderer/prerender_render_frame_observer.cc
+++ b/components/no_state_prefetch/renderer/prerender_render_frame_observer.cc
@@ -41,9 +41,8 @@
   if (!prerender_helper) {
     // The PrerenderHelper will destroy itself either after recording
     // histograms or on destruction of the RenderView.
-    prerender_helper = new prerender::PrerenderHelper(
-        render_frame(), /*prerender_mode=*/mojom::PrerenderMode::kPrefetchOnly,
-        histogram_prefix);
+    prerender_helper =
+        new prerender::PrerenderHelper(render_frame(), histogram_prefix);
   }
 
   prerender::PrerenderObserverList::SetIsPrerenderingForFrame(
diff --git a/components/password_manager/core/browser/browser_save_password_progress_logger.cc b/components/password_manager/core/browser/browser_save_password_progress_logger.cc
index 5f1fa0d..5af2b691 100644
--- a/components/password_manager/core/browser/browser_save_password_progress_logger.cc
+++ b/components/password_manager/core/browser/browser_save_password_progress_logger.cc
@@ -4,6 +4,7 @@
 
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 
+#include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -18,6 +19,7 @@
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/browser/proto/server.pb.h"
 #include "components/autofill/core/common/signatures.h"
+#include "components/password_manager/core/browser/generation/password_requirements_spec_printer.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_metrics_recorder.h"
 #include "components/password_manager/core/browser/password_manager.h"
@@ -265,6 +267,12 @@
     if (field->generated_password_changed())
       field_info += ", generated password changed";
 
+    if (field->password_requirements()) {
+      std::ostringstream s;
+      s << *field->password_requirements();
+      base::StrAppend(&field_info, {", PASSWORD_REQUIREMENTS: ", s.str()});
+    }
+
     result += field_info + "\n";
   }
 
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
index 1c23bb42..ac6067e 100644
--- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -421,7 +421,7 @@
   const std::vector<CompromisedCredentials> credentials = {
       CompromisedCredentials(
           form_digest_.signon_realm, base::ASCIIToUTF16("username_value"),
-          base::Time::FromTimeT(1), CompromiseType::kLeaked, false)};
+          base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false))};
   static_cast<CompromisedCredentialsConsumer*>(form_fetcher_.get())
       ->OnGetCompromisedCredentials(credentials);
   EXPECT_THAT(form_fetcher_->GetCompromisedCredentials(),
@@ -491,7 +491,7 @@
 TEST_P(FormFetcherImplTest, FetchCompromised) {
   CompromisedCredentials credentials(
       form_digest_.signon_realm, base::ASCIIToUTF16("username_value"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   std::vector<CompromisedCredentials> list = {credentials};
   EXPECT_CALL(*mock_store_,
               GetMatchingCompromisedCredentialsImpl(form_digest_.signon_realm))
@@ -813,7 +813,7 @@
   const std::vector<CompromisedCredentials> credentials = {
       CompromisedCredentials(
           form_digest_.signon_realm, base::ASCIIToUTF16("username_value"),
-          base::Time::FromTimeT(1), CompromiseType::kLeaked, false)};
+          base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false))};
   static_cast<CompromisedCredentialsConsumer*>(form_fetcher_.get())
       ->OnGetCompromisedCredentials(credentials);
 
diff --git a/components/password_manager/core/browser/insecure_credentials_table.cc b/components/password_manager/core/browser/insecure_credentials_table.cc
index 83ad7c9..04c6481 100644
--- a/components/password_manager/core/browser/insecure_credentials_table.cc
+++ b/components/password_manager/core/browser/insecure_credentials_table.cc
@@ -28,7 +28,7 @@
     bool is_muted = !!s->ColumnInt64(4);
 
     results.emplace_back(std::move(signon_realm), std::move(username),
-                         create_time, insecurity_type, is_muted);
+                         create_time, insecurity_type, IsMuted(is_muted));
   }
   return results;
 }
@@ -41,7 +41,7 @@
                                                base::string16 username,
                                                base::Time create_time,
                                                CompromiseType insecurity_type,
-                                               bool is_muted)
+                                               IsMuted is_muted)
     : signon_realm(std::move(signon_realm)),
       username(std::move(username)),
       create_time(create_time),
@@ -107,7 +107,7 @@
   s.BindInt(0, static_cast<int>(compromised_credentials.compromise_type));
   s.BindInt64(1, compromised_credentials.create_time.ToDeltaSinceWindowsEpoch()
                      .InMicroseconds());
-  s.BindBool(2, compromised_credentials.is_muted);
+  s.BindBool(2, compromised_credentials.is_muted.value());
   s.BindString(3, compromised_credentials.signon_realm);
   s.BindString16(4, compromised_credentials.username);
 
diff --git a/components/password_manager/core/browser/insecure_credentials_table.h b/components/password_manager/core/browser/insecure_credentials_table.h
index 792fcb9..99cd489c 100644
--- a/components/password_manager/core/browser/insecure_credentials_table.h
+++ b/components/password_manager/core/browser/insecure_credentials_table.h
@@ -19,6 +19,7 @@
 namespace password_manager {
 
 using BulkCheckDone = base::StrongAlias<class BulkCheckDoneTag, bool>;
+using IsMuted = base::StrongAlias<class IsMutedTag, bool>;
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
@@ -51,7 +52,7 @@
                          base::string16 username,
                          base::Time create_time,
                          CompromiseType compromise_type,
-                         bool is_muted);
+                         IsMuted is_muted);
   CompromisedCredentials(const CompromisedCredentials& rhs);
   CompromisedCredentials(CompromisedCredentials&& rhs);
   CompromisedCredentials& operator=(const CompromisedCredentials& rhs);
@@ -67,7 +68,7 @@
   // The type of the credentials that was compromised.
   CompromiseType compromise_type = CompromiseType::kLeaked;
   // Whether the problem was explicitly muted by the user.
-  bool is_muted = false;
+  IsMuted is_muted{false};
   // The store in which those credentials are stored.
   PasswordForm::Store in_store = PasswordForm::Store::kNotSet;
 };
diff --git a/components/password_manager/core/browser/insecure_credentials_table_unittest.cc b/components/password_manager/core/browser/insecure_credentials_table_unittest.cc
index b4c2ddec..332a580 100644
--- a/components/password_manager/core/browser/insecure_credentials_table_unittest.cc
+++ b/components/password_manager/core/browser/insecure_credentials_table_unittest.cc
@@ -78,7 +78,7 @@
   std::unique_ptr<LoginDatabase> login_db_;
   CompromisedCredentials test_data_{kTestDomain, base::ASCIIToUTF16(kUsername),
                                     base::Time::FromTimeT(1),
-                                    CompromiseType::kLeaked, false};
+                                    CompromiseType::kLeaked, IsMuted(false)};
   PasswordForm test_form_ = TestForm();
 };
 
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper.cc b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
index 0b891d9..5215a29 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_helper.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
@@ -59,7 +59,7 @@
             form->IsUsingAccountStore() ? *account_store_ : *profile_store_;
         store.AddCompromisedCredentials(CompromisedCredentials(
             form->signon_realm, form->username_value, base::Time::Now(),
-            CompromiseType::kLeaked, false));
+            CompromiseType::kLeaked, IsMuted(false)));
       }
     }
 
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
index f6c5392f..ad504a5 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
@@ -202,11 +202,11 @@
   EXPECT_CALL(*store_, AddCompromisedCredentialsImpl(CompromisedCredentials(
                            GetSignonRealm(GURL(kLeakedOrigin)),
                            ASCIIToUTF16(kLeakedUsername), base::Time::Now(),
-                           CompromiseType::kLeaked, false)));
+                           CompromiseType::kLeaked, IsMuted(false))));
   EXPECT_CALL(*store_, AddCompromisedCredentialsImpl(CompromisedCredentials(
                            GetSignonRealm(GURL(kOtherOrigin)),
                            ASCIIToUTF16(kLeakedUsername), base::Time::Now(),
-                           CompromiseType::kLeaked, false)));
+                           CompromiseType::kLeaked, IsMuted(false))));
   InitiateGetCredentialLeakType();
 }
 
@@ -217,10 +217,11 @@
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
                                                 CompromisedSitesCount(1));
 
-  EXPECT_CALL(*store_, AddCompromisedCredentialsImpl(CompromisedCredentials(
-                           GetSignonRealm(GURL(kOtherOrigin)),
-                           ASCIIToUTF16(kLeakedUsernameNonCanonicalized),
-                           base::Time::Now(), CompromiseType::kLeaked, false)));
+  EXPECT_CALL(*store_,
+              AddCompromisedCredentialsImpl(CompromisedCredentials(
+                  GetSignonRealm(GURL(kOtherOrigin)),
+                  ASCIIToUTF16(kLeakedUsernameNonCanonicalized),
+                  base::Time::Now(), CompromiseType::kLeaked, IsMuted(false))));
   InitiateGetCredentialLeakType();
 }
 
diff --git a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
index 820e7be..1ac5475 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
@@ -347,7 +347,7 @@
 
   const CompromisedCredentials compromised_credentials(
       GetSignonRealm(form.url), form.username_value, base::Time::Now(),
-      CompromiseType::kLeaked, false);
+      CompromiseType::kLeaked, IsMuted(false));
   EXPECT_CALL(*store(), AddCompromisedCredentialsImpl(compromised_credentials));
   WaitForPasswordStore();
 }
diff --git a/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc b/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc
index 856f686..a28d924 100644
--- a/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc
+++ b/components/password_manager/core/browser/multi_store_form_fetcher_unittest.cc
@@ -443,13 +443,13 @@
   Fetch();
   CompromisedCredentials profile_store_compromised_credentials(
       form_digest_.signon_realm, base::ASCIIToUTF16("profile_username"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   profile_store_compromised_credentials.in_store =
       PasswordForm::Store::kProfileStore;
 
   CompromisedCredentials account_store_compromised_credentials(
       form_digest_.signon_realm, base::ASCIIToUTF16("account_username"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   account_store_compromised_credentials.in_store =
       PasswordForm::Store::kAccountStore;
 
diff --git a/components/password_manager/core/browser/password_store_unittest.cc b/components/password_manager/core/browser/password_store_unittest.cc
index c5dca5b..6da2d33 100644
--- a/components/password_manager/core/browser/password_store_unittest.cc
+++ b/components/password_manager/core/browser/password_store_unittest.cc
@@ -365,7 +365,7 @@
 TEST_F(PasswordStoreTest, CompromisedCredentialsObserverOnRemoveLogin) {
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
 
   scoped_refptr<PasswordStoreImpl> store = CreatePasswordStore();
   store->Init(nullptr);
@@ -405,7 +405,7 @@
 TEST_F(PasswordStoreTest, CompromisedCredentialsObserverOnLoginUpdated) {
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   scoped_refptr<PasswordStoreImpl> store = CreatePasswordStore();
   store->Init(nullptr);
 
@@ -444,7 +444,7 @@
 TEST_F(PasswordStoreTest, CompromisedCredentialsObserverOnLoginAdded) {
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   scoped_refptr<PasswordStoreImpl> store = CreatePasswordStore();
   store->Init(nullptr);
 
@@ -495,7 +495,7 @@
                                                  1};
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
 
   scoped_refptr<PasswordStoreImpl> store = CreatePasswordStore();
   store->Init(nullptr);
@@ -533,7 +533,7 @@
 
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
 
   scoped_refptr<PasswordStoreImpl> store = CreatePasswordStore();
   store->Init(nullptr);
@@ -566,7 +566,7 @@
 
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   constexpr PasswordFormData kTestCredentials = {PasswordForm::Scheme::kHtml,
                                                  kTestWebRealm1,
                                                  kTestWebRealm1,
@@ -1504,10 +1504,10 @@
     store->AddLogin(*FillPasswordFormWithData(data));
   CompromisedCredentials compromised_credentials(
       "https://example.com/", base::ASCIIToUTF16("username"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   CompromisedCredentials compromised_credentials2(
       "https://2.example.com/", base::ASCIIToUTF16("username2"),
-      base::Time::FromTimeT(2), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(2), CompromiseType::kLeaked, IsMuted(false));
 
   store->AddCompromisedCredentials(compromised_credentials);
   store->AddCompromisedCredentials(compromised_credentials2);
@@ -1546,10 +1546,10 @@
 
   CompromisedCredentials credentials1(
       kTestWebRealm1, base::ASCIIToUTF16("username_value"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   CompromisedCredentials credentials2(
       kTestWebRealm2, base::ASCIIToUTF16("username_value"),
-      base::Time::FromTimeT(2), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(2), CompromiseType::kLeaked, IsMuted(false));
   for (const auto& credentials : {credentials1, credentials2})
     store->AddCompromisedCredentials(credentials);
 
@@ -1580,13 +1580,13 @@
 
   CompromisedCredentials credentials1(
       kTestWebRealm1, base::ASCIIToUTF16("username_value"),
-      base::Time::FromTimeT(1), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(1), CompromiseType::kLeaked, IsMuted(false));
   CompromisedCredentials credentials2(
       kTestAndroidRealm1, base::ASCIIToUTF16("username_value_1"),
-      base::Time::FromTimeT(2), CompromiseType::kPhished, false);
+      base::Time::FromTimeT(2), CompromiseType::kPhished, IsMuted(false));
   CompromisedCredentials credentials3(
       kTestWebRealm2, base::ASCIIToUTF16("username_value_2"),
-      base::Time::FromTimeT(3), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(3), CompromiseType::kLeaked, IsMuted(false));
   for (const auto& credentials : {credentials1, credentials2, credentials3})
     store->AddCompromisedCredentials(credentials);
 
@@ -1610,13 +1610,13 @@
 TEST_F(PasswordStoreTest, RemoveCompromisedCredentialsCreatedBetween) {
   CompromisedCredentials compromised_credentials1(
       "https://example1.com/", base::ASCIIToUTF16("username1"),
-      base::Time::FromTimeT(100), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(100), CompromiseType::kLeaked, IsMuted(false));
   CompromisedCredentials compromised_credentials2(
       "https://2.example.com/", base::ASCIIToUTF16("username2"),
-      base::Time::FromTimeT(200), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(200), CompromiseType::kLeaked, IsMuted(false));
   CompromisedCredentials compromised_credentials3(
       "https://example3.com/", base::ASCIIToUTF16("username3"),
-      base::Time::FromTimeT(300), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(300), CompromiseType::kLeaked, IsMuted(false));
 
   scoped_refptr<PasswordStoreImpl> store = CreatePasswordStore();
   store->Init(nullptr);
@@ -1671,7 +1671,7 @@
 
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username1"),
-      base::Time::FromTimeT(100), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(100), CompromiseType::kLeaked, IsMuted(false));
   constexpr PasswordFormData kTestCredential = {PasswordForm::Scheme::kHtml,
                                                 kTestWebRealm1,
                                                 kTestWebOrigin1,
@@ -1707,7 +1707,7 @@
 
   CompromisedCredentials compromised_credentials(
       kTestWebRealm1, base::ASCIIToUTF16("username1"),
-      base::Time::FromTimeT(100), CompromiseType::kLeaked, false);
+      base::Time::FromTimeT(100), CompromiseType::kLeaked, IsMuted(false));
   constexpr PasswordFormData kTestCredential = {PasswordForm::Scheme::kHtml,
                                                 kTestWebRealm1,
                                                 kTestWebOrigin1,
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
index 2ec0103e..f3fcba9c 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
@@ -267,7 +267,7 @@
       GetStoreFor(saved_password)
           .AddCompromisedCredentials(CompromisedCredentials(
               saved_password.signon_realm, saved_password.username_value,
-              base::Time::Now(), CompromiseType::kLeaked, false));
+              base::Time::Now(), CompromiseType::kLeaked, IsMuted(false)));
     }
   }
 }
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
index 63ff837..cbb21389 100644
--- a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
+++ b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
@@ -63,7 +63,7 @@
     CompromiseType type = CompromiseType::kLeaked) {
   return CompromisedCredentials(std::move(signon_realm),
                                 base::ASCIIToUTF16(username), base::Time(),
-                                type, false);
+                                type, IsMuted(false));
 }
 
 PasswordForm MakeSavedPassword(base::StringPiece signon_realm,
diff --git a/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc b/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc
index 48103ac..6f5d177 100644
--- a/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc
+++ b/components/password_manager/core/browser/ui/post_save_compromised_helper_unittest.cc
@@ -31,7 +31,7 @@
     PasswordForm::Store store = PasswordForm::Store::kProfileStore) {
   CompromisedCredentials compromised(kSignonRealm, base::ASCIIToUTF16(username),
                                      base::Time(), CompromiseType::kLeaked,
-                                     false);
+                                     IsMuted(false));
   compromised.in_store = store;
   return compromised;
 }
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index 5f3e977..3727500 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -46,6 +46,7 @@
     "execution_context_priority/root_vote_observer.cc",
     "execution_context_priority/root_vote_observer.h",
     "features.cc",
+    "freezing/freezing.cc",
     "freezing/freezing_vote_aggregator.cc",
     "freezing/freezing_vote_aggregator.h",
     "graph/frame_node.cc",
@@ -295,6 +296,7 @@
   if (!is_android) {
     sources += [
       "decorators/site_data_recorder_unittest.cc",
+      "freezing/freezing_unittest.cc",
       "persistence/site_data/exponential_moving_average_unittest.cc",
       "persistence/site_data/leveldb_site_data_store_unittest.cc",
       "persistence/site_data/non_recording_site_data_cache_unittest.cc",
diff --git a/components/performance_manager/embedder/graph_features_helper.h b/components/performance_manager/embedder/graph_features_helper.h
index 679aab3..e20ca222 100644
--- a/components/performance_manager/embedder/graph_features_helper.h
+++ b/components/performance_manager/embedder/graph_features_helper.h
@@ -28,6 +28,7 @@
       bool execution_context_registry : 1;
       bool frame_node_impl_describer : 1;
       bool frame_visibility_decorator : 1;
+      bool freezing_vote_decorator : 1;
       bool page_live_state_decorator : 1;
       bool page_load_tracker_decorator : 1;
       bool page_node_impl_describer : 1;
@@ -64,6 +65,11 @@
     return *this;
   }
 
+  constexpr GraphFeaturesHelper& EnableFreezingVoteDecorator() {
+    flags_.freezing_vote_decorator = true;
+    return *this;
+  }
+
   constexpr GraphFeaturesHelper& EnablePageLiveStateDecorator() {
     flags_.page_live_state_decorator = true;
     return *this;
@@ -120,6 +126,7 @@
     EnableExecutionContextRegistry();
     EnableFrameNodeImplDescriber();
     EnableFrameVisibilityDecorator();
+    EnableFreezingVoteDecorator();
     EnablePageLiveStateDecorator();
     EnablePageLoadTrackerDecorator();
     EnablePageNodeImplDescriber();
diff --git a/components/performance_manager/freezing/freezing.cc b/components/performance_manager/freezing/freezing.cc
new file mode 100644
index 0000000..90bb84f7
--- /dev/null
+++ b/components/performance_manager/freezing/freezing.cc
@@ -0,0 +1,141 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/freezing/freezing.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "components/performance_manager/freezing/freezing_vote_aggregator.h"
+#include "components/performance_manager/performance_manager_impl.h"
+#include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "content/public/browser/web_contents.h"
+
+namespace performance_manager {
+
+namespace freezing {
+
+namespace {
+
+// The counterpart of a FreezingVoteToken that lives on the PM sequence.
+class FreezingVoteTokenPMImpl : public PageNode::ObserverDefaultImpl {
+ public:
+  FreezingVoteTokenPMImpl(content::WebContents* content,
+                          FreezingVoteValue vote_value,
+                          const char* vote_reason);
+  ~FreezingVoteTokenPMImpl() override;
+  FreezingVoteTokenPMImpl(const FreezingVoteTokenPMImpl& other) = delete;
+  FreezingVoteTokenPMImpl& operator=(const FreezingVoteTokenPMImpl&) = delete;
+
+  // PageNodeObserver:
+  void OnBeforePageNodeRemoved(const PageNode* page_node) override;
+
+ private:
+  const PageNode* page_node_ = nullptr;
+  Graph* graph_ = nullptr;
+
+  // Voting channel wrapper. This objects should only be used on the PM
+  // sequence.
+  std::unique_ptr<FreezingVotingChannelWrapper> voter_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+// Concrete implementation of a FreezingVoteToken.
+class FreezingVoteTokenImpl : public FreezingVoteToken {
+ public:
+  FreezingVoteTokenImpl(content::WebContents* content,
+                        FreezingVoteValue vote_value,
+                        const char* vote_reason);
+  ~FreezingVoteTokenImpl() override;
+  FreezingVoteTokenImpl(const FreezingVoteTokenImpl& other) = delete;
+  FreezingVoteTokenImpl& operator=(const FreezingVoteTokenImpl&) = delete;
+
+ private:
+  // Voting channel wrapper. This objects should only be used on the PM
+  // sequence.
+  std::unique_ptr<FreezingVoteTokenPMImpl, base::OnTaskRunnerDeleter> pm_impl_;
+};
+
+}  // namespace
+
+FreezingVoteToken::FreezingVoteToken() = default;
+FreezingVoteToken::~FreezingVoteToken() = default;
+
+FreezingVoteTokenPMImpl::FreezingVoteTokenPMImpl(content::WebContents* content,
+                                                 FreezingVoteValue vote_value,
+                                                 const char* vote_reason) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+  // Register the vote on the PM sequence.
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<PageNode> page_node, FreezingVoteValue vote_value,
+             const char* vote_reason, FreezingVoteTokenPMImpl* voter_pm_impl,
+             Graph* graph) {
+            voter_pm_impl->voter_ =
+                std::make_unique<FreezingVotingChannelWrapper>();
+            voter_pm_impl->graph_ = graph;
+            graph->AddPageNodeObserver(voter_pm_impl);
+            voter_pm_impl->voter_->SetVotingChannel(
+                graph->GetRegisteredObjectAs<FreezingVoteAggregator>()
+                    ->GetVotingChannel());
+            if (page_node) {
+              voter_pm_impl->voter_->SubmitVote(page_node.get(),
+                                                {vote_value, vote_reason});
+              voter_pm_impl->page_node_ = page_node.get();
+            }
+          },
+          PerformanceManager::GetPageNodeForWebContents(content), vote_value,
+          // It's safe to use Unretained because |vote_reason| is a static
+          // string.
+          base::Unretained(vote_reason),
+          // It's safe to use Unretained because |this| can only be deleted
+          // from a task running on the PM sequence after this callback.
+          base::Unretained(this)));
+}
+
+FreezingVoteTokenPMImpl::~FreezingVoteTokenPMImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (graph_)
+    graph_->RemovePageNodeObserver(this);
+}
+
+void FreezingVoteTokenPMImpl::OnBeforePageNodeRemoved(
+    const PageNode* page_node) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (page_node == page_node_) {
+    // Invalidate the vote if its associated page node is destroyed. This can
+    // happen if a freezing vote token is released after the destruction of the
+    // WebContents it's associated with.
+    voter_->InvalidateVote(page_node);
+    page_node_ = nullptr;
+    graph_->RemovePageNodeObserver(this);
+    graph_ = nullptr;
+  }
+}
+
+FreezingVoteTokenImpl::FreezingVoteTokenImpl(content::WebContents* content,
+                                             FreezingVoteValue vote_value,
+                                             const char* vote_reason)
+    : pm_impl_(new FreezingVoteTokenPMImpl(content, vote_value, vote_reason),
+               base::OnTaskRunnerDeleter(PerformanceManager::GetTaskRunner())) {
+}
+
+FreezingVoteTokenImpl::~FreezingVoteTokenImpl() = default;
+
+std::unique_ptr<FreezingVoteToken> EmitFreezingVoteForWebContents(
+    content::WebContents* content,
+    FreezingVoteValue vote_value,
+    const char* vote_reason) {
+  return std::make_unique<FreezingVoteTokenImpl>(content, vote_value,
+                                                 vote_reason);
+}
+
+}  // namespace freezing
+}  // namespace performance_manager
\ No newline at end of file
diff --git a/components/performance_manager/freezing/freezing_unittest.cc b/components/performance_manager/freezing/freezing_unittest.cc
new file mode 100644
index 0000000..4d4e8fd8
--- /dev/null
+++ b/components/performance_manager/freezing/freezing_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/freezing/freezing.h"
+
+#include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/test_support/performance_manager_test_harness.h"
+#include "components/performance_manager/test_support/test_harness_helper.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+namespace freezing {
+
+namespace {
+
+constexpr char kCanFreeze[] = "Can freeze";
+constexpr char kCannotFreeze[] = "Cannot freeze";
+
+// Check that the freezing vote attached to the page node associated with
+// |content| has the expected value.
+void ExpectFreezingVote(content::WebContents* content,
+                        base::Optional<FreezingVote> expected_vote) {
+  base::RunLoop run_loop;
+  auto quit_closure = run_loop.QuitClosure();
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<PageNode> page_node, base::OnceClosure quit_closure,
+             base::Optional<FreezingVote> expected_vote) {
+            EXPECT_TRUE(page_node);
+            auto vote = page_node->GetFreezingVote();
+            EXPECT_EQ(expected_vote, vote);
+            std::move(quit_closure).Run();
+          },
+          PerformanceManager::GetPageNodeForWebContents(content),
+          std::move(quit_closure), expected_vote));
+  run_loop.Run();
+}
+
+}  // namespace
+
+class FreezingTest : public PerformanceManagerTestHarness {
+ public:
+  FreezingTest() = default;
+  ~FreezingTest() override = default;
+  FreezingTest(const FreezingTest& other) = delete;
+  FreezingTest& operator=(const FreezingTest&) = delete;
+
+  void SetUp() override {
+    GetGraphFeaturesHelper().EnableFreezingVoteDecorator();
+    PerformanceManagerTestHarness::SetUp();
+    SetContents(CreateTestWebContents());
+  }
+};
+
+TEST_F(FreezingTest, FreezingToken) {
+  content::WebContentsTester* web_contents_tester =
+      content::WebContentsTester::For(web_contents());
+  EXPECT_TRUE(web_contents_tester);
+  web_contents_tester->NavigateAndCommit(GURL("https:/foo.com"));
+
+  {
+    // Emit a positive freezing vote, this should make the page node freezable.
+    auto token = EmitFreezingVoteForWebContents(
+        web_contents(), FreezingVoteValue::kCanFreeze, kCanFreeze);
+    ExpectFreezingVote(web_contents(),
+                       FreezingVote(FreezingVoteValue::kCanFreeze, kCanFreeze));
+  }
+  // Once the freezing vote token is destroyed the vote should be invalidated.
+  ExpectFreezingVote(web_contents(), base::nullopt);
+
+  // Same test but for a negative freezing vote.
+  {
+    auto token = EmitFreezingVoteForWebContents(
+        web_contents(), FreezingVoteValue::kCannotFreeze, kCannotFreeze);
+    ExpectFreezingVote(
+        web_contents(),
+        FreezingVote(FreezingVoteValue::kCannotFreeze, kCannotFreeze));
+  }
+  ExpectFreezingVote(web_contents(), base::nullopt);
+}
+
+TEST_F(FreezingTest, WebContentsDestroyedBeforeToken) {
+  content::WebContentsTester* web_contents_tester =
+      content::WebContentsTester::For(web_contents());
+  EXPECT_TRUE(web_contents_tester);
+  web_contents_tester->NavigateAndCommit(GURL("https:/foo.com"));
+
+  // Emit a positive freezing vote, this should make the page node freezable.
+  auto token = EmitFreezingVoteForWebContents(
+      web_contents(), FreezingVoteValue::kCanFreeze, kCanFreeze);
+  ExpectFreezingVote(web_contents(),
+                     FreezingVote(FreezingVoteValue::kCanFreeze, kCanFreeze));
+  DeleteContents();
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace freezing
+}  // namespace performance_manager
\ No newline at end of file
diff --git a/components/performance_manager/graph_features_helper.cc b/components/performance_manager/graph_features_helper.cc
index d0dd4f32..7a56dd22 100644
--- a/components/performance_manager/graph_features_helper.cc
+++ b/components/performance_manager/graph_features_helper.cc
@@ -8,6 +8,7 @@
 
 #include "build/build_config.h"
 #include "components/performance_manager/decorators/frame_visibility_decorator.h"
+#include "components/performance_manager/decorators/freezing_vote_decorator.h"
 #include "components/performance_manager/decorators/page_load_tracker_decorator.h"
 #include "components/performance_manager/execution_context/execution_context_registry_impl.h"
 #include "components/performance_manager/execution_context_priority/execution_context_priority_decorator.h"
@@ -42,6 +43,8 @@
     Install<FrameNodeImplDescriber>(graph);
   if (flags_.frame_visibility_decorator)
     Install<FrameVisibilityDecorator>(graph);
+  if (flags_.freezing_vote_decorator)
+    Install<FreezingVoteDecorator>(graph);
   if (flags_.page_live_state_decorator)
     Install<PageLiveStateDecorator>(graph);
   if (flags_.page_load_tracker_decorator)
diff --git a/components/performance_manager/graph_features_helper_unittest.cc b/components/performance_manager/graph_features_helper_unittest.cc
index 5c5e5a3..cf0d6b1 100644
--- a/components/performance_manager/graph_features_helper_unittest.cc
+++ b/components/performance_manager/graph_features_helper_unittest.cc
@@ -50,7 +50,7 @@
       execution_context::ExecutionContextRegistry::GetFromGraph(&graph));
   EXPECT_FALSE(v8_memory::V8ContextTracker::GetFromGraph(&graph));
 
-  size_t graph_owned_count = 10;
+  size_t graph_owned_count = 11;
 #if !defined(OS_ANDROID)
   // The SiteDataRecorder is not available on Android.
   graph_owned_count++;
@@ -60,7 +60,7 @@
   features.EnableDefault();
   features.ConfigureGraph(&graph);
   EXPECT_EQ(graph_owned_count, graph.GraphOwnedCountForTesting());
-  EXPECT_EQ(2u, graph.GraphRegisteredCountForTesting());
+  EXPECT_EQ(3u, graph.GraphRegisteredCountForTesting());
   EXPECT_EQ(8u, graph.NodeDataDescriberCountForTesting());
   // Ensure the GraphRegistered objects can be queried directly.
   EXPECT_TRUE(
diff --git a/components/performance_manager/public/freezing/freezing.h b/components/performance_manager/public/freezing/freezing.h
index ff6980bf..10eaadefa 100644
--- a/components/performance_manager/public/freezing/freezing.h
+++ b/components/performance_manager/public/freezing/freezing.h
@@ -10,6 +10,10 @@
 
 #include "components/performance_manager/public/voting/voting.h"
 
+namespace content {
+class WebContents;
+}
+
 namespace performance_manager {
 
 class PageNode;
@@ -30,6 +34,28 @@
     voting::VoteConsumerDefaultImpl<FreezingVote>;
 using FreezingVotingChannelWrapper = voting::VotingChannelWrapper<FreezingVote>;
 
+// A freezing vote token, instances of this are meant to be retrieved by calling
+// |EmitFreezingVoteForWebContents|.
+class FreezingVoteToken {
+ public:
+  FreezingVoteToken(const FreezingVoteToken& other) = delete;
+  FreezingVoteToken& operator=(const FreezingVoteToken&) = delete;
+  virtual ~FreezingVoteToken() = 0;
+
+ protected:
+  FreezingVoteToken();
+};
+
+// Allows emiting a freezing vote for a WebContents. The vote's lifetime will
+// follow the lifetime of this object, as soon as it's released the vote will be
+// invalidated.
+//
+// NOTE: |vote_reason| *must* be a static string.
+std::unique_ptr<FreezingVoteToken> EmitFreezingVoteForWebContents(
+    content::WebContents* content,
+    FreezingVoteValue vote_value,
+    const char* vote_reason);
+
 }  // namespace freezing
 }  // namespace performance_manager
 
diff --git a/components/performance_manager/public/voting/voting.h b/components/performance_manager/public/voting/voting.h
index 7e47e5c..071ba2af 100644
--- a/components/performance_manager/public/voting/voting.h
+++ b/components/performance_manager/public/voting/voting.h
@@ -486,6 +486,9 @@
   // Returns true if the underlying VotingChannel is valid.
   bool IsValid() const;
 
+  // Checks whether or not there's a vote associated with |context|.
+  bool HasVoteForContext(const ContextType* context);
+
   VoterId<VoteImpl> voter_id() const { return voting_channel_.voter_id(); }
 
  private:
@@ -1034,6 +1037,12 @@
   return voting_channel_.IsValid();
 }
 
+template <class VoteImpl>
+bool VotingChannelWrapper<VoteImpl>::HasVoteForContext(
+    const ContextType* context) {
+  return base::Contains(vote_receipts_, context);
+}
+
 }  // namespace voting
 }  // namespace performance_manager
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 82eb9dbe..7e7aed6 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -23420,7 +23420,7 @@
 
       If this policy is enabled or not set the <ph name="PRODUCT_NAME">User-Agent Client Hints</ph> feature is enabled. If the policy is disabled the feature is unavailable.
 
-      This enterprise policy is for short-term adaptation and will be removed in Chrome 88.''',
+      This enterprise policy is for short-term adaptation purposes and will be available at least until Chrome 91.''',
     },
     {
       'name': 'SuggestedContentEnabled',
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index 98d52b4..50a8037 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -25,6 +25,8 @@
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/base/signin_switches.h"
 
+using signin::PrimaryAccountChangeEvent;
+
 PrimaryAccountManager::PrimaryAccountManager(
     SigninClient* client,
     ProfileOAuth2TokenService* token_service,
@@ -155,11 +157,16 @@
   }
 
   bool account_changed = account_info != primary_account_info();
+  PrimaryAccountChangeEvent::State previous_state(
+      primary_account_info(), signin::ConsentLevel::kNotRequired);
   SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/false);
 
   if (account_changed) {
+    PrimaryAccountChangeEvent::State current_state(
+        account_info, signin::ConsentLevel::kNotRequired);
+    PrimaryAccountChangeEvent event_details(previous_state, current_state);
     for (Observer& observer : observers_)
-      observer.UnconsentedPrimaryAccountChanged(primary_account_info());
+      observer.UnconsentedPrimaryAccountChanged(event_details);
   }
 }
 
@@ -243,12 +250,21 @@
   }
 
   bool account_changed = info != primary_account_info();
+  PrimaryAccountChangeEvent::State previous_state(
+      primary_account_info(), signin::ConsentLevel::kNotRequired);
+  PrimaryAccountChangeEvent::State current_state(info,
+                                                 signin::ConsentLevel::kSync);
+  PrimaryAccountChangeEvent event_details(previous_state, current_state);
+
   SetAuthenticatedAccountInfo(info);
 
   for (Observer& observer : observers_) {
+    // TODO(https://crbug.com/1158855): Remove call to
+    // UnconsentedPrimaryAccountChanged() after IdentityManager::Observer
+    // migration has been completed.
     if (account_changed)
-      observer.UnconsentedPrimaryAccountChanged(info);
-    observer.GoogleSigninSucceeded(info);
+      observer.UnconsentedPrimaryAccountChanged(event_details);
+    observer.GoogleSigninSucceeded(event_details);
   }
 }
 
@@ -335,9 +351,10 @@
   }
 
   const CoreAccountInfo account_info = primary_account_info();
+  const bool has_sync_consent = HasPrimaryAccount(signin::ConsentLevel::kSync);
   client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
   // Revoke the sync consent.
-  if (HasPrimaryAccount(signin::ConsentLevel::kSync))
+  if (has_sync_consent)
     SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/false);
 
   DCHECK(!HasPrimaryAccount(signin::ConsentLevel::kSync));
@@ -357,8 +374,15 @@
       break;
   }
 
+  PrimaryAccountChangeEvent::State previous_state;
+  previous_state.primary_account = account_info;
+  previous_state.consent_level = has_sync_consent
+                                     ? signin::ConsentLevel::kSync
+                                     : signin::ConsentLevel::kNotRequired;
+  PrimaryAccountChangeEvent event_details(previous_state,
+                                          PrimaryAccountChangeEvent::State());
   for (Observer& observer : observers_)
-    observer.GoogleSignedOut(account_info);
+    observer.GoogleSignedOut(event_details);
 }
 
 void PrimaryAccountManager::OnRefreshTokensLoaded() {
diff --git a/components/signin/internal/identity_manager/primary_account_manager.h b/components/signin/internal/identity_manager/primary_account_manager.h
index 2d4d490..b05fb4c 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.h
+++ b/components/signin/internal/identity_manager/primary_account_manager.h
@@ -30,6 +30,7 @@
 #include "components/signin/public/base/signin_client.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/consent_level.h"
+#include "components/signin/public/identity_manager/primary_account_change_event.h"
 
 class AccountTrackerService;
 class PrefRegistrySimple;
@@ -48,15 +49,17 @@
    public:
     // Called whenever a user signs into Google services such as sync.
     // Not called during a reauth.
-    virtual void GoogleSigninSucceeded(const CoreAccountInfo& info) {}
+    virtual void GoogleSigninSucceeded(
+        const signin::PrimaryAccountChangeEvent& event_details) {}
 
     // Called whenever the unconsented primary account changes. This includes
     // the changes for the consented primary account as well.
-    virtual void UnconsentedPrimaryAccountChanged(const CoreAccountInfo& info) {
-    }
+    virtual void UnconsentedPrimaryAccountChanged(
+        const signin::PrimaryAccountChangeEvent& event_details) {}
 
     // Called whenever the currently signed-in user has been signed out.
-    virtual void GoogleSignedOut(const CoreAccountInfo& info) {}
+    virtual void GoogleSignedOut(
+        const signin::PrimaryAccountChangeEvent& event_details) {}
   };
 
   // Used to remove accounts from the token service and the account tracker.
diff --git a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index e9b42c7..dd98841 100644
--- a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -121,11 +121,13 @@
     EXPECT_EQ(1, num_successful_signins_);
   }
 
-  void GoogleSigninSucceeded(const CoreAccountInfo& account_info) override {
+  void GoogleSigninSucceeded(
+      const signin::PrimaryAccountChangeEvent& account_info) override {
     num_successful_signins_++;
   }
 
-  void UnconsentedPrimaryAccountChanged(const CoreAccountInfo& info) override {
+  void UnconsentedPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& info) override {
     num_unconsented_account_changed_++;
   }
 
@@ -477,4 +479,4 @@
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kNotRequired));
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
\ No newline at end of file
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/signin/public/identity_manager/BUILD.gn b/components/signin/public/identity_manager/BUILD.gn
index 9a65599..5ba621b1 100644
--- a/components/signin/public/identity_manager/BUILD.gn
+++ b/components/signin/public/identity_manager/BUILD.gn
@@ -34,6 +34,8 @@
     "load_credentials_state.h",
     "primary_account_access_token_fetcher.cc",
     "primary_account_access_token_fetcher.h",
+    "primary_account_change_event.cc",
+    "primary_account_change_event.h",
     "primary_account_mutator.h",
     "scope_set.h",
     "set_accounts_in_cookie_result.h",
@@ -100,6 +102,7 @@
     "identity_test_environment_unittest.cc",
     "identity_utils_unittest.cc",
     "primary_account_access_token_fetcher_unittest.cc",
+    "primary_account_change_event_unittest.cc",
     "primary_account_mutator_unittest.cc",
   ]
 
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc
index 37dc9de..53c6ee0 100644
--- a/components/signin/public/identity_manager/identity_manager.cc
+++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -480,9 +480,12 @@
 }
 
 void IdentityManager::GoogleSigninSucceeded(
-    const CoreAccountInfo& account_info) {
+    const PrimaryAccountChangeEvent& event_details) {
+  const CoreAccountInfo& account_info =
+      event_details.GetCurrentState().primary_account;
   for (auto& observer : observer_list_) {
     observer.OnPrimaryAccountSet(account_info);
+    observer.OnPrimaryAccountChanged(event_details);
   }
 #if defined(OS_ANDROID)
   if (java_identity_manager_) {
@@ -495,12 +498,19 @@
 }
 
 void IdentityManager::UnconsentedPrimaryAccountChanged(
-    const CoreAccountInfo& account_info) {
-  for (auto& observer : observer_list_)
+    const PrimaryAccountChangeEvent& event_details) {
+  const CoreAccountInfo& account_info =
+      event_details.GetCurrentState().primary_account;
+  for (auto& observer : observer_list_) {
     observer.OnUnconsentedPrimaryAccountChanged(account_info);
+    observer.OnPrimaryAccountChanged(event_details);
+  }
 }
 
-void IdentityManager::GoogleSignedOut(const CoreAccountInfo& account_info) {
+void IdentityManager::GoogleSignedOut(
+    const PrimaryAccountChangeEvent& event_details) {
+  const CoreAccountInfo& account_info =
+      event_details.GetPreviousState().primary_account;
   DCHECK(!HasPrimaryAccount());
   DCHECK(!account_info.IsEmpty());
   for (auto& observer : observer_list_) {
@@ -509,6 +519,7 @@
 
   for (auto& observer : observer_list_) {
     observer.OnPrimaryAccountCleared(account_info);
+    observer.OnPrimaryAccountChanged(event_details);
   }
 
 #if defined(OS_ANDROID)
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index 307693b..ece5290 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -75,14 +75,21 @@
     Observer(const Observer&) = delete;
     Observer& operator=(const Observer&) = delete;
 
+    // Called when there is a change in the primary account or in the consent
+    // level for the primary account.
+    virtual void OnPrimaryAccountChanged(
+        const PrimaryAccountChangeEvent& event_details) {}
+
     // Called when an account becomes the user's primary account.
     // This method is not called during a reauth.
+    // DEPRECATED: Use OnPrimaryAccountChanged() instead.
     virtual void OnPrimaryAccountSet(
         const CoreAccountInfo& primary_account_info) {}
 
     // Called when the user moves from having a primary account to no longer
     // having a primary account (note that the user may still have an
     // *unconsented* primary account after this event; see./README.md).
+    // DEPRECATED: Use OnPrimaryAccountChanged() instead.
     virtual void OnPrimaryAccountCleared(
         const CoreAccountInfo& previous_primary_account_info) {}
 
@@ -106,6 +113,7 @@
     // the identity manager does not have clear guarantees that that account
     // cannot change in one atomic operation (without getting cleared in the
     // mean-time).
+    // DEPRECATED: Use OnPrimaryAccountChanged() instead.
     virtual void OnUnconsentedPrimaryAccountChanged(
         const CoreAccountInfo& unconsented_primary_account_info) {}
 
@@ -627,10 +635,11 @@
       const CoreAccountId& account_id) const;
 
   // PrimaryAccountManager::Observer:
-  void GoogleSigninSucceeded(const CoreAccountInfo& account_info) override;
+  void GoogleSigninSucceeded(
+      const PrimaryAccountChangeEvent& event_details) override;
   void UnconsentedPrimaryAccountChanged(
-      const CoreAccountInfo& account_info) override;
-  void GoogleSignedOut(const CoreAccountInfo& account_info) override;
+      const PrimaryAccountChangeEvent& event_details) override;
+  void GoogleSignedOut(const PrimaryAccountChangeEvent& event_details) override;
 
   // ProfileOAuth2TokenServiceObserver:
   void OnRefreshTokenAvailable(const CoreAccountId& account_id) override;
diff --git a/components/signin/public/identity_manager/primary_account_change_event.cc b/components/signin/public/identity_manager/primary_account_change_event.cc
new file mode 100644
index 0000000..890ef755
--- /dev/null
+++ b/components/signin/public/identity_manager/primary_account_change_event.cc
@@ -0,0 +1,80 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/public/identity_manager/primary_account_change_event.h"
+
+namespace signin {
+
+PrimaryAccountChangeEvent::State::State() = default;
+
+PrimaryAccountChangeEvent::State::State(const State& other) = default;
+
+PrimaryAccountChangeEvent::State::State(CoreAccountInfo account_info,
+                                        ConsentLevel consent_level)
+    : primary_account(account_info), consent_level(consent_level) {}
+
+PrimaryAccountChangeEvent::State::~State() = default;
+
+PrimaryAccountChangeEvent::State& PrimaryAccountChangeEvent::State::operator=(
+    const State& other) = default;
+
+PrimaryAccountChangeEvent::PrimaryAccountChangeEvent() = default;
+
+PrimaryAccountChangeEvent::PrimaryAccountChangeEvent(State previous_state,
+                                                     State current_state)
+    : previous_state_(previous_state), current_state_(current_state) {}
+
+PrimaryAccountChangeEvent::~PrimaryAccountChangeEvent() = default;
+
+PrimaryAccountChangeEvent::Type PrimaryAccountChangeEvent::GetEventTypeFor(
+    ConsentLevel consent_level) const {
+  if (previous_state_ == current_state_)
+    return Type::kNone;
+
+  if (previous_state_.consent_level == ConsentLevel::kSync) {
+    // Cannot change the Sync account without signing out first.
+    DCHECK(previous_state_.primary_account == current_state_.primary_account ||
+           current_state_.primary_account.IsEmpty());
+  }
+  if (previous_state_.primary_account == current_state_.primary_account) {
+    // Cannot change the consent level for the empty account.
+    DCHECK(!previous_state_.primary_account.IsEmpty());
+  }
+
+  switch (consent_level) {
+    case ConsentLevel::kNotRequired:
+      if (previous_state_.primary_account != current_state_.primary_account) {
+        return current_state_.primary_account.IsEmpty() ? Type::kCleared
+                                                        : Type::kSet;
+      }
+      return Type::kNone;
+    case ConsentLevel::kSync:
+      if (previous_state_.consent_level != current_state_.consent_level) {
+        return current_state_.consent_level == ConsentLevel::kSync
+                   ? Type::kSet
+                   : Type::kCleared;
+      }
+      // Cannot change the Sync account without signing out first.
+      DCHECK_EQ(current_state_.consent_level, ConsentLevel::kNotRequired);
+      return Type::kNone;
+  }
+}
+
+const PrimaryAccountChangeEvent::State&
+PrimaryAccountChangeEvent::GetCurrentState() const {
+  return current_state_;
+}
+
+const PrimaryAccountChangeEvent::State&
+PrimaryAccountChangeEvent::GetPreviousState() const {
+  return previous_state_;
+}
+
+bool operator==(const PrimaryAccountChangeEvent::State& lhs,
+                const PrimaryAccountChangeEvent::State& rhs) {
+  return lhs.primary_account == rhs.primary_account &&
+         lhs.consent_level == rhs.consent_level;
+}
+
+}  // namespace signin
\ No newline at end of file
diff --git a/components/signin/public/identity_manager/primary_account_change_event.h b/components/signin/public/identity_manager/primary_account_change_event.h
new file mode 100644
index 0000000..8ca54ae
--- /dev/null
+++ b/components/signin/public/identity_manager/primary_account_change_event.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_CHANGE_EVENT_H_
+#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_CHANGE_EVENT_H_
+
+#include "components/signin/public/identity_manager/account_info.h"
+#include "components/signin/public/identity_manager/consent_level.h"
+
+namespace signin {
+
+class PrimaryAccountChangeEvent {
+ public:
+  // Used to denote the type of the change event.
+  enum class Type {
+    // No change.
+    kNone = 0,
+    // Primary account set or changed.
+    kSet,
+    // Primary account cleared.
+    kCleared
+  };
+
+  struct State {
+    State();
+    State(const State& other);
+    State(CoreAccountInfo account_info, ConsentLevel consent_level);
+    ~State();
+
+    State& operator=(const State& other);
+
+    CoreAccountInfo primary_account;
+    ConsentLevel consent_level = ConsentLevel::kNotRequired;
+  };
+
+  PrimaryAccountChangeEvent();
+  PrimaryAccountChangeEvent(State previous_state, State current_state);
+  ~PrimaryAccountChangeEvent();
+
+  // Returns primary account change event type for the corresponding
+  // consent_level. There can be 3 different event types.
+  // kNone - No change in primary account for the given consent_level.
+  // kSet - A new primary account is set or changed for the given consent_level.
+  // kCleared - The primary account set for the consent level is cleared.
+  Type GetEventTypeFor(ConsentLevel consent_level) const;
+
+  const State& GetPreviousState() const;
+  const State& GetCurrentState() const;
+
+ private:
+  State previous_state_, current_state_;
+};
+
+bool operator==(const PrimaryAccountChangeEvent::State& lhs,
+                const PrimaryAccountChangeEvent::State& rhs);
+
+}  // namespace signin
+
+#endif  // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_CHANGE_EVENT_H_
diff --git a/components/signin/public/identity_manager/primary_account_change_event_unittest.cc b/components/signin/public/identity_manager/primary_account_change_event_unittest.cc
new file mode 100644
index 0000000..33866c8
--- /dev/null
+++ b/components/signin/public/identity_manager/primary_account_change_event_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/public/identity_manager/primary_account_change_event.h"
+#include "components/signin/public/identity_manager/consent_level.h"
+#include "google_apis/gaia/core_account_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using signin::ConsentLevel;
+using signin::PrimaryAccountChangeEvent;
+using Type = signin::PrimaryAccountChangeEvent::Type;
+using State = signin::PrimaryAccountChangeEvent::State;
+
+class PrimaryAccountChangeEventTest : public testing::Test {
+ public:
+  PrimaryAccountChangeEventTest() {
+    CoreAccountInfo account_info1 = GetCoreAccountInfoFrom("account1@test.com");
+    CoreAccountInfo account_info2 = GetCoreAccountInfoFrom("account2@test.com");
+
+    empty_not_required_ = State(CoreAccountInfo(), ConsentLevel::kNotRequired);
+    account1_not_required_ = State(account_info1, ConsentLevel::kNotRequired);
+    account2_not_required_ = State(account_info2, ConsentLevel::kNotRequired);
+    account1_sync_ = State(account_info1, ConsentLevel::kSync);
+    account2_sync_ = State(account_info2, ConsentLevel::kSync);
+  }
+
+  State empty_not_required_;
+  State account1_not_required_;
+  State account2_not_required_;
+  State account1_sync_;
+  State account2_sync_;
+
+ private:
+  CoreAccountInfo GetCoreAccountInfoFrom(const char* account_name) {
+    CoreAccountInfo account_info;
+    account_info.account_id = CoreAccountId(account_name);
+    account_info.gaia = account_info.email = account_name;
+
+    return account_info;
+  }
+};
+
+TEST_F(PrimaryAccountChangeEventTest, NoStateChange) {
+  PrimaryAccountChangeEvent event(empty_not_required_, empty_not_required_);
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event =
+      PrimaryAccountChangeEvent(account1_not_required_, account1_not_required_);
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event = PrimaryAccountChangeEvent(account1_sync_, account1_sync_);
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kSync));
+}
+
+TEST_F(PrimaryAccountChangeEventTest,
+       ConsentLevelChangeFromNotRequiredToNotRequired) {
+  PrimaryAccountChangeEvent event(empty_not_required_, account1_not_required_);
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event =
+      PrimaryAccountChangeEvent(account1_not_required_, account2_not_required_);
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event =
+      PrimaryAccountChangeEvent(account1_not_required_, empty_not_required_);
+  EXPECT_EQ(Type::kCleared, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kSync));
+}
+
+TEST_F(PrimaryAccountChangeEventTest, ConsentLevelChangeFromNotRequiredToSync) {
+  PrimaryAccountChangeEvent event(empty_not_required_, account1_sync_);
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event = PrimaryAccountChangeEvent(account1_not_required_, account1_sync_);
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event = PrimaryAccountChangeEvent(account1_not_required_, account2_sync_);
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kSet, event.GetEventTypeFor(ConsentLevel::kSync));
+}
+
+TEST_F(PrimaryAccountChangeEventTest, ConsentLevelChangeFromSyncToNotRequired) {
+  PrimaryAccountChangeEvent event(account1_sync_, account1_not_required_);
+  EXPECT_EQ(Type::kNone, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kCleared, event.GetEventTypeFor(ConsentLevel::kSync));
+
+  event = PrimaryAccountChangeEvent(account1_sync_, empty_not_required_);
+  EXPECT_EQ(Type::kCleared, event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(Type::kCleared, event.GetEventTypeFor(ConsentLevel::kSync));
+}
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index bbb65761..5179bb69 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -14,6 +14,8 @@
     "content_activation_list_utils.h",
     "content_subresource_filter_throttle_manager.cc",
     "content_subresource_filter_throttle_manager.h",
+    "devtools_interaction_tracker.cc",
+    "devtools_interaction_tracker.h",
     "navigation_console_logger.cc",
     "navigation_console_logger.h",
     "page_load_statistics.cc",
diff --git a/components/subresource_filter/content/browser/devtools_interaction_tracker.cc b/components/subresource_filter/content/browser/devtools_interaction_tracker.cc
new file mode 100644
index 0000000..fd6c9feb
--- /dev/null
+++ b/components/subresource_filter/content/browser/devtools_interaction_tracker.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/subresource_filter/content/browser/devtools_interaction_tracker.h"
+
+#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
+
+namespace subresource_filter {
+
+DevtoolsInteractionTracker::DevtoolsInteractionTracker(
+    content::WebContents* web_contents) {}
+
+DevtoolsInteractionTracker::~DevtoolsInteractionTracker() = default;
+
+void DevtoolsInteractionTracker::ToggleForceActivation(bool force_activation) {
+  if (!activated_via_devtools_ && force_activation)
+    ContentSubresourceFilterThrottleManager::LogAction(
+        SubresourceFilterAction::kForcedActivationEnabled);
+  activated_via_devtools_ = force_activation;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(DevtoolsInteractionTracker)
+
+}  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/devtools_interaction_tracker.h b/components/subresource_filter/content/browser/devtools_interaction_tracker.h
new file mode 100644
index 0000000..85481aa
--- /dev/null
+++ b/components/subresource_filter/content/browser/devtools_interaction_tracker.h
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_DEVTOOLS_INTERACTION_TRACKER_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_DEVTOOLS_INTERACTION_TRACKER_H_
+
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace subresource_filter {
+
+// Can be used to track whether forced activation has been set by devtools
+// within a given WebContents.
+// Scoped to the lifetime of a WebContents.
+class DevtoolsInteractionTracker
+    : public content::WebContentsUserData<DevtoolsInteractionTracker> {
+ public:
+  explicit DevtoolsInteractionTracker(content::WebContents* web_contents);
+  ~DevtoolsInteractionTracker() override;
+
+  DevtoolsInteractionTracker(const DevtoolsInteractionTracker&) = delete;
+  DevtoolsInteractionTracker& operator=(const DevtoolsInteractionTracker&) =
+      delete;
+
+  // Should be called by devtools in response to a protocol command to enable ad
+  // blocking in this WebContents. Should only persist while devtools is
+  // attached.
+  void ToggleForceActivation(bool force_activation);
+
+  bool activated_via_devtools() { return activated_via_devtools_; }
+
+ private:
+  friend class content::WebContentsUserData<DevtoolsInteractionTracker>;
+
+  // Corresponds to a devtools command which triggers filtering on all page
+  // loads. We must be careful to ensure this boolean does not persist after the
+  // devtools window is closed, which should be handled by the devtools system.
+  bool activated_via_devtools_ = false;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace subresource_filter
+
+#endif  // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_DEVTOOLS_INTERACTION_TRACKER_H_
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
index 4d1124f6..3f444aa 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
@@ -15,6 +15,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "components/subresource_filter/content/browser/content_activation_list_utils.h"
+#include "components/subresource_filter/content/browser/devtools_interaction_tracker.h"
 #include "components/subresource_filter/content/browser/navigation_console_logger.h"
 #include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
@@ -194,8 +195,17 @@
     activation_level = mojom::ActivationLevel::kDisabled;
   }
 
+  auto* devtools_interaction_tracker =
+      DevtoolsInteractionTracker::FromWebContents(
+          navigation_handle()->GetWebContents());
+
+  if (devtools_interaction_tracker &&
+      devtools_interaction_tracker->activated_via_devtools()) {
+    activation_level = mojom::ActivationLevel::kEnabled;
+    activation_decision = ActivationDecision::FORCED_ACTIVATION;
+  }
+
   // Let the embedder get the last word when it comes to activation level.
-  // TODO(csharrison): Move all ActivationDecision code to the embedder.
   activation_level = client_->OnPageActivationComputed(
       navigation_handle(), activation_level, &activation_decision);
 
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index a53efc30..08f1cc5 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "components/safe_browsing/core/db/test_database_manager.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
+#include "components/subresource_filter/content/browser/devtools_interaction_tracker.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
@@ -66,6 +67,7 @@
     "SubresourceFilter.SafeBrowsing.TotalCheckTime";
 const char kActivationListHistogram[] =
     "SubresourceFilter.PageLoad.ActivationList";
+const char kSubresourceFilterActionsHistogram[] = "SubresourceFilter.Actions2";
 
 class MockSubresourceFilterClient : public SubresourceFilterClient {
  public:
@@ -87,7 +89,6 @@
   }
 
   MOCK_METHOD0(ShowNotification, void());
-  MOCK_METHOD0(ForceActivationInCurrentWebContents, bool());
   MOCK_METHOD2(OnAdsViolationTriggered,
                void(content::RenderFrameHost*,
                     subresource_filter::mojom::AdsViolation));
@@ -720,6 +721,63 @@
   }
 }
 
+TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
+       ToggleForceActivation) {
+  auto* web_contents = RenderViewHostTestHarness::web_contents();
+  DevtoolsInteractionTracker::CreateForWebContents(web_contents);
+  auto* devtools_interaction_tracker =
+      DevtoolsInteractionTracker::FromWebContents(web_contents);
+
+  base::HistogramTester histogram_tester;
+  const GURL url("https://example.test/");
+
+  // Navigate initially, should be no activation.
+  SimulateNavigateAndCommit({url}, main_rfh());
+  EXPECT_CALL(*client(), ShowNotification()).Times(0);
+  EXPECT_TRUE(CreateAndNavigateDisallowedSubframe(main_rfh()));
+
+  // Simulate opening devtools and forcing activation.
+  devtools_interaction_tracker->ToggleForceActivation(true);
+  histogram_tester.ExpectBucketCount(
+      kSubresourceFilterActionsHistogram,
+      subresource_filter::SubresourceFilterAction::kForcedActivationEnabled, 1);
+
+  SimulateNavigateAndCommit({url}, main_rfh());
+  EXPECT_CALL(*client(), ShowNotification()).Times(1);
+  EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
+
+  histogram_tester.ExpectBucketCount(
+      "SubresourceFilter.PageLoad.ActivationDecision",
+      subresource_filter::ActivationDecision::FORCED_ACTIVATION, 1);
+
+  // Simulate closing devtools.
+  devtools_interaction_tracker->ToggleForceActivation(false);
+
+  SimulateNavigateAndCommit({url}, main_rfh());
+  EXPECT_TRUE(CreateAndNavigateDisallowedSubframe(main_rfh()));
+  histogram_tester.ExpectBucketCount(
+      kSubresourceFilterActionsHistogram,
+      subresource_filter::SubresourceFilterAction::kForcedActivationEnabled, 1);
+}
+
+TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
+       ToggleOffForceActivation_AfterCommit) {
+  auto* web_contents = RenderViewHostTestHarness::web_contents();
+  DevtoolsInteractionTracker::CreateForWebContents(web_contents);
+  auto* devtools_interaction_tracker =
+      DevtoolsInteractionTracker::FromWebContents(web_contents);
+
+  base::HistogramTester histogram_tester;
+  devtools_interaction_tracker->ToggleForceActivation(true);
+  const GURL url("https://example.test/");
+  SimulateNavigateAndCommit({url}, main_rfh());
+  devtools_interaction_tracker->ToggleForceActivation(false);
+
+  // Resource should be disallowed, since navigation commit had activation.
+  EXPECT_CALL(*client(), ShowNotification()).Times(1);
+  EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
+}
+
 TEST_P(SubresourceFilterSafeBrowsingActivationThrottleScopeTest,
        ActivateForScopeType) {
   const ActivationScopeTestData& test_data = GetParam();
diff --git a/components/sync/base/user_selectable_type.cc b/components/sync/base/user_selectable_type.cc
index b82e5dd8..2085c746 100644
--- a/components/sync/base/user_selectable_type.cc
+++ b/components/sync/base/user_selectable_type.cc
@@ -196,13 +196,6 @@
   return GetUserSelectableTypeInfo(type).canonical_model_type;
 }
 
-int UserSelectableTypeToHistogramInt(UserSelectableType type) {
-  // TODO(crbug.com/1007293): Use ModelTypeHistogramValue instead of casting to
-  // int.
-  return static_cast<int>(
-      ModelTypeHistogramValue(UserSelectableTypeToCanonicalModelType(type)));
-}
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 const char* GetUserSelectableOsTypeName(UserSelectableOsType type) {
   return GetUserSelectableOsTypeInfo(type).type_name;
diff --git a/components/sync/base/user_selectable_type.h b/components/sync/base/user_selectable_type.h
index 2b918df..629b877 100644
--- a/components/sync/base/user_selectable_type.h
+++ b/components/sync/base/user_selectable_type.h
@@ -43,11 +43,6 @@
 ModelTypeSet UserSelectableTypeToAllModelTypes(UserSelectableType type);
 
 ModelType UserSelectableTypeToCanonicalModelType(UserSelectableType type);
-int UserSelectableTypeToHistogramInt(UserSelectableType type);
-
-constexpr int UserSelectableTypeHistogramNumEntries() {
-  return static_cast<int>(ModelType::NUM_ENTRIES);
-}
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Chrome OS provides a separate UI with sync controls for OS data types. Note
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 9910e61..d6dd6d2 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -196,10 +196,8 @@
              << invalidation.version() << ", last seen version was "
              << last_invalidation->second;
     redundant_invalidation = true;
-    // TODO(crbug.com/1158476): ModelTypeHistogramValue() should be used instead
-    // of |type|, this is incorrect. Deprecate and add a new correct histogram.
-    UMA_HISTOGRAM_ENUMERATION("Sync.RedundantInvalidationPerModelType", type,
-                              static_cast<int>(syncer::ModelType::NUM_ENTRIES));
+    UMA_HISTOGRAM_ENUMERATION("Sync.RedundantInvalidationPerModelType2",
+                              ModelTypeHistogramValue(type));
   }
 
   return !fcm_invalidation && redundant_invalidation;
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 8c03a77..cadc4a8b 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -1330,22 +1330,22 @@
     kTransport = 1,
     kMaxValue = kTransport
   };
-  UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureDataTypeManagerOption",
-                            use_transport_only_mode
-                                ? ConfigureDataTypeManagerOption::kTransport
-                                : ConfigureDataTypeManagerOption::kFeature);
+  base::UmaHistogramEnumeration("Sync.ConfigureDataTypeManagerOption",
+                                use_transport_only_mode
+                                    ? ConfigureDataTypeManagerOption::kTransport
+                                    : ConfigureDataTypeManagerOption::kFeature);
 
   // Only if it's the full Sync feature, also record the user's choice of data
   // types.
   if (!use_transport_only_mode) {
     bool sync_everything = sync_prefs_.HasKeepEverythingSynced();
-    UMA_HISTOGRAM_BOOLEAN("Sync.SyncEverything2", sync_everything);
+    base::UmaHistogramBoolean("Sync.SyncEverything2", sync_everything);
 
     if (!sync_everything) {
       for (UserSelectableType type : user_settings_->GetSelectedTypes()) {
-        UMA_HISTOGRAM_ENUMERATION("Sync.CustomSync2",
-                                  UserSelectableTypeToHistogramInt(type),
-                                  UserSelectableTypeHistogramNumEntries());
+        ModelTypeForHistograms canonical_model_type = ModelTypeHistogramValue(
+            UserSelectableTypeToCanonicalModelType(type));
+        base::UmaHistogramEnumeration("Sync.CustomSync3", canonical_model_type);
       }
     }
   }
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc
index 0e69ef3..d416607b 100644
--- a/components/sync/engine/net/http_bridge.cc
+++ b/components/sync/engine/net/http_bridge.cc
@@ -373,8 +373,6 @@
                            fetch_state_.request_succeeded
                                ? fetch_state_.http_status_code
                                : fetch_state_.net_error_code);
-  UMA_HISTOGRAM_LONG_TIMES("Sync.URLFetchTime",
-                           fetch_state_.end_time - fetch_state_.start_time);
 
   // Use a real (non-debug) log to facilitate troubleshooting in the wild.
   VLOG(2) << "HttpBridge::OnURLFetchComplete for: " << final_url.spec();
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc
index e2d12f9..2717618e 100644
--- a/components/sync_preferences/pref_model_associator.cc
+++ b/components/sync_preferences/pref_model_associator.cc
@@ -17,7 +17,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "build/chromeos_buildflags.h"
@@ -629,8 +628,6 @@
       // done on a higher level.
       user_pref_store_->RemoveValue(
           pref_name, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-      UMA_HISTOGRAM_BOOLEAN("Sync.Preferences.ClearedLocalPrefOnTypeMismatch",
-                            true);
     }
   }
 }
diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc
index 87eff95..e417c89 100644
--- a/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "build/chromeos_buildflags.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_notifier_impl.h"
@@ -637,7 +636,6 @@
 };
 
 TEST_F(PrefServiceSyncableMergeTest, RegisterShouldClearTypeMismatchingData) {
-  base::HistogramTester histogram_tester;
   const std::string pref_name = "testing.pref";
   user_prefs_->SetString(pref_name, "string_value");
   ASSERT_TRUE(user_prefs_->GetValue(pref_name, nullptr));
@@ -653,8 +651,6 @@
   EXPECT_TRUE(GetPreferenceValue(pref_name).GetList().empty());
   EXPECT_FALSE(user_prefs_->GetValue(pref_name, nullptr));
 
-  histogram_tester.ExpectBucketCount(
-      "Sync.Preferences.ClearedLocalPrefOnTypeMismatch", true, 1);
   prefs_.RemoveSyncedPrefObserver(pref_name, &observer);
 }
 
diff --git a/content/browser/devtools/devtools_video_consumer_browsertest.cc b/content/browser/devtools/devtools_video_consumer_browsertest.cc
index f9227e57..50d131e 100644
--- a/content/browser/devtools/devtools_video_consumer_browsertest.cc
+++ b/content/browser/devtools/devtools_video_consumer_browsertest.cc
@@ -87,7 +87,7 @@
 // Tests that setting new frame dimensions via SetMinAndMaxFrameSizes
 // produces frames of the new dimensions.
 IN_PROC_BROWSER_TEST_F(DevToolsVideoConsumerTest,
-                       SetMinAndMaxFramesChangesDimensions) {
+                       DISABLED_SetMinAndMaxFramesChangesDimensions) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Complete navigation to a page and then start capture. Since navigation is
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 1b892f9..6c0bb3d6 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8882,25 +8882,7 @@
   // renderer one. The browser will just "push" the correct value.
   if (navigation_request->state() >=
       NavigationRequest::NavigationState::WILL_PROCESS_RESPONSE) {
-    if (params.sandbox_flags != navigation_request->SandboxFlagsToCommit()) {
-      DCHECK(false);
-
-      base::debug::ScopedCrashKeyString scoped_url(
-          base::debug::AllocateCrashKeyString(
-              "url", base::debug::CrashKeySize::Size256),
-          params.url.possibly_invalid_spec());
-      base::debug::ScopedCrashKeyString scoped_sandbox(
-          base::debug::AllocateCrashKeyString(
-              "sandbox", base::debug::CrashKeySize::Size256),
-          base::StringPrintf(
-              "%u, %u", uint32_t(params.sandbox_flags),
-              uint32_t(navigation_request->SandboxFlagsToCommit())));
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_main_frame", base::debug::CrashKeySize::Size32),
-          frame_tree_node_->IsMainFrame() ? "true" : "false");
-      base::debug::DumpWithoutCrashing();
-    }
+    DCHECK_EQ(params.sandbox_flags, navigation_request->SandboxFlagsToCommit());
   }
 
   coep_reporter_ = navigation_request->TakeCoepReporter();
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 8fa5af5..7366e180 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -116,7 +116,11 @@
       return;
     }
     case IdpNetworkRequestManager::FetchStatus::kFetchError: {
-      CompleteRequest(RequestIdTokenStatus::kError, "");
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingWellKnown, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kInvalidResponseError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorInvalidWellKnown, "");
       return;
     }
     case IdpNetworkRequestManager::FetchStatus::kSuccess: {
@@ -187,7 +191,11 @@
       return;
     }
     case IdpNetworkRequestManager::SigninResponse::kSigninError: {
-      CompleteRequest(RequestIdTokenStatus::kError, "");
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingSignin, "");
+      return;
+    }
+    case IdpNetworkRequestManager::SigninResponse::kInvalidResponseError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorInvalidSigninResponse, "");
       return;
     }
   }
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 4117e73..5f0d5cc 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -230,7 +230,7 @@
     data_decoder::DataDecoder::ValueOrError result) {
   if (!result.value) {
     std::move(idp_well_known_callback_)
-        .Run(FetchStatus::kFetchError, std::string());
+        .Run(FetchStatus::kInvalidResponseError, std::string());
     return;
   }
 
@@ -238,7 +238,7 @@
 
   if (!response.is_dict()) {
     std::move(idp_well_known_callback_)
-        .Run(FetchStatus::kFetchError, std::string());
+        .Run(FetchStatus::kInvalidResponseError, std::string());
     return;
   }
 
@@ -246,7 +246,7 @@
 
   if (!idp_endpoint || !idp_endpoint->is_string()) {
     std::move(idp_well_known_callback_)
-        .Run(FetchStatus::kFetchError, std::string());
+        .Run(FetchStatus::kInvalidResponseError, std::string());
     return;
   }
 
@@ -278,7 +278,7 @@
     data_decoder::DataDecoder::ValueOrError result) {
   if (!result.value) {
     std::move(signin_request_callback_)
-        .Run(SigninResponse::kSigninError, std::string());
+        .Run(SigninResponse::kInvalidResponseError, std::string());
     return;
   }
 
@@ -286,7 +286,7 @@
 
   if (!response.is_dict()) {
     std::move(signin_request_callback_)
-        .Run(SigninResponse::kSigninError, std::string());
+        .Run(SigninResponse::kInvalidResponseError, std::string());
     return;
   }
 
@@ -302,7 +302,7 @@
   bool both_present = signin_url_present && token_present;
   if (!(signin_url_present || token_present) || both_present) {
     std::move(signin_request_callback_)
-        .Run(SigninResponse::kSigninError, std::string());
+        .Run(SigninResponse::kInvalidResponseError, std::string());
     return;
   }
 
diff --git a/content/browser/webid/idp_network_request_manager.h b/content/browser/webid/idp_network_request_manager.h
index dbf7958..2833ba1 100644
--- a/content/browser/webid/idp_network_request_manager.h
+++ b/content/browser/webid/idp_network_request_manager.h
@@ -54,12 +54,14 @@
     kSuccess,
     kWebIdNotSupported,
     kFetchError,
+    kInvalidResponseError,
   };
 
   enum class SigninResponse {
     kLoadIdp,
     kTokenGranted,
     kSigninError,
+    kInvalidResponseError,
   };
 
   using FetchWellKnownCallback =
diff --git a/content/test/data/accessibility/html/img-expected-android.txt b/content/test/data/accessibility/html/img-expected-android.txt
index c58bed9..75a396a 100644
--- a/content/test/data/accessibility/html/img-expected-android.txt
+++ b/content/test/data/accessibility/html/img-expected-android.txt
@@ -3,4 +3,6 @@
 ++++android.widget.Image role_description='graphic' has_image name='pipe'
 ++++android.widget.TextView name=' '
 ++++android.widget.TextView name=' '
-++++android.widget.Image role_description='graphic' has_image name='  '
\ No newline at end of file
+++++android.widget.Image role_description='graphic' has_image name='  '
+++++android.widget.TextView name=' '
+++++android.widget.Image role_description='graphic' has_image name='SVG face'
diff --git a/content/test/data/accessibility/html/img-expected-auralinux.txt b/content/test/data/accessibility/html/img-expected-auralinux.txt
index fc193e0..715f68f 100644
--- a/content/test/data/accessibility/html/img-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/img-expected-auralinux.txt
@@ -4,3 +4,5 @@
 ++++[static] name=' '
 ++++[static] name=' '
 ++++[image] name='  ' xml-roles:img
+++++[static] name=' '
+++++[image] name='SVG face' xml-roles:img
diff --git a/content/test/data/accessibility/html/img-expected-blink.txt b/content/test/data/accessibility/html/img-expected-blink.txt
index 98185c0e..c90e4cd 100644
--- a/content/test/data/accessibility/html/img-expected-blink.txt
+++ b/content/test/data/accessibility/html/img-expected-blink.txt
@@ -7,3 +7,5 @@
 ++++++staticText name=' '
 ++++++++inlineTextBox name=' '
 ++++++image name='  '
+++++++staticText name=' '
+++++++image name='SVG face'
diff --git a/content/test/data/accessibility/html/img-expected-mac.txt b/content/test/data/accessibility/html/img-expected-mac.txt
index bb9aa663..5f952574 100644
--- a/content/test/data/accessibility/html/img-expected-mac.txt
+++ b/content/test/data/accessibility/html/img-expected-mac.txt
@@ -4,3 +4,5 @@
 ++++AXStaticText AXValue=' '
 ++++AXStaticText AXValue=' '
 ++++AXImage AXDescription='  ' AXRoleDescription='image'
+++++AXStaticText AXValue=' '
+++++AXImage AXDescription='SVG face' AXRoleDescription='image'
diff --git a/content/test/data/accessibility/html/img-expected-uia-win.txt b/content/test/data/accessibility/html/img-expected-uia-win.txt
index e14113e..8a066b7 100644
--- a/content/test/data/accessibility/html/img-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-expected-uia-win.txt
@@ -4,3 +4,5 @@
 ++++Text Name=' '
 ++++Text Name=' '
 ++++Image Name='  '
+++++Text Name=' '
+++++Image Name='SVG face'
diff --git a/content/test/data/accessibility/html/img-expected-win.txt b/content/test/data/accessibility/html/img-expected-win.txt
index af254395..f8012a1 100644
--- a/content/test/data/accessibility/html/img-expected-win.txt
+++ b/content/test/data/accessibility/html/img-expected-win.txt
@@ -4,3 +4,5 @@
 ++++ROLE_SYSTEM_STATICTEXT name=' '
 ++++ROLE_SYSTEM_STATICTEXT name=' '
 ++++ROLE_SYSTEM_GRAPHIC name='  ' READONLY xml-roles:img
+++++ROLE_SYSTEM_STATICTEXT name=' '
+++++ROLE_SYSTEM_GRAPHIC name='SVG face' READONLY xml-roles:img
diff --git a/content/test/data/accessibility/html/img.html b/content/test/data/accessibility/html/img.html
index 39cea3a..33e904a7 100644
--- a/content/test/data/accessibility/html/img.html
+++ b/content/test/data/accessibility/html/img.html
@@ -9,5 +9,6 @@
   <img src="pipe.jpg" alt="pipe">
   <img src="pipe.jpg" alt="">
   <img src="pipe.jpg" alt=" ">
+  <img src="svg-face.svg" alt="SVG face">
 </body>
 </html>
diff --git a/third_party/blink/web_tests/accessibility/resources/svg-face.svg b/content/test/data/accessibility/html/svg-face.svg
similarity index 100%
rename from third_party/blink/web_tests/accessibility/resources/svg-face.svg
rename to content/test/data/accessibility/html/svg-face.svg
diff --git a/content/test/data/accessibility/html/svg-style-element-expected-blink.txt b/content/test/data/accessibility/html/svg-style-element-expected-blink.txt
index e874f9c3..c8654017 100644
--- a/content/test/data/accessibility/html/svg-style-element-expected-blink.txt
+++ b/content/test/data/accessibility/html/svg-style-element-expected-blink.txt
@@ -3,6 +3,5 @@
 ++++genericContainer ignored
 ++++++genericContainer
 ++++++++button name='Kettle'
-++++++++++presentational
 ++++++++++staticText name='Kettle'
-++++++++++++inlineTextBox name='Kettle'
+++++++++++++inlineTextBox name='Kettle'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-style-element-expected-mac.txt b/content/test/data/accessibility/html/svg-style-element-expected-mac.txt
index 8c3fb3c..9d1f1ba 100644
--- a/content/test/data/accessibility/html/svg-style-element-expected-mac.txt
+++ b/content/test/data/accessibility/html/svg-style-element-expected-mac.txt
@@ -1,5 +1,3 @@
 AXWebArea
 ++AXGroup
-++++AXButton AXTitle='Kettle'
-++++++AXGroup
-++++++AXStaticText AXValue='Kettle'
+++++AXButton AXTitle='Kettle'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-style-element-expected-win.txt b/content/test/data/accessibility/html/svg-style-element-expected-win.txt
index f39ecfaae..2025a8a3 100644
--- a/content/test/data/accessibility/html/svg-style-element-expected-win.txt
+++ b/content/test/data/accessibility/html/svg-style-element-expected-win.txt
@@ -1,5 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_SECTION
 ++++ROLE_SYSTEM_PUSHBUTTON name='Kettle' FOCUSABLE
-++++++ROLE_SYSTEM_PANE
-++++++ROLE_SYSTEM_STATICTEXT name='Kettle'
diff --git a/google_apis/test/embedded_setup_chromeos.html b/google_apis/test/embedded_setup_chromeos.html
index f99d8e73..c38684b 100644
--- a/google_apis/test/embedded_setup_chromeos.html
+++ b/google_apis/test/embedded_setup_chromeos.html
@@ -61,19 +61,9 @@
               passwordBytes: password,
               keyType: 'KEY_TYPE_PASSWORD_PLAIN'}
       }, '/');
-  var services = document.getElementById("services").value;
-  if (services) {
-    services = JSON.parse(services);
-    if (!services)
-      services = "Failed to parse test services JSON.";
-  } else if (email.endsWith('@corp.example.com') ||
-             email.endsWith('@example.test')) {
-    // SAML tests.
-    services = [];
-  } else {
-    services = "Services are not set for testing.";
-  }
+};
 
+gaia.chromeOSLogin.sendUserInfo = function(services) {
   msg = {
     'method': 'userInfo',
     'services': services,
@@ -124,11 +114,24 @@
   } else if (!document.getElementById("page2").hidden) {
     var email = document.getElementById("identifier").value;
     var password = document.getElementById("password").value;
+    var services = document.getElementById("services").value;
+    if (services) {
+      services = JSON.parse(services);
+      if (!services)
+        services = "Failed to parse test services JSON.";
+    } else if (email.endsWith('@corp.example.com') ||
+               email.endsWith('@example.test')) {
+      // SAML tests.
+      services = [];
+    } else {
+      services = "Services are not set for testing.";
+    }
 
     request = new XMLHttpRequest();
     request.open('POST', '/_/embedded/signin/challenge', true);
     request.onreadystatechange = function() {
       if (request.readyState == 4 && request.status == 200) {
+        gaia.chromeOSLogin.sendUserInfo(services);
         history.pushState({}, "", window.location.pathname + "#close");
       }
     };
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_check_manager_unittest.mm b/ios/chrome/browser/passwords/ios_chrome_password_check_manager_unittest.mm
index 5a110ebc..2826913 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_check_manager_unittest.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_check_manager_unittest.mm
@@ -108,7 +108,8 @@
     CompromiseType compromise_type = CompromiseType::kLeaked) {
   return CompromisedCredentials(
       std::string(signon_realm), base::ASCIIToUTF16(username),
-      base::Time::Now() - time_since_creation, compromise_type, false);
+      base::Time::Now() - time_since_creation, compromise_type,
+      password_manager::IsMuted(false));
 }
 
 PasswordForm MakeSavedPassword(
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index 0fdf37c1..0880696e 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -67,6 +67,7 @@
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/voice/voice_search_navigations_tab_helper.h"
 #import "ios/chrome/browser/web/blocked_popup_tab_helper.h"
+#include "ios/chrome/browser/web/error_page_controller_bridge.h"
 #import "ios/chrome/browser/web/features.h"
 #import "ios/chrome/browser/web/font_size_tab_helper.h"
 #import "ios/chrome/browser/web/image_fetch_tab_helper.h"
@@ -120,6 +121,7 @@
       web_state);
   password_manager::WellKnownChangePasswordTabHelper::CreateForWebState(
       web_state);
+  ErrorPageControllerBridge::CreateForWebState(web_state);
 
   if (base::FeatureList::IsEnabled(web::features::kUseJSForErrorPage)) {
     InvalidUrlTabHelper::CreateForWebState(web_state);
diff --git a/ios/chrome/browser/ui/first_run/location_permissions_field_trial.cc b/ios/chrome/browser/ui/first_run/location_permissions_field_trial.cc
index 3c401bd..c5c6ed1 100644
--- a/ios/chrome/browser/ui/first_run/location_permissions_field_trial.cc
+++ b/ios/chrome/browser/ui/first_run/location_permissions_field_trial.cc
@@ -28,10 +28,10 @@
 const char kDisabledGroup[] = "Disabled";
 const char kDefaultGroup[] = "Default";
 // Experiment IDs defined for the above field trial groups.
-const variations::VariationID kDefaultTrialID = 3329331;
-const variations::VariationID kDisabledTrialID = 3329332;
-const variations::VariationID kLocationRemoveFirstRunPromptTrialID = 3329333;
-const variations::VariationID kLocationFirstRunModalTrialID = 3329334;
+const variations::VariationID kDefaultTrialID = 3329335;
+const variations::VariationID kDisabledTrialID = 3329336;
+const variations::VariationID kLocationRemoveFirstRunPromptTrialID = 3329337;
+const variations::VariationID kLocationFirstRunModalTrialID = 3329338;
 
 // Default local state pref value.
 const int kDefaultPrefValue = -1;
diff --git a/ios/chrome/browser/ui/settings/password/password_issues_mediator_unittest.mm b/ios/chrome/browser/ui/settings/password/password_issues_mediator_unittest.mm
index f80d841..7eca44a0 100644
--- a/ios/chrome/browser/ui/settings/password/password_issues_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_issues_mediator_unittest.mm
@@ -58,7 +58,8 @@
                                        base::StringPiece username) {
   return CompromisedCredentials(std::string(signon_realm),
                                 base::ASCIIToUTF16(username), base::Time::Now(),
-                                CompromiseType::kLeaked, false);
+                                CompromiseType::kLeaked,
+                                password_manager::IsMuted(false));
 }
 }  // namespace
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
index 33ee823..15e52851 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller_unittest.mm
@@ -235,7 +235,8 @@
       base::StringPiece username) {
     return password_manager::CompromisedCredentials(
         std::string(signon_realm), base::ASCIIToUTF16(username),
-        base::Time::Now(), CompromiseType::kLeaked, false);
+        base::Time::Now(), CompromiseType::kLeaked,
+        password_manager::IsMuted(false));
   }
 
   void AddCompromisedCredential1() {
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
index 3c0801bd..05381b2 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_mediator_unittest.mm
@@ -199,7 +199,8 @@
       base::StringPiece username) {
     return password_manager::CompromisedCredentials(
         std::string(signon_realm), base::ASCIIToUTF16(username),
-        base::Time::Now(), CompromiseType::kLeaked, false);
+        base::Time::Now(), CompromiseType::kLeaked,
+        password_manager::IsMuted(false));
   }
 
   TestPasswordStore& GetTestStore() {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_strip/BUILD.gn
index 8bab8a6..2a7acb6 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/BUILD.gn
@@ -41,6 +41,7 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui/image_util",
     "//ios/chrome/browser/ui/tab_switcher",
+    "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common/ui/colors",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.h b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.h
index b53ebdf..19ecfbe 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.h
@@ -19,6 +19,9 @@
 // and a close tab button.
 @interface TabStripCell : UICollectionViewCell
 
+// The close button associated with this cell.
+@property(nonatomic, strong) UIButton* closeButton;
+// Title is displayed by this label.
 @property(nonatomic, strong) UILabel* titleLabel;
 // View for displaying the favicon.
 @property(nonatomic, strong) UIImageView* faviconView;
@@ -27,6 +30,10 @@
 @property(nonatomic, copy) NSString* itemIdentifier;
 // Delegate to inform the TabStrip on the cell.
 @property(nonatomic, weak) id<TabStripCellDelegate> delegate;
+// YES if dark mode is needed for incognito on iOS 12 and less.
+// iOS 13 there is no need to pick custom incognito assets because
+// |overrideUserInterfaceStyle| is set to dark mode when in incognito.
+@property(nonatomic) BOOL useIncognitoFallback;
 
 @end
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.mm
index 5c2433a..2729bac 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.mm
@@ -16,6 +16,7 @@
 const CGFloat kTabBackgroundLeftCapInset = 34.0;
 const CGFloat kFaviconInset = 28;
 const CGFloat kTitleInset = 10.0;
+const CGFloat kFontSize = 14.0;
 }  // namespace
 
 @implementation TabStripCell
@@ -26,50 +27,49 @@
 
     UIImage* favicon = [[UIImage imageNamed:@"default_world_favicon"]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-    UIImageView* faviconView = [[UIImageView alloc] initWithImage:favicon];
-    faviconView.tintColor = [UIColor colorNamed:kGrey500Color];
-    [self.contentView addSubview:faviconView];
-    faviconView.translatesAutoresizingMaskIntoConstraints = NO;
+    _faviconView = [[UIImageView alloc] initWithImage:favicon];
+    [self.contentView addSubview:_faviconView];
+    _faviconView.translatesAutoresizingMaskIntoConstraints = NO;
     [NSLayoutConstraint activateConstraints:@[
-      [faviconView.leadingAnchor
+      [_faviconView.leadingAnchor
           constraintEqualToAnchor:self.contentView.leadingAnchor
                          constant:kFaviconInset],
-      [faviconView.centerYAnchor
+      [_faviconView.centerYAnchor
           constraintEqualToAnchor:self.contentView.centerYAnchor],
     ]];
 
     UIImage* close = [[UIImage imageNamed:@"grid_cell_close_button"]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-    UIButton* closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    [closeButton setImage:close forState:UIControlStateNormal];
-    closeButton.tintColor = [UIColor colorNamed:kGrey500Color];
-    [self.contentView addSubview:closeButton];
-    closeButton.translatesAutoresizingMaskIntoConstraints = NO;
+    _closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
+    [_closeButton setImage:close forState:UIControlStateNormal];
+    [self.contentView addSubview:_closeButton];
+    _closeButton.translatesAutoresizingMaskIntoConstraints = NO;
     [NSLayoutConstraint activateConstraints:@[
-      [closeButton.trailingAnchor
+      [_closeButton.trailingAnchor
           constraintEqualToAnchor:self.contentView.trailingAnchor
                          constant:-kFaviconInset],
-      [closeButton.centerYAnchor
+      [_closeButton.centerYAnchor
           constraintEqualToAnchor:self.contentView.centerYAnchor],
     ]];
-    [closeButton addTarget:self
-                    action:@selector(closeButtonTapped:)
-          forControlEvents:UIControlEventTouchUpInside];
+    [_closeButton addTarget:self
+                     action:@selector(closeButtonTapped:)
+           forControlEvents:UIControlEventTouchUpInside];
 
-    UILabel* titleLabel = [[UILabel alloc] init];
-    [self.contentView addSubview:titleLabel];
-    titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
+    _titleLabel = [[UILabel alloc] init];
+    _titleLabel.font = [UIFont systemFontOfSize:kFontSize
+                                         weight:UIFontWeightMedium];
+    [self.contentView addSubview:_titleLabel];
+    _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
     [NSLayoutConstraint activateConstraints:@[
-      [titleLabel.leadingAnchor
-          constraintEqualToAnchor:faviconView.trailingAnchor
+      [_titleLabel.leadingAnchor
+          constraintEqualToAnchor:_faviconView.trailingAnchor
                          constant:kTitleInset],
-      [titleLabel.trailingAnchor
-          constraintLessThanOrEqualToAnchor:closeButton.leadingAnchor
+      [_titleLabel.trailingAnchor
+          constraintLessThanOrEqualToAnchor:_closeButton.leadingAnchor
                                    constant:-kTitleInset],
-      [titleLabel.centerYAnchor
-          constraintEqualToAnchor:faviconView.centerYAnchor],
+      [_titleLabel.centerYAnchor
+          constraintEqualToAnchor:_faviconView.centerYAnchor],
     ]];
-    self.titleLabel = titleLabel;
   }
   return self;
 }
@@ -79,6 +79,7 @@
   self.titleLabel.text = nil;
   self.itemIdentifier = nil;
   self.selected = NO;
+  self.faviconView = nil;
 }
 
 - (void)setupBackgroundViews {
@@ -122,4 +123,35 @@
   [self.delegate closeButtonTappedForCell:self];
 }
 
+- (void)setSelected:(BOOL)selected {
+  [super setSelected:selected];
+  // Style the favicon tint color.
+  self.faviconView.tintColor = selected ? [UIColor colorNamed:kCloseButtonColor]
+                                        : [UIColor colorNamed:kGrey500Color];
+  // Style the close button tint color.
+  self.closeButton.tintColor = selected ? [UIColor colorNamed:kCloseButtonColor]
+                                        : [UIColor colorNamed:kGrey500Color];
+  // Style the title tint color.
+  self.titleLabel.textColor = selected ? [UIColor colorNamed:kTextPrimaryColor]
+                                       : [UIColor colorNamed:kGrey600Color];
+  // These dark-theme specific colorsets should only be used for iOS 12
+  // dark theme, as they will be removed along with iOS 12.
+  // TODO (crbug.com/981889): The following lines will be removed
+  // along with iOS 12
+  if (self.useIncognitoFallback) {
+    // Style the favicon tint color.
+    self.faviconView.tintColor =
+        selected ? [UIColor colorNamed:kCloseButtonDarkColor]
+                 : [UIColor colorNamed:kGrey500Color];
+    // Style the close button tint color.
+    self.closeButton.tintColor =
+        selected ? [UIColor colorNamed:kCloseButtonDarkColor]
+                 : [UIColor colorNamed:kGrey500Color];
+    // Style the title tint color.
+    self.titleLabel.textColor = selected
+                                    ? [UIColor colorNamed:kTextPrimaryDarkColor]
+                                    : [UIColor colorNamed:kGrey600Color];
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_consumer.h b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_consumer.h
index 8058faf..3e94535e 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_consumer.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_consumer.h
@@ -12,6 +12,9 @@
 // TabStripConsumer sets the current appearance of the TabStrip.
 @protocol TabStripConsumer
 
+// YES if the state is incognito.
+@property(nonatomic) BOOL isOffTheRecord;
+
 // Tells the consumer to replace its current set of items with |items| and
 // update the selected item ID to be |selectedItemID|. It's an error to pass
 // an |items| array containing items without unique IDs.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_coordinator.mm
index f80713b7..aa827bd9 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_coordinator.mm
@@ -45,6 +45,8 @@
         self.browser->GetBrowserState()->IsOffTheRecord()
             ? UIUserInterfaceStyleDark
             : UIUserInterfaceStyleUnspecified;
+    self.tabStripViewController.isOffTheRecord =
+        self.browser->GetBrowserState()->IsOffTheRecord();
   }
 
   self.mediator =
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm
index c69a594..c783d6d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm
@@ -251,6 +251,9 @@
   if (self.webStateList->count() > 0) {
     [self.consumer populateItems:CreateItems(self.webStateList)
                   selectedItemID:GetActiveTabId(self.webStateList)];
+    self.consumer.isOffTheRecord = self.webStateList->GetWebStateAt(0)
+                                       ->GetBrowserState()
+                                       ->IsOffTheRecord();
   }
 }
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_controller.mm
index fc03a4f..38e4fe2 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_controller.mm
@@ -5,6 +5,8 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_controller.h"
 
 #import "base/allocator/partition_allocator/partition_alloc.h"
+#import "base/ios/ios_util.h"
+#import "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_cell.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_layout.h"
@@ -47,6 +49,8 @@
 
 @implementation TabStripViewController
 
+@synthesize isOffTheRecord = _isOffTheRecord;
+
 - (instancetype)init {
   TabStripViewLayout* layout = [[TabStripViewLayout alloc] init];
   if (self = [super initWithCollectionViewLayout:layout]) {
@@ -110,11 +114,14 @@
     itemIndex = self.items.count - 1;
 
   TabSwitcherItem* item = self.items[itemIndex];
-  TabStripCell* cell = (TabStripCell*)[collectionView
+  TabStripCell* cell = base::mac::ObjCCastStrict<TabStripCell>([collectionView
       dequeueReusableCellWithReuseIdentifier:kReuseIdentifier
-                                forIndexPath:indexPath];
+                                forIndexPath:indexPath]);
 
   [self configureCell:cell withItem:item];
+  cell.useIncognitoFallback =
+      self.isOffTheRecord && !base::ios::IsRunningOnIOS13OrLater();
+  cell.selected = (self.selectedItemID == cell.itemIdentifier) ? YES : NO;
   return cell;
 }
 
@@ -185,11 +192,19 @@
   [self.collectionView
       deselectItemAtIndexPath:CreateIndexPath(self.selectedIndex)
                      animated:YES];
+  UICollectionViewCell* cell = [self.collectionView
+      cellForItemAtIndexPath:CreateIndexPath(self.selectedIndex)];
+  cell.selected = NO;
+
   self.selectedItemID = selectedItemID;
+
   [self.collectionView
       selectItemAtIndexPath:CreateIndexPath(self.selectedIndex)
                    animated:YES
              scrollPosition:UICollectionViewScrollPositionNone];
+  cell = [self.collectionView
+      cellForItemAtIndexPath:CreateIndexPath(self.selectedIndex)];
+  cell.selected = YES;
   [self orderBySelectedTab];
 }
 
@@ -212,6 +227,7 @@
                     if (cell.itemIdentifier == itemIdentifier)
                       cell.faviconView.image = icon;
                   }];
+    cell.selected = (cell.itemIdentifier == self.selectedItemID) ? YES : NO;
   }
 }
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_layout.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_layout.mm
index 7c652390..b62a5b99 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_layout.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_layout.mm
@@ -4,6 +4,10 @@
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_view_layout.h"
 
+#import "base/numerics/ranges.h"
+#import "ios/chrome/browser/ui/util/ui_util.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -11,19 +15,35 @@
 namespace {
 
 // Tab dimensions.
-const CGFloat kTabOverlapStacked = 32.0;
-const CGFloat kMinTabWidthStacked = 200.0;
+const CGFloat kTabOverlap = 32.0;
+const CGFloat kNewTabOverlap = 13.0;
+const CGFloat kMaxTabWidth = 265.0;
+const CGFloat kMinTabWidth = 200.0;
+
+// The size of the new tab button.
+const CGFloat kNewTabButtonWidth = 44;
 
 }  // namespace
 
+@interface TabStripViewLayout ()
+
+// The current tab width.  Recomputed whenever a tab is added or removed.
+@property(nonatomic) CGFloat currentTabWidth;
+
+@end
+
 @implementation TabStripViewLayout
 
 - (CGSize)collectionViewContentSize {
   UICollectionView* collection = self.collectionView;
   NSInteger num = [collection numberOfItemsInSection:0];
 
-  CGFloat width = kMinTabWidthStacked * num;
-  width -= (num - 1) * kTabOverlapStacked;
+  CGFloat visibleSpace = [self tabStripVisibleSpace];
+  _currentTabWidth = (visibleSpace + (kTabOverlap * (num - 1))) / num;
+  _currentTabWidth =
+      base::ClampToRange(_currentTabWidth, kMinTabWidth, kMaxTabWidth);
+
+  CGFloat width = _currentTabWidth * num - (num - 1) * kTabOverlap;
   width = MAX(width, collection.bounds.size.width);
   return CGSizeMake(width, collection.bounds.size.height);
 }
@@ -36,9 +56,9 @@
   CGRect bounds = collection.bounds;
 
   // Calculating tab's length size depending on the number of tabs.
-  CGFloat x = indexPath.row * kMinTabWidthStacked;
+  CGFloat x = indexPath.row * _currentTabWidth;
   if (indexPath.row > 0) {
-    x -= (kTabOverlapStacked * indexPath.row);
+    x -= (kTabOverlap * indexPath.row);
   }
 
   x = MAX(x, bounds.origin.x);
@@ -46,7 +66,7 @@
   UICollectionViewLayoutAttributes* attr = [UICollectionViewLayoutAttributes
       layoutAttributesForCellWithIndexPath:indexPath];
   attr.frame =
-      CGRectMake(x, bounds.origin.y, kMinTabWidthStacked, bounds.size.height);
+      CGRectMake(x, bounds.origin.y, _currentTabWidth, bounds.size.height);
   return attr;
 }
 
@@ -85,4 +105,14 @@
   return attr;
 }
 
+#pragma mark - Private
+
+// The available space for the tabstrip which is the view width without newtab-
+// button width.
+- (CGFloat)tabStripVisibleSpace {
+  CGFloat availableSpace = CGRectGetWidth([self.collectionView bounds]) -
+                           kNewTabButtonWidth + kNewTabOverlap;
+  return availableSpace;
+}
+
 @end
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 98afe52b..def75a7 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -9,6 +9,8 @@
   sources = [
     "dom_altering_lock.h",
     "dom_altering_lock.mm",
+    "error_page_controller_bridge.h",
+    "error_page_controller_bridge.mm",
     "error_page_util.h",
     "error_page_util.mm",
     "font_size_tab_helper.h",
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 2f0b144..d5ab352 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -29,6 +29,7 @@
 #include "ios/chrome/browser/ssl/ios_ssl_error_handler.h"
 #import "ios/chrome/browser/ui/elements/windowed_container_view.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
+#include "ios/chrome/browser/web/error_page_controller_bridge.h"
 #import "ios/chrome/browser/web/error_page_util.h"
 #include "ios/chrome/browser/web/features.h"
 #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h"
@@ -378,6 +379,13 @@
   } else {
     std::move(error_html_callback)
         .Run(GetErrorPage(url, error, is_post, is_off_the_record));
+    ErrorPageControllerBridge* error_page_controller =
+        ErrorPageControllerBridge::FromWebState(web_state);
+    if (error_page_controller) {
+      // ErrorPageControllerBridge may not be created for web_state not attached
+      // to a tab.
+      error_page_controller->StartHandlingJavascriptCommands();
+    }
   }
 }
 
diff --git a/ios/chrome/browser/web/chrome_web_client_unittest.mm b/ios/chrome/browser/web/chrome_web_client_unittest.mm
index 191834e..f714a80 100644
--- a/ios/chrome/browser/web/chrome_web_client_unittest.mm
+++ b/ios/chrome/browser/web/chrome_web_client_unittest.mm
@@ -27,6 +27,7 @@
 #import "ios/chrome/browser/safe_browsing/safe_browsing_unsafe_resource_container.h"
 #import "ios/chrome/browser/ssl/captive_portal_detector_tab_helper.h"
 #import "ios/chrome/browser/ssl/captive_portal_detector_tab_helper_delegate.h"
+#include "ios/chrome/browser/web/error_page_controller_bridge.h"
 #import "ios/chrome/browser/web/error_page_util.h"
 #include "ios/chrome/browser/web/features.h"
 #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h"
@@ -187,6 +188,7 @@
         page = error_html;
       });
   web::FakeWebState web_state;
+  ErrorPageControllerBridge::CreateForWebState(&web_state);
   web_client.PrepareErrorPage(&web_state, GURL(kTestUrl), error,
                               /*is_post=*/false,
                               /*is_off_the_record=*/false,
@@ -213,6 +215,7 @@
         page = error_html;
       });
   web::FakeWebState web_state;
+  ErrorPageControllerBridge::CreateForWebState(&web_state);
   web_client.PrepareErrorPage(&web_state, GURL(kTestUrl), error,
                               /*is_post=*/true,
                               /*is_off_the_record=*/false,
@@ -239,6 +242,7 @@
         page = error_html;
       });
   web::FakeWebState web_state;
+  ErrorPageControllerBridge::CreateForWebState(&web_state);
   web_client.PrepareErrorPage(&web_state, GURL(kTestUrl), error,
                               /*is_post=*/false,
                               /*is_off_the_record=*/true,
@@ -265,6 +269,7 @@
         page = error_html;
       });
   web::FakeWebState web_state;
+  ErrorPageControllerBridge::CreateForWebState(&web_state);
   web_client.PrepareErrorPage(&web_state, GURL(kTestUrl), error,
                               /*is_post=*/true,
                               /*is_off_the_record=*/true,
diff --git a/ios/chrome/browser/web/error_page_controller_bridge.h b/ios/chrome/browser/web/error_page_controller_bridge.h
new file mode 100644
index 0000000..11eb1a2
--- /dev/null
+++ b/ios/chrome/browser/web/error_page_controller_bridge.h
@@ -0,0 +1,54 @@
+// 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_ERROR_PAGE_CONTROLLER_BRIDGE_H_
+#define IOS_CHROME_BROWSER_WEB_ERROR_PAGE_CONTROLLER_BRIDGE_H_
+
+#include "ios/web/public/web_state_observer.h"
+#import "ios/web/public/web_state_user_data.h"
+
+namespace web {
+class WebState;
+}
+
+// A class to bridge the JS errorPageController object in the error page.
+// The class receives the JS messages and handle/dispatch them when needed.
+// Messages are sent in
+// components/neterror/resources/error_page_controller_ios.js.
+class ErrorPageControllerBridge
+    : public web::WebStateObserver,
+      public web::WebStateUserData<ErrorPageControllerBridge> {
+ public:
+  ~ErrorPageControllerBridge() override;
+
+  // Start observing "errorPageController" commands until next navigation.
+  void StartHandlingJavascriptCommands();
+
+  // WebStateObserver overrides
+  void DidStartNavigation(web::WebState* web_state,
+                          web::NavigationContext* navigation_context) override;
+  void WebStateDestroyed(web::WebState* web_state) override;
+
+ private:
+  friend class WebStateUserData<ErrorPageControllerBridge>;
+
+  ErrorPageControllerBridge(web::WebState* web_state);
+
+  // Handler for "errorPageController.*" JavaScript command.
+  void OnErrorPageCommand(const base::DictionaryValue& message,
+                          const GURL& url,
+                          bool user_is_interacting,
+                          web::WebFrame* sender_frame);
+
+  // The WebState this instance is observing. Will be null after
+  // WebStateDestroyed has been called.
+  web::WebState* web_state_ = nullptr;
+
+  // Subscription for JS message.
+  base::CallbackListSubscription subscription_;
+
+  WEB_STATE_USER_DATA_KEY_DECL();
+};
+
+#endif  // IOS_CHROME_BROWSER_WEB_ERROR_PAGE_CONTROLLER_BRIDGE_H_
diff --git a/ios/chrome/browser/web/error_page_controller_bridge.mm b/ios/chrome/browser/web/error_page_controller_bridge.mm
new file mode 100644
index 0000000..a7b1db66
--- /dev/null
+++ b/ios/chrome/browser/web/error_page_controller_bridge.mm
@@ -0,0 +1,88 @@
+// 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 "ios/chrome/browser/web/error_page_controller_bridge.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Prefix for the errorPageController activity event commands.
+// Must be kept in sync with
+// components/neterror/resources/error_page_controller_ios.js.
+const char kCommandPrefix[] = "errorPageController";
+
+// The NSUserDefault key to store the easter egg game on error page high score.
+NSString* const kEasterEggHighScore = @"EasterEggHighScore";
+}  // namespace
+
+ErrorPageControllerBridge::ErrorPageControllerBridge(web::WebState* web_state)
+    : web_state_(web_state) {}
+
+ErrorPageControllerBridge::~ErrorPageControllerBridge() {}
+
+void ErrorPageControllerBridge::StartHandlingJavascriptCommands() {
+  web_state_->AddObserver(this);
+  subscription_ = web_state_->AddScriptCommandCallback(
+      base::BindRepeating(&ErrorPageControllerBridge::OnErrorPageCommand,
+                          base::Unretained(this)),
+      kCommandPrefix);
+}
+
+void ErrorPageControllerBridge::OnErrorPageCommand(
+    const base::DictionaryValue& message,
+    const GURL& url,
+    bool user_is_interacting,
+    web::WebFrame* sender_frame) {
+  std::string command;
+  if (!message.GetString("command", &command)) {
+    return;
+  }
+  if (command == "errorPageController.updateEasterEggHighScore") {
+    std::string high_score_string;
+    if (!message.GetString("highScore", &high_score_string)) {
+      return;
+    }
+    int high_score;
+    if (!base::StringToInt(high_score_string, &high_score)) {
+      return;
+    }
+    [[NSUserDefaults standardUserDefaults] setInteger:high_score
+                                               forKey:kEasterEggHighScore];
+  }
+  if (command == "errorPageController.resetEasterEggHighScore") {
+    [[NSUserDefaults standardUserDefaults]
+        removeObjectForKey:kEasterEggHighScore];
+  }
+  if (command == "errorPageController.trackEasterEgg") {
+    int high_score = [[NSUserDefaults standardUserDefaults]
+        integerForKey:kEasterEggHighScore];
+    std::vector<base::Value> parameters;
+    parameters.push_back(base::Value(high_score));
+    sender_frame->CallJavaScriptFunction(
+        "errorPageController.initializeEasterEggHighScore", parameters);
+  }
+}
+
+#pragma mark WebStateObserver
+
+void ErrorPageControllerBridge::DidStartNavigation(
+    web::WebState* web_state,
+    web::NavigationContext* navigation_context) {
+  subscription_ = base::CallbackListSubscription();
+  web_state_->RemoveObserver(this);
+}
+
+void ErrorPageControllerBridge::WebStateDestroyed(web::WebState* web_state) {
+  web_state_->RemoveObserver(this);
+}
+
+WEB_STATE_USER_DATA_KEY_IMPL(ErrorPageControllerBridge)
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index d4c8083..56371f5 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-09f5e9e6fd9a03986fdca73d1f5bb799165cdb6a
\ No newline at end of file
+007525822c0412a6bd0c1d65fbe58d3ba96fce12
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 8146536..e897ab42 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-b02a12ffa132578d5bd7eaedaec6daf7ab7fb55f
\ No newline at end of file
+d2ff5c31c703178b4d77c016eb8955f172b4e977
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index ee77eea..61ca384 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-b41a5b45785bd97e9089a06e1d4b7853204dbedd
\ No newline at end of file
+5de3632f4616a649ee5d54f5045ce4fa67cf243e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index b10a9a2..6595a8a 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-269513051141507be86a9cf852ff9534b8ba8d31
\ No newline at end of file
+d38a9cd1e9045dc0a0118c76d312c10cadedd611
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 99872152..850d35b 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-5757c66ac70fe7f237ab7adde499254e41a6e4f1
\ No newline at end of file
+b2baad9993ddb7d625b42bbfdbfe9c611f853c3b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index aac795a..29291508 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-8fa54abbfe799c05d2e85bdf3f5a989357494098
\ No newline at end of file
+62455b7671269cbcd94966da3547f93e102c0362
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 7ae2d44..f8ec414 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-29505483040ef6c2fae9b09f8d784bc33d246aa2
\ No newline at end of file
+b49d3ea1f889d93e4091f0fed83d596372b957a9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 6033e06..ef5fbf3 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-d6c7316f355fcbb3c5c6d1da08904f1a39686fd4
\ No newline at end of file
+d9365568cb99a51f25a79edd1a091f8ac32a1b95
\ No newline at end of file
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc
index c3f150de..b5934c35 100644
--- a/net/http/http_stream_factory_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -2953,11 +2953,12 @@
   HttpRequestInfo request_info;
   request_info.method = "GET";
   request_info.url = GURL("https://example.com");
-  Initialize(request_info);
-  url::SchemeHostPort origin(request_info.url);
   NetworkIsolationKey network_isolation_key(
       SchemefulSite(GURL("https://example.com")),
       SchemefulSite(GURL("https://example.com")));
+  request_info.network_isolation_key = network_isolation_key;
+  Initialize(request_info);
+  url::SchemeHostPort origin(request_info.url);
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
   headers->AddHeader("alt-svc", alt_svc_header);
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index 3c67b124..e00e3125 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -205,17 +205,21 @@
   }
 
   if (sysno == __NR_madvise) {
-    // Only allow MADV_DONTNEED, MADV_RANDOM, MADV_NORMAL and MADV_FREE.
+    // Only allow MADV_DONTNEED, MADV_RANDOM, MADV_REMOVE, MADV_NORMAL and
+    // MADV_FREE.
     const Arg<int> advice(2);
-    return If(AnyOf(advice == MADV_DONTNEED,
-                    advice == MADV_RANDOM,
+    return If(AnyOf(advice == MADV_DONTNEED, advice == MADV_RANDOM,
+                    advice == MADV_REMOVE,
                     advice == MADV_NORMAL
 #if defined(MADV_FREE)
                     // MADV_FREE was introduced in Linux 4.5 and started being
                     // defined in glibc 2.24.
-                    , advice == MADV_FREE
+                    ,
+                    advice == MADV_FREE
 #endif
-                    ), Allow()).Else(Error(EPERM));
+                    ),
+              Allow())
+        .Else(Error(EPERM));
   }
 
 #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
diff --git a/services/network/public/cpp/is_potentially_trustworthy_unittest.cc b/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
index ab20533c..7e3d1e3 100644
--- a/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
+++ b/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
@@ -10,6 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
+#include "url/url_util.h"
 
 namespace network {
 
@@ -49,6 +50,10 @@
   EXPECT_FALSE(IsOriginPotentiallyTrustworthy("about:srcdoc"));
   EXPECT_FALSE(IsOriginPotentiallyTrustworthy("javascript:alert('blah')"));
   EXPECT_FALSE(IsOriginPotentiallyTrustworthy("data:test/plain;blah"));
+
+  EXPECT_FALSE(IsOriginPotentiallyTrustworthy("custom-scheme://example.com"));
+  EXPECT_TRUE(
+      IsOriginPotentiallyTrustworthy("quic-transport://example.com/counter"));
 }
 
 TEST(IsPotentiallyTrustworthy, Url) {
@@ -134,6 +139,31 @@
       IsUrlPotentiallyTrustworthy("blob:ftp://127.0.0.1/guid-goes-here"));
   EXPECT_TRUE(IsUrlPotentiallyTrustworthy(
       "blob:https://www.example.com/guid-goes-here"));
+
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("blob:data:text/html,Hello"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("blob:about:blank"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("filesystem:data:text/html,Hello"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("filesystem:about:blank"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy(
+      "blob:blob:https://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"));
+  EXPECT_FALSE(
+      IsUrlPotentiallyTrustworthy("filesystem:blob:https://example.com/"
+                                  "578223a1-8c13-17b3-84d5-eca045ae384a"));
+
+  EXPECT_TRUE(
+      IsUrlPotentiallyTrustworthy("quic-transport://example.com/counter"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("custom-scheme://example.com"));
+}
+
+TEST(IsPotentiallyTrustworthy, CustomScheme) {
+  url::ScopedSchemeRegistryForTests scoped_registry;
+  url::AddSecureScheme("custom-scheme");
+
+  // TODO(crbug.com/1159371): These tests should return true.
+  EXPECT_FALSE(IsOriginPotentiallyTrustworthy(
+      "custom-scheme://578223a1-8c13-17b3-84d5-eca045ae384a/fun.js"));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy(
+      "custom-scheme://578223a1-8c13-17b3-84d5-eca045ae384a/fun.js"));
 }
 
 // Tests that were for the removed blink::network_utils::IsOriginSecure.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 89e4af88..d72a0db 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8531,6 +8531,27 @@
             ]
         }
     ],
+    "WebRtcDistinctWorkerThread": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "linux",
+                "ios",
+                "android",
+                "android_weblayer"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "WebRtcDistinctWorkerThread"
+                    ]
+                }
+            ]
+        }
+    ],
     "WebRtcHybridAgc": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/webid/federated_auth_request.mojom b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
index 9ed7158..4e4d543f 100644
--- a/third_party/blink/public/mojom/webid/federated_auth_request.mojom
+++ b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
@@ -15,6 +15,10 @@
   kApprovalDeclined,
   kErrorTooManyRequests,
   kErrorWebIdNotSupportedByProvider,
+  kErrorFetchingWellKnown,
+  kErrorInvalidWellKnown,
+  kErrorFetchingSignin,
+  kErrorInvalidSigninResponse,
   kError,
 };
 
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.cc b/third_party/blink/renderer/bindings/core/v8/module_record.cc
index 364be175..e5ce258 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.cc
@@ -48,7 +48,6 @@
 v8::Local<v8::Module> ModuleRecord::Compile(
     v8::Isolate* isolate,
     const ModuleScriptCreationParams& params,
-    const KURL& base_url,
     const ScriptFetchOptions& options,
     const TextPosition& text_position,
     ExceptionState& exception_state,
@@ -76,7 +75,7 @@
 
   if (!V8ScriptRunner::CompileModule(
            isolate, params, text_position, compile_options, no_cache_reason,
-           ReferrerScriptInfo(base_url, options,
+           ReferrerScriptInfo(params.BaseURL(), options,
                               ReferrerScriptInfo::BaseUrlSource::kOther))
            .ToLocal(&module)) {
     DCHECK(try_catch.HasCaught());
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.h b/third_party/blink/renderer/bindings/core/v8/module_record.h
index 0b1779c..6e3be7e 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.h
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.h
@@ -65,7 +65,6 @@
   static v8::Local<v8::Module> Compile(
       v8::Isolate*,
       const ModuleScriptCreationParams& params,
-      const KURL& base_url,
       const ScriptFetchOptions&,
       const TextPosition&,
       ExceptionState&,
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index 63b9ae1..a62b433 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/core/loader/resource/script_resource.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
@@ -266,6 +267,100 @@
 
 size_t ScriptStreamer::small_script_threshold_ = 30 * 1024;
 
+std::tuple<ScriptStreamer*, ScriptStreamer::NotStreamingReason>
+ScriptStreamer::TakeFrom(ScriptResource* script_resource) {
+  ScriptStreamer::NotStreamingReason not_streamed_reason =
+      script_resource->NoStreamerReason();
+  ScriptStreamer* streamer = script_resource->TakeStreamer();
+  if (streamer) {
+    if (streamer->IsStreamingSuppressed()) {
+      not_streamed_reason = streamer->StreamingSuppressedReason();
+      streamer = nullptr;
+    } else {
+      DCHECK_EQ(not_streamed_reason,
+                ScriptStreamer::NotStreamingReason::kInvalid);
+    }
+  }
+  return std::make_tuple(streamer, not_streamed_reason);
+}
+
+namespace {
+
+enum class StreamedBoolean {
+  // Must match BooleanStreamed in enums.xml.
+  kNotStreamed = 0,
+  kStreamed = 1,
+  kMaxValue = kStreamed
+};
+
+void RecordStartedStreamingHistogram(ScriptSchedulingType type,
+                                     bool did_use_streamer) {
+  StreamedBoolean streamed = did_use_streamer ? StreamedBoolean::kStreamed
+                                              : StreamedBoolean::kNotStreamed;
+  switch (type) {
+    case ScriptSchedulingType::kParserBlocking: {
+      UMA_HISTOGRAM_ENUMERATION(
+          "WebCore.Scripts.ParsingBlocking.StartedStreaming", streamed);
+      break;
+    }
+    case ScriptSchedulingType::kDefer: {
+      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Deferred.StartedStreaming",
+                                streamed);
+      break;
+    }
+    case ScriptSchedulingType::kAsync: {
+      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Async.StartedStreaming",
+                                streamed);
+      break;
+    }
+    default: {
+      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Other.StartedStreaming",
+                                streamed);
+      break;
+    }
+  }
+}
+
+void RecordNotStreamingReasonHistogram(
+    ScriptSchedulingType type,
+    ScriptStreamer::NotStreamingReason reason) {
+  switch (type) {
+    case ScriptSchedulingType::kParserBlocking: {
+      UMA_HISTOGRAM_ENUMERATION(
+          "WebCore.Scripts.ParsingBlocking.NotStreamingReason", reason);
+      break;
+    }
+    case ScriptSchedulingType::kDefer: {
+      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Deferred.NotStreamingReason",
+                                reason);
+      break;
+    }
+    case ScriptSchedulingType::kAsync: {
+      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Async.NotStreamingReason",
+                                reason);
+      break;
+    }
+    default: {
+      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Other.NotStreamingReason",
+                                reason);
+      break;
+    }
+  }
+}
+
+}  // namespace
+
+void ScriptStreamer::RecordStreamingHistogram(
+    ScriptSchedulingType type,
+    bool can_use_streamer,
+    ScriptStreamer::NotStreamingReason reason) {
+  RecordStartedStreamingHistogram(type, can_use_streamer);
+  if (!can_use_streamer) {
+    DCHECK_NE(ScriptStreamer::NotStreamingReason::kInvalid, reason);
+    RecordNotStreamingReasonHistogram(type, reason);
+  }
+}
+
 bool ScriptStreamer::ConvertEncoding(
     const char* encoding_name,
     v8::ScriptCompiler::StreamedSource::Encoding* encoding) {
@@ -485,11 +580,15 @@
   source_ = std::make_unique<v8::ScriptCompiler::StreamedSource>(
       std::move(stream_ptr), encoding_);
 
-  // TODO(crbug.com/1061857) Pass ScriptResource::ScriptType to the streaming
-  // task.
   std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
-      script_streaming_task(base::WrapUnique(v8::ScriptCompiler::StartStreaming(
-          V8PerIsolateData::MainThreadIsolate(), source_.get())));
+      script_streaming_task =
+          base::WrapUnique(v8::ScriptCompiler::StartStreaming(
+              V8PerIsolateData::MainThreadIsolate(), source_.get(),
+              script_resource_->GetScriptType() ==
+                      mojom::blink::ScriptType::kClassic
+                  ? v8::ScriptType::kClassic
+                  : v8::ScriptType::kModule));
+
   if (!script_streaming_task) {
     // V8 cannot stream the script.
     stream_ = nullptr;
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.h b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
index 13e25e7..2275f212 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
@@ -6,11 +6,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
 
 #include <memory>
+#include <tuple>
 
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/script/script_scheduling_type.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "v8/include/v8.h"
@@ -73,6 +75,12 @@
   ~ScriptStreamer();
   void Trace(Visitor*) const;
 
+  static std::tuple<ScriptStreamer*, NotStreamingReason> TakeFrom(
+      ScriptResource*);
+  static void RecordStreamingHistogram(ScriptSchedulingType type,
+                                       bool can_use_streamer,
+                                       ScriptStreamer::NotStreamingReason);
+
   // Returns false if we cannot stream the given encoding.
   static bool ConvertEncoding(const char* encoding_name,
                               v8::ScriptCompiler::StreamedSource::Encoding*);
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index bb8584a..2693444d 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -271,8 +271,16 @@
                           true,                    // is_module
                           referrer_info.ToV8HostDefinedOptions(isolate));
 
-  // TODO(crbug.com/1061857): Finalize module streaming here.
   v8::Local<v8::String> code = V8String(isolate, params.GetSourceText());
+  if (ScriptStreamer* streamer = params.GetScriptStreamer()) {
+    // Final compile call for a streamed compilation.
+    // Streaming compilation may involve use of code cache.
+    // TODO(leszeks): Add compile timer to streaming compilation.
+    DCHECK(streamer->IsFinished());
+    DCHECK(!streamer->IsStreamingSuppressed());
+    return v8::ScriptCompiler::CompileModule(isolate->GetCurrentContext(),
+                                             streamer->Source(), code, origin);
+  }
 
   v8::MaybeLocal<v8::Module> script;
   inspector_compile_script_event::V8CacheResult cache_result;
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 5816e92c..7e3bb39d 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -1749,10 +1749,10 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_file_system_directory_iterator.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file_manager.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file_manager.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file_sync.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file_sync.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_manager.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_manager.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_navigation_preload_manager.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_navigation_preload_manager.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_navigator.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 05a6eae..077ebec 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -400,8 +400,8 @@
           "//third_party/blink/renderer/modules/mediastream/navigator_user_media.idl",
           "//third_party/blink/renderer/modules/mediastream/overconstrained_error.idl",
           "//third_party/blink/renderer/modules/native_io/native_io_file.idl",
+          "//third_party/blink/renderer/modules/native_io/native_io_file_manager.idl",
           "//third_party/blink/renderer/modules/native_io/native_io_file_sync.idl",
-          "//third_party/blink/renderer/modules/native_io/native_io_manager.idl",
           "//third_party/blink/renderer/modules/native_io/window_native_io.idl",
           "//third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl",
           "//third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.idl",
diff --git a/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.cc b/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.cc
index 7274982..30bd28f 100644
--- a/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.cc
+++ b/third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.cc
@@ -3,20 +3,21 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.h"
+#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 
 namespace blink {
 
 SelectorFilterParentScope* SelectorFilterParentScope::current_scope_ = nullptr;
 
 void SelectorFilterParentScope::PushAncestors(Element& element) {
-  if (Element* ancestor = element.ParentOrShadowHostElement()) {
+  if (Element* ancestor = FlatTreeTraversal::ParentElement(element)) {
     PushAncestors(*ancestor);
     resolver_->GetSelectorFilter().PushParent(*ancestor);
   }
 }
 
 void SelectorFilterParentScope::PopAncestors(Element& element) {
-  if (Element* ancestor = element.ParentOrShadowHostElement()) {
+  if (Element* ancestor = FlatTreeTraversal::ParentElement(element)) {
     resolver_->GetSelectorFilter().PopParent(*ancestor);
     PopAncestors(*ancestor);
   }
diff --git a/third_party/blink/renderer/core/css/selector_filter.cc b/third_party/blink/renderer/core/css/selector_filter.cc
index 9523178..7d129889 100644
--- a/third_party/blink/renderer/core/css/selector_filter.cc
+++ b/third_party/blink/renderer/core/css/selector_filter.cc
@@ -32,6 +32,7 @@
 
 #include "third_party/blink/renderer/core/css/css_selector.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 
 namespace blink {
 
@@ -65,8 +66,9 @@
 void SelectorFilter::PushParentStackFrame(Element& parent) {
   DCHECK(ancestor_identifier_filter_);
   DCHECK(parent_stack_.IsEmpty() ||
-         parent_stack_.back().element == parent.ParentOrShadowHostElement());
-  DCHECK(!parent_stack_.IsEmpty() || !parent.ParentOrShadowHostElement());
+         parent_stack_.back().element ==
+             FlatTreeTraversal::ParentElement(parent));
+  DCHECK(!parent_stack_.IsEmpty() || !FlatTreeTraversal::ParentElement(parent));
   parent_stack_.push_back(ParentStackFrame(parent));
   ParentStackFrame& parent_frame = parent_stack_.back();
   // Mix tags, class names and ids into some sort of weird bouillabaisse.
@@ -106,7 +108,7 @@
   DCHECK(ancestor_identifier_filter_);
   // We may get invoked for some random elements in some wacky cases during
   // style resolve. Pause maintaining the stack in this case.
-  if (parent_stack_.back().element != parent.ParentOrShadowHostElement())
+  if (parent_stack_.back().element != FlatTreeTraversal::ParentElement(parent))
     return;
   PushParentStackFrame(parent);
 }
@@ -167,9 +169,6 @@
         skip_over_subselectors = true;
         break;
       case CSSSelector::kShadowSlot:
-        // Disable fastRejectSelector.
-        *identifier_hashes = 0;
-        return;
       case CSSSelector::kDescendant:
       case CSSSelector::kChild:
       case CSSSelector::kUAShadow:
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index c4dd7ea..52ca536 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -2006,20 +2006,20 @@
 
 void StyleEngine::RecalcStyle(StyleRecalcChange change) {
   DCHECK(GetDocument().documentElement());
-  Element* root_element = &style_recalc_root_.RootElement();
-  Element* parent = root_element->ParentOrShadowHostElement();
+  Element& root_element = style_recalc_root_.RootElement();
+  Element* parent = FlatTreeTraversal::ParentElement(root_element);
 
   SelectorFilterRootScope filter_scope(parent);
-  root_element->RecalcStyle(change);
+  root_element.RecalcStyle(change);
 
-  for (ContainerNode* ancestor = root_element->GetStyleRecalcParent(); ancestor;
+  for (ContainerNode* ancestor = root_element.GetStyleRecalcParent(); ancestor;
        ancestor = ancestor->GetStyleRecalcParent()) {
     if (auto* ancestor_element = DynamicTo<Element>(ancestor))
       ancestor_element->RecalcStyleForTraversalRootAncestor();
     ancestor->ClearChildNeedsStyleRecalc();
   }
   style_recalc_root_.Clear();
-  if (!parent || IsA<HTMLBodyElement>(*root_element))
+  if (!parent || IsA<HTMLBodyElement>(root_element))
     PropagateWritingModeAndDirectionToHTMLRoot();
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 3909117a3..896b68a2 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -3836,4 +3836,86 @@
   EXPECT_NE(old_inner_style, new_inner_style);
 }
 
+TEST_F(StyleEngineTest, FastRejectForHostChild) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      .notfound span {
+        color: pink;
+      }
+    </style>
+    <div id="host">
+      <span id="slotted"></span>
+    </div>
+  )HTML");
+
+  Element* host = GetDocument().getElementById("host");
+  ASSERT_TRUE(host);
+  ShadowRoot& shadow_root =
+      host->AttachShadowRootInternal(ShadowRootType::kOpen);
+  shadow_root.setInnerHTML(R"HTML(
+    <slot></slot>
+  )HTML");
+  UpdateAllLifecyclePhases();
+
+  StyleEngine& engine = GetStyleEngine();
+  // If the Stats() were already enabled, we would not start with 0 counts.
+  EXPECT_FALSE(engine.Stats());
+  engine.SetStatsEnabled(true);
+
+  StyleResolverStats* stats = engine.Stats();
+  ASSERT_TRUE(stats);
+  EXPECT_EQ(0u, stats->rules_fast_rejected);
+
+  Element* span = GetDocument().getElementById("slotted");
+  ASSERT_TRUE(span);
+  span->SetInlineStyleProperty(CSSPropertyID::kColor, "green");
+
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
+  GetStyleEngine().RecalcStyle();
+
+  // Should fast reject ".notfound span"
+  EXPECT_EQ(1u, stats->rules_fast_rejected);
+}
+
+TEST_F(StyleEngineTest, RejectSlottedSelector) {
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <div id="host">
+      <span id="slotted"></span>
+    </div>
+  )HTML");
+
+  Element* host = GetDocument().getElementById("host");
+  ASSERT_TRUE(host);
+  ShadowRoot& shadow_root =
+      host->AttachShadowRootInternal(ShadowRootType::kOpen);
+  shadow_root.setInnerHTML(R"HTML(
+    <style>
+      .notfound ::slotted(span) {
+        color: pink;
+      }
+    </style>
+    <slot></slot>
+  )HTML");
+  UpdateAllLifecyclePhases();
+
+  StyleEngine& engine = GetStyleEngine();
+  // If the Stats() were already enabled, we would not start with 0 counts.
+  EXPECT_FALSE(engine.Stats());
+  engine.SetStatsEnabled(true);
+
+  StyleResolverStats* stats = engine.Stats();
+  ASSERT_TRUE(stats);
+  EXPECT_EQ(0u, stats->rules_fast_rejected);
+
+  Element* span = GetDocument().getElementById("slotted");
+  ASSERT_TRUE(span);
+  span->SetInlineStyleProperty(CSSPropertyID::kColor, "green");
+
+  GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
+  GetStyleEngine().RecalcStyle();
+
+  // Should fast reject ".notfound ::slotted(span)"
+  EXPECT_EQ(1u, stats->rules_fast_rejected);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index c23d0d5..d2f5a72f 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -4116,9 +4116,15 @@
 
 void HTMLMediaElement::BindMediaPlayerReceiver(
     mojo::PendingReceiver<media::mojom::blink::MediaPlayer> receiver) {
-  media_player_receiver_set_.Add(
+  mojo::ReceiverId receiver_id = media_player_receiver_set_.Add(
       std::move(receiver),
       GetDocument().GetTaskRunner(TaskType::kInternalMedia));
+
+  media_player_receiver_set_.set_disconnect_handler(WTF::BindRepeating(
+      [](HTMLMediaElement* html_media_element, mojo::ReceiverId receiver_id) {
+        html_media_element->media_player_receiver_set_.Remove(receiver_id);
+      },
+      WrapWeakPersistent(this), receiver_id));
 }
 
 void HTMLMediaElement::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
index 0dc66e71..46c2de49 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
@@ -79,13 +79,15 @@
   if (!child || IsPrescriptDelimiter(child))
     return false;
   bool number_of_scripts_is_even = true;
+  bool prescript_delimiter_found = false;
   while (child) {
     child = To<NGBlockNode>(NextSiblingInFlow(child));
     if (!child)
       continue;
     if (IsPrescriptDelimiter(child)) {
-      if (!number_of_scripts_is_even)
+      if (!number_of_scripts_is_even || prescript_delimiter_found)
         return false;
+      prescript_delimiter_found = true;
       continue;
     }
     number_of_scripts_is_even = !number_of_scripts_is_even;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
index 56a9d5e..4a62899 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
@@ -148,7 +148,7 @@
         // The structure of mmultiscripts is specified here:
         // https://mathml-refresh.github.io/mathml-core/#prescripts-and-tensor-indices-mmultiscripts
         if (IsPrescriptDelimiter(block_child)) {
-          if (!number_of_scripts_is_even || *first_prescript_index > 0) {
+          if (!number_of_scripts_is_even || *prescripts) {
             NOTREACHED();
             return;
           }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc
index 6e6a77d..aac0aba 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc
@@ -33,9 +33,22 @@
 
 namespace blink {
 
+namespace {
+
+bool SupportsMarkers(const SVGGeometryElement& element) {
+  return element.HasTagName(svg_names::kLineTag) ||
+         element.HasTagName(svg_names::kPathTag) ||
+         element.HasTagName(svg_names::kPolygonTag) ||
+         element.HasTagName(svg_names::kPolylineTag);
+}
+
+}  // namespace
+
 LayoutSVGPath::LayoutSVGPath(SVGGeometryElement* node)
     // <line> elements have no joins and thus needn't care about miters.
-    : LayoutSVGShape(node, IsA<SVGLineElement>(node) ? kNoMiters : kComplex) {}
+    : LayoutSVGShape(node, IsA<SVGLineElement>(node) ? kNoMiters : kComplex) {
+  DCHECK(SupportsMarkers(*node));
+}
 
 LayoutSVGPath::~LayoutSVGPath() = default;
 
@@ -70,8 +83,7 @@
   marker_positions_.clear();
 
   const SVGComputedStyle& svg_style = StyleRef().SvgStyle();
-  if (!svg_style.HasMarkers() ||
-      !SVGResources::SupportsMarkers(*To<SVGGraphicsElement>(GetElement())))
+  if (!svg_style.HasMarkers())
     return;
   SVGElementResourceClient* client = SVGResources::GetClient(*this);
   if (!client)
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
index 27093f372..2afe8794 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
@@ -30,7 +30,6 @@
 #include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h"
 #include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h"
 #include "third_party/blink/renderer/core/svg/svg_resource.h"
-#include "third_party/blink/renderer/core/svg_names.h"
 #include "third_party/blink/renderer/platform/graphics/filters/filter.h"
 #include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
 #include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
@@ -63,17 +62,6 @@
   return obb_layout_object->ObjectBoundingBox();
 }
 
-bool SVGResources::SupportsMarkers(const SVGElement& element) {
-  DEFINE_STATIC_LOCAL(HashSet<AtomicString>, tag_list,
-                      ({
-                          svg_names::kLineTag.LocalName(),
-                          svg_names::kPathTag.LocalName(),
-                          svg_names::kPolygonTag.LocalName(),
-                          svg_names::kPolylineTag.LocalName(),
-                      }));
-  return tag_list.Contains(element.localName());
-}
-
 void SVGResources::UpdateClipPathFilterMask(SVGElement& element,
                                             const ComputedStyle* old_style,
                                             const ComputedStyle& style) {
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources.h b/third_party/blink/renderer/core/layout/svg/svg_resources.h
index d7c3427a..38e6a0d 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources.h
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources.h
@@ -54,8 +54,6 @@
                             const ComputedStyle* old_style,
                             const ComputedStyle&);
   static void ClearMarkers(SVGElement&, const ComputedStyle*);
-
-  static bool SupportsMarkers(const SVGElement&);
 };
 
 class SVGElementResourceClient final
diff --git a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
index f28f2f5a..7f173c4 100644
--- a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_streamer.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/loader/resource/script_resource.h"
+#include "third_party/blink/renderer/core/script/pending_script.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -37,20 +38,37 @@
 
   auto* script_resource = To<ScriptResource>(resource);
 
-  HeapVector<Member<ConsoleMessage>> error_messages;
   ModuleScriptCreationParams::ModuleType module_type;
-  if (!WasModuleLoadSuccessful(script_resource, &error_messages,
-                               &module_type)) {
-    client_->NotifyFetchFinished(base::nullopt, error_messages);
-    return;
+  {
+    HeapVector<Member<ConsoleMessage>> error_messages;
+    if (!WasModuleLoadSuccessful(script_resource, &error_messages,
+                                 &module_type)) {
+      client_->NotifyFetchFinishedError(error_messages);
+      return;
+    }
   }
+  // Check if we can use the script streamer.
+  ScriptStreamer* streamer;
+  ScriptStreamer::NotStreamingReason not_streamed_reason;
+  std::tie(streamer, not_streamed_reason) =
+      ScriptStreamer::TakeFrom(script_resource);
+
+  ScriptStreamer::RecordStreamingHistogram(ScriptSchedulingType::kAsync,
+                                           streamer, not_streamed_reason);
+
+  TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
+                         "DocumentModuleScriptFetcher::NotifyFinished", this,
+                         TRACE_EVENT_FLAG_FLOW_IN, "not_streamed_reason",
+                         not_streamed_reason);
   // TODO(crbug.com/1061857): Pass ScriptStreamer to the client here.
-  ModuleScriptCreationParams params(
-      script_resource->GetResponse().CurrentRequestUrl(), module_type,
+  const KURL& url = script_resource->GetResponse().CurrentRequestUrl();
+  // Create an external module script where base_url == source_url.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-base-url
+  client_->NotifyFetchFinishedSuccess(ModuleScriptCreationParams(
+      /*source_url=*/url, /*base_url=*/url, module_type,
       script_resource->SourceText(), script_resource->CacheHandler(),
-      script_resource->GetResourceRequest().GetCredentialsMode(), nullptr,
-      ScriptStreamer::NotStreamingReason::kStreamingDisabled);
-  client_->NotifyFetchFinished(params, error_messages);
+      script_resource->GetResourceRequest().GetCredentialsMode(), streamer,
+      not_streamed_reason));
 }
 
 void DocumentModuleScriptFetcher::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
index 0b711b4c..c7e41452 100644
--- a/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
@@ -46,7 +46,7 @@
         mojom::ConsoleMessageLevel::kError,
         "Failed to load the script unexpectedly",
         fetch_params.Url().GetString(), nullptr, 0));
-    client->NotifyFetchFinished(base::nullopt, error_messages);
+    client->NotifyFetchFinishedError(error_messages);
     return;
   }
 
@@ -99,16 +99,17 @@
         mojom::ConsoleMessageLevel::kError,
         "Failed to load the script unexpectedly",
         fetch_params.Url().GetString(), nullptr, 0));
-    client->NotifyFetchFinished(base::nullopt, error_messages);
+    client->NotifyFetchFinishedError(error_messages);
     return;
   }
 
-  ModuleScriptCreationParams params(
-      fetch_params.Url(), module_type,
-      ParkableString(script_data->TakeSourceText().Impl()),
-      nullptr /* cache_handler */,
-      fetch_params.GetResourceRequest().GetCredentialsMode());
-  client->NotifyFetchFinished(params, HeapVector<Member<ConsoleMessage>>());
+  // Create an external module script where base_url == source_url.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-base-url
+  client->NotifyFetchFinishedSuccess(ModuleScriptCreationParams(
+      /*source_url=*/fetch_params.Url(), /*base_url=*/fetch_params.Url(),
+      module_type, ParkableString(script_data->TakeSourceText().Impl()),
+      /*cache_handler=*/nullptr,
+      fetch_params.GetResourceRequest().GetCredentialsMode()));
 }
 
 void InstalledServiceWorkerModuleScriptFetcher::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h b/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h
index 258f3646..8fdfb2a 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h
@@ -27,6 +27,7 @@
  public:
   ModuleScriptCreationParams(
       const KURL& source_url,
+      const KURL& base_url,
       const ModuleScriptCreationParams::ModuleType module_type,
       const ParkableString& source_text,
       SingleCachedMetadataHandler* cache_handler,
@@ -35,6 +36,7 @@
       ScriptStreamer::NotStreamingReason not_streaming_reason =
           ScriptStreamer::NotStreamingReason::kStreamingDisabled)
       : source_url_(source_url),
+        base_url_(base_url),
         module_type_(module_type),
         is_isolated_(false),
         source_text_(source_text),
@@ -54,8 +56,8 @@
     String isolated_source_text =
         isolated_source_text_ ? isolated_source_text_.IsolatedCopy()
                               : GetSourceText().ToString().IsolatedCopy();
-    return ModuleScriptCreationParams(SourceURL().Copy(), module_type_,
-                                      isolated_source_text,
+    return ModuleScriptCreationParams(SourceURL().Copy(), BaseURL().Copy(),
+                                      module_type_, isolated_source_text,
                                       GetFetchCredentialsMode());
   }
 
@@ -64,6 +66,7 @@
   }
 
   const KURL& SourceURL() const { return source_url_; }
+  const KURL& BaseURL() const { return base_url_; }
 
   const ParkableString& GetSourceText() const {
     if (is_isolated_) {
@@ -74,11 +77,10 @@
     return source_text_;
   }
 
-  // TODO(crbug.com/1154943): Make this non-const.
-  void ClearSourceText() const {
-    source_text_ = ParkableString();
-    isolated_source_text_ = String();
-    is_isolated_ = false;
+  ModuleScriptCreationParams CopyWithClearedSourceText() const {
+    return ModuleScriptCreationParams(
+        source_url_, base_url_, module_type_, ParkableString(), cache_handler_,
+        credentials_mode_, script_streamer_, not_streaming_reason_);
   }
 
   SingleCachedMetadataHandler* CacheHandler() const { return cache_handler_; }
@@ -88,17 +90,22 @@
   }
 
   bool IsSafeToSendToAnotherThread() const {
-    return source_url_.IsSafeToSendToAnotherThread() && is_isolated_;
+    return source_url_.IsSafeToSendToAnotherThread() &&
+           base_url_.IsSafeToSendToAnotherThread() && is_isolated_;
   }
 
+  ScriptStreamer* GetScriptStreamer() const { return script_streamer_; }
+
  private:
   // Creates an isolated copy.
   ModuleScriptCreationParams(
       const KURL& source_url,
+      const KURL& base_url,
       const ModuleScriptCreationParams::ModuleType& module_type,
       const String& isolated_source_text,
       network::mojom::CredentialsMode credentials_mode)
       : source_url_(source_url),
+        base_url_(base_url),
         module_type_(module_type),
         is_isolated_(true),
         source_text_(),
@@ -113,6 +120,7 @@
             ScriptStreamer::NotStreamingReason::kStreamingDisabled) {}
 
   const KURL source_url_;
+  const KURL base_url_;
   const ModuleType module_type_;
 
   // Mutable because an isolated copy can become bound to a thread when
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc
index 9b3f8cc..62d8346 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc
@@ -21,12 +21,12 @@
     base::PassKey<ModuleScriptLoader> pass_key) {}
 
 void ModuleScriptFetcher::Client::OnFetched(
-    const base::Optional<ModuleScriptCreationParams>& params) {
-  NotifyFetchFinished(params, HeapVector<Member<ConsoleMessage>>());
+    const ModuleScriptCreationParams& params) {
+  NotifyFetchFinishedSuccess(params);
 }
 
 void ModuleScriptFetcher::Client::OnFailed() {
-  NotifyFetchFinished(base::nullopt, HeapVector<Member<ConsoleMessage>>());
+  NotifyFetchFinishedError(HeapVector<Member<ConsoleMessage>>());
 }
 
 void ModuleScriptFetcher::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.h b/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.h
index a47aefdc..0b286f1 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.h
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.h
@@ -29,13 +29,14 @@
 
   class CORE_EXPORT Client : public GarbageCollectedMixin {
    public:
-    virtual void NotifyFetchFinished(
-        const base::Optional<ModuleScriptCreationParams>&,
+    virtual void NotifyFetchFinishedError(
         const HeapVector<Member<ConsoleMessage>>& error_messages) = 0;
+    virtual void NotifyFetchFinishedSuccess(
+        const ModuleScriptCreationParams&) = 0;
 
     // These helpers are used only from WorkletModuleResponsesMap.
     // TODO(nhiroki): Move these helpers to WorkletModuleResponsesMap.
-    void OnFetched(const base::Optional<ModuleScriptCreationParams>&);
+    void OnFetched(const ModuleScriptCreationParams&);
     void OnFailed();
   };
 
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
index d529d21..db7ed5d 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
@@ -224,8 +224,7 @@
 }
 
 // <specdef href="https://html.spec.whatwg.org/C/#fetch-a-single-module-script">
-void ModuleScriptLoader::NotifyFetchFinished(
-    const base::Optional<ModuleScriptCreationParams>& params,
+void ModuleScriptLoader::NotifyFetchFinishedError(
     const HeapVector<Member<ConsoleMessage>>& error_messages) {
   // [nospec] Abort the steps if the browsing context is discarded.
   if (!modulator_->HasValidContext()) {
@@ -238,11 +237,17 @@
   // <spec step="9">If any of the following conditions are met, set
   // moduleMap[url] to null, asynchronously complete this algorithm with null,
   // and abort these steps: ...</spec>
-  if (!params.has_value()) {
-    for (ConsoleMessage* error_message : error_messages) {
-      ExecutionContext::From(modulator_->GetScriptState())
-          ->AddConsoleMessage(error_message);
-    }
+  for (ConsoleMessage* error_message : error_messages) {
+    ExecutionContext::From(modulator_->GetScriptState())
+        ->AddConsoleMessage(error_message);
+  }
+  AdvanceState(State::kFinished);
+}
+
+void ModuleScriptLoader::NotifyFetchFinishedSuccess(
+    const ModuleScriptCreationParams& params) {
+  // [nospec] Abort the steps if the browsing context is discarded.
+  if (!modulator_->HasValidContext()) {
     AdvanceState(State::kFinished);
     return;
   }
@@ -253,7 +258,7 @@
   // <spec step="12.2">Set module script to the result of creating a JavaScript
   // module script given source text, module map settings object, response's
   // url, and options.</spec>
-  switch (params->GetModuleType()) {
+  switch (params.GetModuleType()) {
     case ModuleScriptCreationParams::ModuleType::kJSONModule:
       DCHECK(base::FeatureList::IsEnabled(blink::features::kJSONModules));
       module_script_ = ValueWrapperSyntheticModuleScript::
@@ -264,16 +269,17 @@
       module_script_ = ValueWrapperSyntheticModuleScript::
           CreateCSSWrapperSyntheticModuleScript(params, modulator_);
       break;
-    case ModuleScriptCreationParams::ModuleType::kJavaScriptModule:
+    case ModuleScriptCreationParams::ModuleType::kJavaScriptModule: {
       // Step 9. "Let source text be the result of UTF-8 decoding response's
       // body." [spec text]
       // Step 10. "Let module script be the result of creating
       // a module script given source text, module map settings object,
       // response's url, and options." [spec text]
       module_script_ = JSModuleScript::Create(
-          params.value(), params->SourceURL() /* base URL */,
-          ScriptSourceLocationType::kExternalFile, modulator_, options_);
+          params, ScriptSourceLocationType::kExternalFile, modulator_,
+          options_);
       break;
+    };
   }
 
   AdvanceState(State::kFinished);
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.h b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.h
index 5c90418..1681aec4 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.h
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.h
@@ -58,8 +58,8 @@
                     ModuleScriptLoaderClient*);
 
   // Implements ModuleScriptFetcher::Client.
-  void NotifyFetchFinished(
-      const base::Optional<ModuleScriptCreationParams>&,
+  void NotifyFetchFinishedSuccess(const ModuleScriptCreationParams&) override;
+  void NotifyFetchFinishedError(
       const HeapVector<Member<ConsoleMessage>>& error_messages) override;
 
   bool IsInitialState() const { return state_ == State::kInitial; }
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index 03418efe..6b059a5 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -90,12 +90,14 @@
   ClearResource();
 
   auto* script_resource = To<ScriptResource>(resource);
-  HeapVector<Member<ConsoleMessage>> error_messages;
   ModuleScriptCreationParams::ModuleType module_type;
-  if (!WasModuleLoadSuccessful(script_resource, &error_messages,
-                               &module_type)) {
-    client_->NotifyFetchFinished(base::nullopt, error_messages);
-    return;
+  {
+    HeapVector<Member<ConsoleMessage>> error_messages;
+    if (!WasModuleLoadSuccessful(script_resource, &error_messages,
+                                 &module_type)) {
+      client_->NotifyFetchFinishedError(error_messages);
+      return;
+    }
   }
 
   NotifyClient(resource->Url(), module_type,
@@ -147,7 +149,7 @@
           mojom::ConsoleMessageSource::kSecurity,
           mojom::ConsoleMessageLevel::kError,
           "Refused to cross-origin redirects of the top-level worker script."));
-      client_->NotifyFetchFinished(base::nullopt, error_messages);
+      client_->NotifyFetchFinishedError(error_messages);
       return;
     }
 
@@ -176,13 +178,15 @@
         response_origin_trial_tokens.get(), response.AppCacheID());
   }
 
-  ModuleScriptCreationParams params(response.CurrentRequestUrl(), module_type,
-                                    source_text, cache_handler,
-                                    credentials_mode);
 
   // <spec step="12.7">Asynchronously complete the perform the fetch steps with
   // response.</spec>
-  client_->NotifyFetchFinished(params, error_messages);
+  const KURL& url = response.CurrentRequestUrl();
+  // Create an external module script where base_url == source_url.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-base-url
+  client_->NotifyFetchFinishedSuccess(ModuleScriptCreationParams(
+      /*source_url=*/url, /*base_url=*/url, module_type, source_text,
+      cache_handler, credentials_mode));
 }
 
 void WorkerModuleScriptFetcher::DidReceiveData(base::span<const char> span) {
@@ -213,7 +217,7 @@
         resource_response.CurrentRequestUrl().GetString(), /*loader=*/nullptr,
         -1));
     worker_main_script_loader_->Cancel();
-    client_->NotifyFetchFinished(base::nullopt, error_messages);
+    client_->NotifyFetchFinishedError(error_messages);
     return;
   }
 }
@@ -230,8 +234,7 @@
 }
 
 void WorkerModuleScriptFetcher::OnFailedLoadingWorkerMainScript() {
-  client_->NotifyFetchFinished(base::nullopt,
-                               HeapVector<Member<ConsoleMessage>>());
+  client_->NotifyFetchFinishedError(HeapVector<Member<ConsoleMessage>>());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc
index f329dcc..f625741b 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worklet_module_script_fetcher.cc
@@ -49,8 +49,11 @@
   HeapVector<Member<ConsoleMessage>> error_messages;
   ModuleScriptCreationParams::ModuleType module_type;
   if (WasModuleLoadSuccessful(script_resource, &error_messages, &module_type)) {
-    params.emplace(script_resource->GetResponse().CurrentRequestUrl(),
-                   module_type, script_resource->SourceText(),
+    const KURL& url = script_resource->GetResponse().CurrentRequestUrl();
+    // Create an external module script where base_url == source_url.
+    // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-base-url
+    params.emplace(/*source_url=*/url, /*base_url=*/url, module_type,
+                   script_resource->SourceText(),
                    script_resource->CacheHandler(),
                    script_resource->GetResourceRequest().GetCredentialsMode());
   }
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.cc b/third_party/blink/renderer/core/script/classic_pending_script.cc
index 35523e3..460936df 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.cc
+++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -126,71 +126,6 @@
   DCHECK_EQ(is_external_, !!GetResource());
 }
 
-namespace {
-
-enum class StreamedBoolean {
-  // Must match BooleanStreamed in enums.xml.
-  kNotStreamed = 0,
-  kStreamed = 1,
-  kMaxValue = kStreamed
-};
-
-void RecordStartedStreamingHistogram(ScriptSchedulingType type,
-                                     bool did_use_streamer) {
-  StreamedBoolean streamed = did_use_streamer ? StreamedBoolean::kStreamed
-                                              : StreamedBoolean::kNotStreamed;
-  switch (type) {
-    case ScriptSchedulingType::kParserBlocking: {
-      UMA_HISTOGRAM_ENUMERATION(
-          "WebCore.Scripts.ParsingBlocking.StartedStreaming", streamed);
-      break;
-    }
-    case ScriptSchedulingType::kDefer: {
-      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Deferred.StartedStreaming",
-                                streamed);
-      break;
-    }
-    case ScriptSchedulingType::kAsync: {
-      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Async.StartedStreaming",
-                                streamed);
-      break;
-    }
-    default: {
-      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Other.StartedStreaming",
-                                streamed);
-      break;
-    }
-  }
-}
-
-void RecordNotStreamingReasonHistogram(
-    ScriptSchedulingType type,
-    ScriptStreamer::NotStreamingReason reason) {
-  switch (type) {
-    case ScriptSchedulingType::kParserBlocking: {
-      UMA_HISTOGRAM_ENUMERATION(
-          "WebCore.Scripts.ParsingBlocking.NotStreamingReason", reason);
-      break;
-    }
-    case ScriptSchedulingType::kDefer: {
-      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Deferred.NotStreamingReason",
-                                reason);
-      break;
-    }
-    case ScriptSchedulingType::kAsync: {
-      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Async.NotStreamingReason",
-                                reason);
-      break;
-    }
-    default: {
-      UMA_HISTOGRAM_ENUMERATION("WebCore.Scripts.Other.NotStreamingReason",
-                                reason);
-      break;
-    }
-  }
-}
-
-}  // namespace
 
 void ClassicPendingScript::RecordThirdPartyRequestWithCookieIfNeeded(
     const ResourceResponse& response) const {
@@ -236,16 +171,6 @@
           kUndeferrableThirdPartySubresourceRequestWithCookie);
 }
 
-void ClassicPendingScript::RecordStreamingHistogram(
-    ScriptSchedulingType type,
-    bool can_use_streamer,
-    ScriptStreamer::NotStreamingReason reason) {
-  RecordStartedStreamingHistogram(type, can_use_streamer);
-  if (!can_use_streamer) {
-    DCHECK_NE(ScriptStreamer::NotStreamingReason::kInvalid, reason);
-    RecordNotStreamingReasonHistogram(type, reason);
-  }
-}
 
 void ClassicPendingScript::DisposeInternal() {
   MemoryPressureListenerRegistry::Instance().UnregisterClient(this);
@@ -373,8 +298,9 @@
     }
 
     DCHECK(!GetResource());
-    RecordStreamingHistogram(GetSchedulingType(), false,
-                             ScriptStreamer::NotStreamingReason::kInlineScript);
+    ScriptStreamer::RecordStreamingHistogram(
+        GetSchedulingType(), false,
+        ScriptStreamer::NotStreamingReason::kInlineScript);
 
     ScriptSourceCode source_code(source_text_for_inline_script_,
                                  source_location_type_, cache_handler,
@@ -398,34 +324,25 @@
   }
 
   // Check if we can use the script streamer.
-  bool did_stream = false;
-  ScriptStreamer::NotStreamingReason not_streamed_reason =
-      resource->NoStreamerReason();
-  ScriptStreamer* streamer = resource->TakeStreamer();
-  if (streamer) {
-    DCHECK_EQ(not_streamed_reason,
-              ScriptStreamer::NotStreamingReason::kInvalid);
-    if (streamer->IsStreamingSuppressed()) {
-      not_streamed_reason = streamer->StreamingSuppressedReason();
-    } else if (ready_state_ == kErrorOccurred) {
-      not_streamed_reason = ScriptStreamer::NotStreamingReason::kErrorOccurred;
-    } else if (not_streamed_reason ==
-               ScriptStreamer::NotStreamingReason::kInvalid) {
-      // streamer can be used to compile script.
-      CHECK_EQ(ready_state_, kReady);
-      did_stream = true;
-    }
+  ScriptStreamer* streamer;
+  ScriptStreamer::NotStreamingReason not_streamed_reason;
+  std::tie(streamer, not_streamed_reason) = ScriptStreamer::TakeFrom(resource);
+
+  if (ready_state_ == kErrorOccurred) {
+    not_streamed_reason = ScriptStreamer::NotStreamingReason::kErrorOccurred;
+    streamer = nullptr;
   }
-  RecordStreamingHistogram(GetSchedulingType(), did_stream,
-                           not_streamed_reason);
+  if (streamer)
+    CHECK_EQ(ready_state_, kReady);
+  ScriptStreamer::RecordStreamingHistogram(GetSchedulingType(), streamer,
+                                           not_streamed_reason);
 
   TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
                          "ClassicPendingScript::GetSource", this,
                          TRACE_EVENT_FLAG_FLOW_IN, "not_streamed_reason",
                          not_streamed_reason);
 
-  ScriptSourceCode source_code(did_stream ? streamer : nullptr, resource,
-                               not_streamed_reason);
+  ScriptSourceCode source_code(streamer, resource, not_streamed_reason);
   // The base URL for external classic script is
   //
   // <spec href="https://html.spec.whatwg.org/C/#concept-script-base-url">
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.h b/third_party/blink/renderer/core/script/classic_pending_script.h
index 842eed8..503c7138 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.h
+++ b/third_party/blink/renderer/core/script/classic_pending_script.h
@@ -97,10 +97,6 @@
   void NotifyFinished(Resource*) override;
   String DebugName() const override { return "PendingScript"; }
 
-  static void RecordStreamingHistogram(
-      ScriptSchedulingType type,
-      bool can_use_streamer,
-      ScriptStreamer::NotStreamingReason reason);
   void RecordThirdPartyRequestWithCookieIfNeeded(const ResourceResponse&) const;
 
   // MemoryPressureListener
diff --git a/third_party/blink/renderer/core/script/js_module_script.cc b/third_party/blink/renderer/core/script/js_module_script.cc
index d4d38ad..000b8815 100644
--- a/third_party/blink/renderer/core/script/js_module_script.cc
+++ b/third_party/blink/renderer/core/script/js_module_script.cc
@@ -18,16 +18,17 @@
 // <specdef
 // href="https://html.spec.whatwg.org/C/#creating-a-javascript-module-script">
 JSModuleScript* JSModuleScript::Create(
-    const ModuleScriptCreationParams& params,
-    const KURL& base_url,
+    const ModuleScriptCreationParams& original_params,
     ScriptSourceLocationType source_location_type,
     Modulator* modulator,
     const ScriptFetchOptions& options,
     const TextPosition& start_position) {
   // <spec step="1">If scripting is disabled for settings's responsible browsing
   // context, then set source to the empty string.</spec>
-  if (modulator->IsScriptingDisabled())
-    params.ClearSourceText();
+  const ModuleScriptCreationParams& params =
+      modulator->IsScriptingDisabled()
+          ? original_params.CopyWithClearedSourceText()
+          : original_params;
 
   // <spec step="2">Let script be a new module script that this algorithm will
   // subsequently initialize.</spec>
@@ -47,7 +48,7 @@
   ModuleRecordProduceCacheData* produce_cache_data = nullptr;
 
   v8::Local<v8::Module> result =
-      ModuleRecord::Compile(isolate, params, base_url, options, start_position,
+      ModuleRecord::Compile(isolate, params, options, start_position,
                             exception_state, modulator->GetV8CacheOptions(),
                             source_location_type, &produce_cache_data);
 
@@ -59,7 +60,7 @@
   // be used for the speced algorithms, but may be used from inspector.
   JSModuleScript* script = CreateInternal(
       params.GetSourceText().length(), modulator, result, params.SourceURL(),
-      base_url, options, start_position, produce_cache_data);
+      params.BaseURL(), options, start_position, produce_cache_data);
 
   // <spec step="8">If result is a list of errors, then:</spec>
   if (exception_state.HadException()) {
diff --git a/third_party/blink/renderer/core/script/js_module_script.h b/third_party/blink/renderer/core/script/js_module_script.h
index 93996d34..47464bc 100644
--- a/third_party/blink/renderer/core/script/js_module_script.h
+++ b/third_party/blink/renderer/core/script/js_module_script.h
@@ -26,7 +26,6 @@
   // https://html.spec.whatwg.org/C/#creating-a-javascript-module-script
   static JSModuleScript* Create(
       const ModuleScriptCreationParams& params,
-      const KURL& base_url,
       ScriptSourceLocationType,
       Modulator*,
       const ScriptFetchOptions&,
diff --git a/third_party/blink/renderer/core/script/module_map_test.cc b/third_party/blink/renderer/core/script/module_map_test.cc
index 146bf8bd..2e76379 100644
--- a/third_party/blink/renderer/core/script/module_map_test.cc
+++ b/third_party/blink/renderer/core/script/module_map_test.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
 
 namespace blink {
 
@@ -116,11 +117,7 @@
                ModuleScriptFetcher::Client* client) override {
       CHECK_EQ(request.GetScriptType(), mojom::blink::ScriptType::kModule);
       TestRequest* test_request = MakeGarbageCollected<TestRequest>(
-          ModuleScriptCreationParams(
-              request.Url(),
-              ModuleScriptCreationParams::ModuleType::kJavaScriptModule,
-              ParkableString(String("").ReleaseImpl()), nullptr,
-              request.GetResourceRequest().GetCredentialsMode()),
+          request.Url(), request.GetResourceRequest().GetCredentialsMode(),
           client);
       modulator_->test_requests_.push_back(test_request);
     }
@@ -150,17 +147,20 @@
   }
 
   struct TestRequest final : public GarbageCollected<TestRequest> {
-    TestRequest(const ModuleScriptCreationParams& params,
+    TestRequest(const KURL& url,
+                network::mojom::CredentialsMode credential_mode,
                 ModuleScriptFetcher::Client* client)
-        : params_(params), client_(client) {}
+        : url_(url), credential_mode_(credential_mode), client_(client) {}
     void NotifyFetchFinished() {
-      client_->NotifyFetchFinished(*params_,
-                                   HeapVector<Member<ConsoleMessage>>());
+      client_->NotifyFetchFinishedSuccess(ModuleScriptCreationParams(
+          url_, url_, ModuleScriptCreationParams::ModuleType::kJavaScriptModule,
+          ParkableString(String("").ReleaseImpl()), nullptr, credential_mode_));
     }
     void Trace(Visitor* visitor) const { visitor->Trace(client_); }
 
    private:
-    base::Optional<ModuleScriptCreationParams> params_;
+    const KURL url_;
+    const network::mojom::CredentialsMode credential_mode_;
     Member<ModuleScriptFetcher::Client> client_;
   };
   HeapVector<Member<TestRequest>> test_requests_;
diff --git a/third_party/blink/renderer/core/script/module_script_test.cc b/third_party/blink/renderer/core/script/module_script_test.cc
index 8f5db341..407062c2 100644
--- a/third_party/blink/renderer/core/script/module_script_test.cc
+++ b/third_party/blink/renderer/core/script/module_script_test.cc
@@ -82,11 +82,11 @@
       const String& source_text,
       SingleCachedMetadataHandler* cache_handler) {
     ModuleScriptCreationParams params(
-        KURL("https://fox.url/script.js"),
+        KURL("https://fox.url/script.js"), KURL("https://fox.url/"),
         ModuleScriptCreationParams::ModuleType::kJavaScriptModule,
         ParkableString(source_text.IsolatedCopy().ReleaseImpl()), cache_handler,
         network::mojom::CredentialsMode::kOmit);
-    return JSModuleScript::Create(params, KURL("https://fox.url/"),
+    return JSModuleScript::Create(params,
                                   ScriptSourceLocationType::kExternalFile,
                                   modulator, ScriptFetchOptions());
   }
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc
index bb39a76..2a1971c 100644
--- a/third_party/blink/renderer/core/script/script_loader.cc
+++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -464,11 +464,6 @@
     SecurityPolicy::ReferrerPolicyFromString(
         referrerpolicy_attr, kDoNotSupportReferrerPolicyLegacyKeywords,
         &referrer_policy);
-    if (context_window->IsSecureContext() &&
-        referrer_policy == network::mojom::ReferrerPolicy::kAlways) {
-      UseCounter::Count(*context_window,
-                        WebFeature::kSetReferrerPolicyUnsafeUrlInSecureContext);
-    }
   }
 
   // Priority Hints is currently a non-standard feature, but we can assume the
@@ -715,13 +710,13 @@
         // text, settings object, base URL, and options.</spec>
 
         ModuleScriptCreationParams params(
-            source_url,
+            source_url, base_url,
             ModuleScriptCreationParams::ModuleType::kJavaScriptModule,
             ParkableString(source_text.Impl()), nullptr,
             options.CredentialsMode());
-        ModuleScript* module_script = JSModuleScript::Create(
-            params, base_url, ScriptSourceLocationType::kInline, modulator,
-            options, position);
+        ModuleScript* module_script =
+            JSModuleScript::Create(params, ScriptSourceLocationType::kInline,
+                                   modulator, options, position);
 
         // <spec label="fetch-an-inline-module-script-graph" step="2">If script
         // is null, asynchronously complete this algorithm with null, and abort
diff --git a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
index d61fa63..e3650df6 100644
--- a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
+++ b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
@@ -25,7 +25,7 @@
 // https://whatpr.org/html/4898/webappapis.html#creating-a-css-module-script
 ValueWrapperSyntheticModuleScript*
 ValueWrapperSyntheticModuleScript::CreateCSSWrapperSyntheticModuleScript(
-    const base::Optional<ModuleScriptCreationParams>& params,
+    const ModuleScriptCreationParams& params,
     Modulator* settings_object) {
   DCHECK(settings_object->HasValidContext());
   ScriptState* script_state = settings_object->GetScriptState();
@@ -40,7 +40,7 @@
     v8::Local<v8::Value> error = V8ThrowException::CreateTypeError(
         isolate, "Cannot create CSS Module in non-document context");
     return ValueWrapperSyntheticModuleScript::CreateWithError(
-        v8::Local<v8::Value>(), settings_object, params->SourceURL(), KURL(),
+        v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(),
         ScriptFetchOptions(), error);
   }
   CSSStyleSheetInit* init = CSSStyleSheetInit::Create();
@@ -50,26 +50,26 @@
     v8::Local<v8::Value> error = exception_state.GetException();
     exception_state.ClearException();
     return ValueWrapperSyntheticModuleScript::CreateWithError(
-        v8::Local<v8::Value>(), settings_object, params->SourceURL(), KURL(),
+        v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(),
         ScriptFetchOptions(), error);
   }
-  style_sheet->replaceSync(params->GetSourceText().ToString(), exception_state);
+  style_sheet->replaceSync(params.GetSourceText().ToString(), exception_state);
   if (exception_state.HadException()) {
     v8::Local<v8::Value> error = exception_state.GetException();
     exception_state.ClearException();
     return ValueWrapperSyntheticModuleScript::CreateWithError(
-        v8::Local<v8::Value>(), settings_object, params->SourceURL(), KURL(),
+        v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(),
         ScriptFetchOptions(), error);
   }
   v8::Local<v8::Value> v8_value_stylesheet = ToV8(style_sheet, script_state);
   return ValueWrapperSyntheticModuleScript::CreateWithDefaultExport(
-      v8_value_stylesheet, settings_object, params->SourceURL(), KURL(),
+      v8_value_stylesheet, settings_object, params.SourceURL(), KURL(),
       ScriptFetchOptions());
 }
 
 ValueWrapperSyntheticModuleScript*
 ValueWrapperSyntheticModuleScript::CreateJSONWrapperSyntheticModuleScript(
-    const base::Optional<ModuleScriptCreationParams>& params,
+    const ModuleScriptCreationParams& params,
     Modulator* settings_object) {
   DCHECK(settings_object->HasValidContext());
   ScriptState::Scope scope(settings_object->GetScriptState());
@@ -78,7 +78,7 @@
   v8::Isolate* isolate = context->GetIsolate();
   v8::TryCatch try_catch(isolate);
   v8::Local<v8::String> original_json =
-      V8String(isolate, params->GetSourceText());
+      V8String(isolate, params.GetSourceText());
   v8::Local<v8::Value> parsed_json;
   ExceptionState exception_state(isolate, ExceptionState::kExecutionContext,
                                  "ModuleScriptLoader",
@@ -102,11 +102,11 @@
     v8::Local<v8::Value> error = exception_state.GetException();
     exception_state.ClearException();
     return ValueWrapperSyntheticModuleScript::CreateWithError(
-        parsed_json, settings_object, params->SourceURL(), KURL(),
+        parsed_json, settings_object, params.SourceURL(), KURL(),
         ScriptFetchOptions(), error);
   } else {
     return ValueWrapperSyntheticModuleScript::CreateWithDefaultExport(
-        parsed_json, settings_object, params->SourceURL(), KURL(),
+        parsed_json, settings_object, params.SourceURL(), KURL(),
         ScriptFetchOptions());
   }
 }
diff --git a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.h b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.h
index 184fe69..edb234e 100644
--- a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.h
+++ b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.h
@@ -26,14 +26,12 @@
     : public ModuleScript {
  public:
   static ValueWrapperSyntheticModuleScript*
-  CreateCSSWrapperSyntheticModuleScript(
-      const base::Optional<ModuleScriptCreationParams>& params,
-      Modulator* settings_object);
+  CreateCSSWrapperSyntheticModuleScript(const ModuleScriptCreationParams&,
+                                        Modulator* settings_object);
 
   static ValueWrapperSyntheticModuleScript*
-  CreateJSONWrapperSyntheticModuleScript(
-      const base::Optional<ModuleScriptCreationParams>& params,
-      Modulator* settings_object);
+  CreateJSONWrapperSyntheticModuleScript(const ModuleScriptCreationParams&,
+                                         Modulator* settings_object);
 
   static ValueWrapperSyntheticModuleScript* CreateWithDefaultExport(
       v8::Local<v8::Value> value,
@@ -78,4 +76,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_VALUE_WRAPPER_SYNTHETIC_MODULE_SCRIPT_H_
\ No newline at end of file
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_VALUE_WRAPPER_SYNTHETIC_MODULE_SCRIPT_H_
diff --git a/third_party/blink/renderer/core/testing/module_test_base.cc b/third_party/blink/renderer/core/testing/module_test_base.cc
index 0d94895..41aa9e82 100644
--- a/third_party/blink/renderer/core/testing/module_test_base.cc
+++ b/third_party/blink/renderer/core/testing/module_test_base.cc
@@ -27,10 +27,11 @@
     const KURL& url,
     ExceptionState& exception_state) {
   ModuleScriptCreationParams params(
-      url, ModuleScriptCreationParams::ModuleType::kJavaScriptModule,
+      /*source_url=*/url, /*base_url=*/url,
+      ModuleScriptCreationParams::ModuleType::kJavaScriptModule,
       ParkableString(source.Impl()), nullptr,
       network::mojom::CredentialsMode::kOmit);
-  return ModuleRecord::Compile(isolate, params, url, ScriptFetchOptions(),
+  return ModuleRecord::Compile(isolate, params, ScriptFetchOptions(),
                                TextPosition::MinimumPosition(),
                                exception_state);
 }
diff --git a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
index 731ba99..64d82de4 100644
--- a/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
+++ b/third_party/blink/renderer/core/workers/worklet_module_responses_map_test.cc
@@ -47,22 +47,21 @@
    public:
     enum class Result { kInitial, kOK, kFailed };
 
-    void NotifyFetchFinished(
-        const base::Optional<ModuleScriptCreationParams>& params,
+    void NotifyFetchFinishedError(
         const HeapVector<Member<ConsoleMessage>>&) override {
       ASSERT_EQ(Result::kInitial, result_);
-      if (params) {
-        result_ = Result::kOK;
-        params_.emplace(*params);
-      } else {
-        result_ = Result::kFailed;
-      }
+      result_ = Result::kFailed;
+    }
+
+    void NotifyFetchFinishedSuccess(
+        const ModuleScriptCreationParams& params) override {
+      ASSERT_EQ(Result::kInitial, result_);
+      result_ = Result::kOK;
+      params_.emplace(std::move(params));
     }
 
     Result GetResult() const { return result_; }
-    base::Optional<ModuleScriptCreationParams> GetParams() const {
-      return params_;
-    }
+    bool HasParams() const { return params_.has_value(); }
 
    private:
     Result result_ = Result::kInitial;
@@ -108,7 +107,7 @@
   clients.push_back(MakeGarbageCollected<ClientImpl>());
   Fetch(kUrl, clients[0]);
   EXPECT_EQ(ClientImpl::Result::kInitial, clients[0]->GetResult());
-  EXPECT_FALSE(clients[0]->GetParams().has_value());
+  EXPECT_FALSE(clients[0]->HasParams());
 
   // The entry is now being fetched. Following read calls should wait for the
   // completion.
@@ -125,7 +124,7 @@
   RunUntilIdle();
   for (auto client : clients) {
     EXPECT_EQ(ClientImpl::Result::kOK, client->GetResult());
-    EXPECT_TRUE(client->GetParams().has_value());
+    EXPECT_TRUE(client->HasParams());
   }
 }
 
@@ -139,7 +138,7 @@
   clients.push_back(MakeGarbageCollected<ClientImpl>());
   Fetch(kUrl, clients[0]);
   EXPECT_EQ(ClientImpl::Result::kInitial, clients[0]->GetResult());
-  EXPECT_FALSE(clients[0]->GetParams().has_value());
+  EXPECT_FALSE(clients[0]->HasParams());
 
   // The entry is now being fetched. Following read calls should wait for the
   // completion.
@@ -156,7 +155,7 @@
   RunUntilIdle();
   for (auto client : clients) {
     EXPECT_EQ(ClientImpl::Result::kFailed, client->GetResult());
-    EXPECT_FALSE(client->GetParams().has_value());
+    EXPECT_FALSE(client->HasParams());
   }
 }
 
@@ -174,7 +173,7 @@
   clients.push_back(MakeGarbageCollected<ClientImpl>());
   Fetch(kUrl1, clients[0]);
   EXPECT_EQ(ClientImpl::Result::kInitial, clients[0]->GetResult());
-  EXPECT_FALSE(clients[0]->GetParams().has_value());
+  EXPECT_FALSE(clients[0]->HasParams());
 
   // The entry is now being fetched. Following read calls for |kUrl1| should
   // wait for the completion.
@@ -186,7 +185,7 @@
   clients.push_back(MakeGarbageCollected<ClientImpl>());
   Fetch(kUrl2, clients[2]);
   EXPECT_EQ(ClientImpl::Result::kInitial, clients[2]->GetResult());
-  EXPECT_FALSE(clients[2]->GetParams().has_value());
+  EXPECT_FALSE(clients[2]->HasParams());
 
   // The entry is now being fetched. Following read calls for |kUrl2| should
   // wait for the completion.
@@ -201,13 +200,13 @@
   platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   RunUntilIdle();
   EXPECT_EQ(ClientImpl::Result::kFailed, clients[0]->GetResult());
-  EXPECT_FALSE(clients[0]->GetParams().has_value());
+  EXPECT_FALSE(clients[0]->HasParams());
   EXPECT_EQ(ClientImpl::Result::kFailed, clients[1]->GetResult());
-  EXPECT_FALSE(clients[1]->GetParams().has_value());
+  EXPECT_FALSE(clients[1]->HasParams());
   EXPECT_EQ(ClientImpl::Result::kOK, clients[2]->GetResult());
-  EXPECT_TRUE(clients[2]->GetParams().has_value());
+  EXPECT_TRUE(clients[2]->HasParams());
   EXPECT_EQ(ClientImpl::Result::kOK, clients[3]->GetResult());
-  EXPECT_TRUE(clients[3]->GetParams().has_value());
+  EXPECT_TRUE(clients[3]->HasParams());
 }
 
 TEST_F(WorkletModuleResponsesMapTest, InvalidURL) {
@@ -217,7 +216,7 @@
   Fetch(kEmptyURL, client1);
   RunUntilIdle();
   EXPECT_EQ(ClientImpl::Result::kFailed, client1->GetResult());
-  EXPECT_FALSE(client1->GetParams().has_value());
+  EXPECT_FALSE(client1->HasParams());
 
   const KURL kNullURL = NullURL();
   ASSERT_TRUE(kNullURL.IsNull());
@@ -225,7 +224,7 @@
   Fetch(kNullURL, client2);
   RunUntilIdle();
   EXPECT_EQ(ClientImpl::Result::kFailed, client2->GetResult());
-  EXPECT_FALSE(client2->GetParams().has_value());
+  EXPECT_FALSE(client2->HasParams());
 
   const KURL kInvalidURL;
   ASSERT_FALSE(kInvalidURL.IsValid());
@@ -233,7 +232,7 @@
   Fetch(kInvalidURL, client3);
   RunUntilIdle();
   EXPECT_EQ(ClientImpl::Result::kFailed, client3->GetResult());
-  EXPECT_FALSE(client3->GetParams().has_value());
+  EXPECT_FALSE(client3->HasParams());
 }
 
 TEST_F(WorkletModuleResponsesMapTest, Dispose) {
@@ -252,7 +251,7 @@
   clients.push_back(MakeGarbageCollected<ClientImpl>());
   Fetch(kUrl1, clients[0]);
   EXPECT_EQ(ClientImpl::Result::kInitial, clients[0]->GetResult());
-  EXPECT_FALSE(clients[0]->GetParams().has_value());
+  EXPECT_FALSE(clients[0]->HasParams());
 
   // The entry is now being fetched. Following read calls for |kUrl1| should
   // wait for the completion.
@@ -265,7 +264,7 @@
   clients.push_back(MakeGarbageCollected<ClientImpl>());
   Fetch(kUrl2, clients[2]);
   EXPECT_EQ(ClientImpl::Result::kInitial, clients[2]->GetResult());
-  EXPECT_FALSE(clients[2]->GetParams().has_value());
+  EXPECT_FALSE(clients[2]->HasParams());
 
   // The entry is now being fetched. Following read calls for |kUrl2| should
   // wait for the completion.
@@ -278,7 +277,7 @@
   RunUntilIdle();
   for (auto client : clients) {
     EXPECT_EQ(ClientImpl::Result::kFailed, client->GetResult());
-    EXPECT_FALSE(client->GetParams().has_value());
+    EXPECT_FALSE(client->HasParams());
   }
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/BUILD.gn b/third_party/blink/renderer/modules/accessibility/BUILD.gn
index 87f1b2f..c9b2ea07 100644
--- a/third_party/blink/renderer/modules/accessibility/BUILD.gn
+++ b/third_party/blink/renderer/modules/accessibility/BUILD.gn
@@ -50,8 +50,6 @@
     "ax_slider.h",
     "ax_sparse_attribute_setter.cc",
     "ax_sparse_attribute_setter.h",
-    "ax_svg_root.cc",
-    "ax_svg_root.h",
     "ax_validation_message.cc",
     "ax_validation_message.h",
     "ax_virtual_object.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index c2c3e6c..a3c15ce 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -91,7 +91,6 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_mock_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
-#include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/text/text_direction.h"
@@ -199,8 +198,6 @@
       return ax::mojom::blink::Role::kImageMap;
     if (IsA<HTMLInputElement>(node))
       return ButtonRoleType();
-    if (IsSVGImage())
-      return ax::mojom::blink::Role::kSvgRoot;
 
     return ax::mojom::blink::Role::kImage;
   }
@@ -292,8 +289,6 @@
 void AXLayoutObject::Detach() {
   AXNodeObject::Detach();
 
-  DetachRemoteSVGRoot();
-
 #if DCHECK_IS_ON()
   if (layout_object_)
     layout_object_->SetHasAXObject(false);
@@ -1534,14 +1529,6 @@
   return result;
 }
 
-AXObject* AXLayoutObject::ElementAccessibilityHitTest(
-    const IntPoint& point) const {
-  if (IsSVGImage())
-    return RemoteSVGElementHitTest(point);
-
-  return AXObject::ElementAccessibilityHitTest(point);
-}
-
 //
 // Low-level accessibility tree exploration, only for use within the
 // accessibility module.
@@ -2449,34 +2436,4 @@
   return nullptr;
 }
 
-void AXLayoutObject::DetachRemoteSVGRoot() {
-  if (AXSVGRoot* root = RemoteSVGRootElement())
-    root->SetParent(nullptr);
-}
-
-AXObject* AXLayoutObject::RemoteSVGElementHitTest(const IntPoint& point) const {
-  AXObject* remote = RemoteSVGRootElement();
-  if (!remote)
-    return nullptr;
-
-  IntSize offset =
-      point - RoundedIntPoint(GetBoundsInFrameCoordinates().Location());
-  return remote->AccessibilityHitTest(IntPoint(offset));
-}
-
-// The boundingBox for elements within the remote SVG element needs to be offset
-// by its position within the parent page, otherwise they are in relative
-// coordinates only.
-void AXLayoutObject::OffsetBoundingBoxForRemoteSVGElement(
-    LayoutRect& rect) const {
-  for (AXObject* parent = ParentObject(); parent;
-       parent = parent->ParentObject()) {
-    if (parent->IsAXSVGRoot()) {
-      rect.MoveBy(
-          parent->ParentObject()->GetBoundsInFrameCoordinates().Location());
-      break;
-    }
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
index 799fb9a..aefa84c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -135,7 +135,6 @@
 
   // Hit testing.
   AXObject* AccessibilityHitTest(const IntPoint&) const override;
-  AXObject* ElementAccessibilityHitTest(const IntPoint&) const override;
 
   // High-level accessibility tree access. Other modules should only use these
   // functions.
@@ -183,9 +182,6 @@
   bool IsTabItemSelected() const;
   AXObject* AccessibilityImageMapHitTest(HTMLAreaElement*,
                                          const IntPoint&) const;
-  void DetachRemoteSVGRoot();
-  AXObject* RemoteSVGElementHitTest(const IntPoint&) const;
-  void OffsetBoundingBoxForRemoteSVGElement(LayoutRect&) const;
   bool FindAllTableCellsWithRole(ax::mojom::blink::Role, AXObjectVector&) const;
 
   LayoutRect ComputeElementRect() const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 0c083f2..f056d5a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -99,14 +99,12 @@
 #include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/svg/svg_element.h"
-#include "third_party/blink/renderer/core/svg/svg_svg_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_image_map_link.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_layout_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_range.h"
-#include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/platform/graphics/image_data_buffer.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
@@ -3213,17 +3211,6 @@
   }
 }
 
-AXSVGRoot* AXNodeObject::RemoteSVGRootElement() const {
-  // FIXME(dmazzoni): none of this code properly handled multiple references to
-  // the same remote SVG document. I'm disabling this support until it can be
-  // fixed properly.
-  return nullptr;
-}
-
-void AXNodeObject::AddRemoteSVGChildren() {
-  AddChild(RemoteSVGRootElement());
-}
-
 void AXNodeObject::AddNodeChildren() {
   if (!node_)
     return;
@@ -3262,7 +3249,6 @@
     AddNodeChildren();
 
   AddPopupChildren();
-  AddRemoteSVGChildren();
   AddImageMapChildren();
   AddInlineTextBoxChildren(false);
   AddValidationMessageChild();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index edc1999..124ca76 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -37,7 +37,6 @@
 namespace blink {
 
 class AXObjectCacheImpl;
-class AXSVGRoot;
 class Element;
 class HTMLLabelElement;
 class Node;
@@ -270,10 +269,6 @@
   // Inline text boxes.
   void LoadInlineTextBoxes() override;
 
-  // SVG.
-  bool IsSVGImage() const { return RemoteSVGRootElement(); }
-  AXSVGRoot* RemoteSVGRootElement() const;
-
   virtual LayoutBoxModelObject* GetLayoutBoxModelObject() const {
     return nullptr;
   }
@@ -313,7 +308,6 @@
   void AddInlineTextBoxChildren(bool force);
   void AddImageMapChildren();
   void AddPopupChildren();
-  void AddRemoteSVGChildren();
   bool IsHtmlTable() const;
   void AddTableChildren();
   void AddValidationMessageChild();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 5a4d9ba5..67164e5 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1183,10 +1183,6 @@
   return false;
 }
 
-bool AXObject::IsAXSVGRoot() const {
-  return false;
-}
-
 bool AXObject::IsValidationMessage() const {
   return false;
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 4b52e757..0715d9d2 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -409,7 +409,6 @@
   virtual bool IsProgressIndicator() const;
   virtual bool IsAXRadioInput() const;
   virtual bool IsSlider() const;
-  virtual bool IsAXSVGRoot() const;
   virtual bool IsValidationMessage() const;
   virtual bool IsVirtualObject() const;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 61423b6..9b0611f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -86,7 +86,6 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_progress_indicator.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_relation_cache.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_slider.h"
-#include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_validation_message.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_virtual_object.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
@@ -454,9 +453,6 @@
       return MakeGarbageCollected<AXSlider>(layout_object, *this);
   }
 
-  if (layout_object->IsSVGRoot())
-    return MakeGarbageCollected<AXSVGRoot>(layout_object, *this);
-
   if (layout_object->IsBoxModelObject()) {
     auto* css_box = To<LayoutBoxModelObject>(layout_object);
     if (auto* select_element = DynamicTo<HTMLSelectElement>(node)) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_svg_root.cc b/third_party/blink/renderer/modules/accessibility/ax_svg_root.cc
deleted file mode 100644
index 6bee578..0000000
--- a/third_party/blink/renderer/modules/accessibility/ax_svg_root.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- *     its contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/modules/accessibility/ax_svg_root.h"
-
-#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
-
-namespace blink {
-
-AXSVGRoot::AXSVGRoot(LayoutObject* layout_object,
-                     AXObjectCacheImpl& ax_object_cache)
-    : AXLayoutObject(layout_object, ax_object_cache) {}
-
-AXSVGRoot::~AXSVGRoot() = default;
-
-void AXSVGRoot::SetParent(AXObject* parent) {
-  // Only update the parent to another objcet if it wasn't already set to
-  // something. Multiple elements in an HTML document can reference
-  // the same remote SVG document, and in that case the parent should just
-  // stay with the first one.
-  if (!parent_ || !parent)
-    parent_ = parent;
-}
-
-AXObject* AXSVGRoot::ComputeParent() const {
-  DCHECK(!IsDetached());
-  // If a parent was set because this is a remote SVG resource, use that
-  // but otherwise, we should rely on the standard layout tree for the parent.
-  if (parent_)
-    return parent_;
-
-  return AXLayoutObject::ComputeParent();
-}
-
-// SVG AAM 1.0 S8.2: the default role for an SVG root is "group".
-ax::mojom::Role AXSVGRoot::DetermineAccessibilityRole() {
-  ax::mojom::Role role = AXLayoutObject::DetermineAccessibilityRole();
-  if (role == ax::mojom::Role::kUnknown)
-    role = ax::mojom::Role::kGroup;
-  return role;
-}
-
-// SVG elements are only ignored if they are a descendant of a leaf or when a
-// generic element would also be ignored.
-bool AXSVGRoot::ComputeAccessibilityIsIgnored(IgnoredReasons* reasons) const {
-  if (IsDescendantOfLeafNode())
-    return AXLayoutObject::ComputeAccessibilityIsIgnored(reasons);
-
-  return AccessibilityIsIgnoredByDefault(reasons);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_svg_root.h b/third_party/blink/renderer/modules/accessibility/ax_svg_root.h
deleted file mode 100644
index 32ce72f..0000000
--- a/third_party/blink/renderer/modules/accessibility/ax_svg_root.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- * 2.  Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- *     its contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_SVG_ROOT_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_SVG_ROOT_H_
-
-#include "base/macros.h"
-#include "third_party/blink/renderer/modules/accessibility/ax_layout_object.h"
-
-namespace blink {
-
-class AXObjectCacheImpl;
-
-class AXSVGRoot final : public AXLayoutObject {
- public:
-  AXSVGRoot(LayoutObject*, AXObjectCacheImpl&);
-  ~AXSVGRoot() override;
-
-  void SetParent(AXObject*) override;
-
-  ax::mojom::Role DetermineAccessibilityRole() override;
-  bool ComputeAccessibilityIsIgnored(IgnoredReasons*) const override;
-
- private:
-  AXObject* ComputeParent() const override;
-  bool IsAXSVGRoot() const override { return true; }
-
-  DISALLOW_COPY_AND_ASSIGN(AXSVGRoot);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_SVG_ROOT_H_
diff --git a/third_party/blink/renderer/modules/native_io/BUILD.gn b/third_party/blink/renderer/modules/native_io/BUILD.gn
index f06538c..0ca85149 100644
--- a/third_party/blink/renderer/modules/native_io/BUILD.gn
+++ b/third_party/blink/renderer/modules/native_io/BUILD.gn
@@ -12,10 +12,10 @@
     "native_io_error.h",
     "native_io_file.cc",
     "native_io_file.h",
+    "native_io_file_manager.cc",
+    "native_io_file_manager.h",
     "native_io_file_sync.cc",
     "native_io_file_sync.h",
-    "native_io_manager.cc",
-    "native_io_manager.h",
   ]
 
   deps = [ "//third_party/blink/renderer/platform" ]
diff --git a/third_party/blink/renderer/modules/native_io/global_native_io.cc b/third_party/blink/renderer/modules/native_io/global_native_io.cc
index b381fe7..a0418c0f 100644
--- a/third_party/blink/renderer/modules/native_io/global_native_io.cc
+++ b/third_party/blink/renderer/modules/native_io/global_native_io.cc
@@ -13,7 +13,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
-#include "third_party/blink/renderer/modules/native_io/native_io_manager.h"
+#include "third_party/blink/renderer/modules/native_io/native_io_file_manager.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
@@ -41,8 +41,8 @@
   explicit GlobalNativeIOImpl(T& supplementable)
       : Supplement<T>(supplementable) {}
 
-  NativeIOManager* GetNativeIOManager(T& scope) {
-    if (!native_io_manager_) {
+  NativeIOFileManager* GetNativeIOFileManager(T& scope) {
+    if (!native_io_file_manager_) {
       ExecutionContext* execution_context = scope.GetExecutionContext();
       if (&execution_context->GetBrowserInterfaceBroker() ==
           &GetEmptyBrowserInterfaceBroker()) {
@@ -53,19 +53,19 @@
       execution_context->GetBrowserInterfaceBroker().GetInterface(
           backend.BindNewPipeAndPassReceiver(
               execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
-      native_io_manager_ = MakeGarbageCollected<NativeIOManager>(
+      native_io_file_manager_ = MakeGarbageCollected<NativeIOFileManager>(
           execution_context, std::move(backend));
     }
-    return native_io_manager_;
+    return native_io_file_manager_;
   }
 
   void Trace(Visitor* visitor) const override {
-    visitor->Trace(native_io_manager_);
+    visitor->Trace(native_io_file_manager_);
     Supplement<T>::Trace(visitor);
   }
 
  private:
-  Member<NativeIOManager> native_io_manager_;
+  Member<NativeIOFileManager> native_io_file_manager_;
 };
 
 // static
@@ -75,15 +75,15 @@
 }  // namespace
 
 // static
-NativeIOManager* GlobalNativeIO::nativeIO(LocalDOMWindow& window) {
-  return GlobalNativeIOImpl<LocalDOMWindow>::From(window).GetNativeIOManager(
-      window);
+NativeIOFileManager* GlobalNativeIO::nativeIO(LocalDOMWindow& window) {
+  return GlobalNativeIOImpl<LocalDOMWindow>::From(window)
+      .GetNativeIOFileManager(window);
 }
 
 // static
-NativeIOManager* GlobalNativeIO::nativeIO(WorkerGlobalScope& worker) {
-  return GlobalNativeIOImpl<WorkerGlobalScope>::From(worker).GetNativeIOManager(
-      worker);
+NativeIOFileManager* GlobalNativeIO::nativeIO(WorkerGlobalScope& worker) {
+  return GlobalNativeIOImpl<WorkerGlobalScope>::From(worker)
+      .GetNativeIOFileManager(worker);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_io/global_native_io.h b/third_party/blink/renderer/modules/native_io/global_native_io.h
index bb9b05b..1a9bc02 100644
--- a/third_party/blink/renderer/modules/native_io/global_native_io.h
+++ b/third_party/blink/renderer/modules/native_io/global_native_io.h
@@ -10,7 +10,7 @@
 namespace blink {
 
 class LocalDOMWindow;
-class NativeIOManager;
+class NativeIOFileManager;
 class WorkerGlobalScope;
 
 // The "nativeIO" attribute on the Window global and Worker global scope.
@@ -18,8 +18,8 @@
   STATIC_ONLY(GlobalNativeIO);
 
  public:
-  static NativeIOManager* nativeIO(LocalDOMWindow&);
-  static NativeIOManager* nativeIO(WorkerGlobalScope&);
+  static NativeIOFileManager* nativeIO(LocalDOMWindow&);
+  static NativeIOFileManager* nativeIO(WorkerGlobalScope&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_io/idls.gni b/third_party/blink/renderer/modules/native_io/idls.gni
index 694bfa92..5f9a67a 100644
--- a/third_party/blink/renderer/modules/native_io/idls.gni
+++ b/third_party/blink/renderer/modules/native_io/idls.gni
@@ -4,8 +4,8 @@
 
 modules_idl_files = [
   "native_io_file.idl",
+  "native_io_file_manager.idl",
   "native_io_file_sync.idl",
-  "native_io_manager.idl",
 ]
 
 modules_dependency_idl_files = [
diff --git a/third_party/blink/renderer/modules/native_io/native_io_manager.cc b/third_party/blink/renderer/modules/native_io/native_io_file_manager.cc
similarity index 87%
rename from third_party/blink/renderer/modules/native_io/native_io_manager.cc
rename to third_party/blink/renderer/modules/native_io/native_io_file_manager.cc
index deb3548..d25e795 100644
--- a/third_party/blink/renderer/modules/native_io/native_io_manager.cc
+++ b/third_party/blink/renderer/modules/native_io/native_io_file_manager.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/native_io/native_io_manager.h"
+#include "third_party/blink/renderer/modules/native_io/native_io_file_manager.h"
 
 #include <algorithm>
 #include <utility>
@@ -120,7 +120,7 @@
 
 }  // namespace
 
-NativeIOManager::NativeIOManager(
+NativeIOFileManager::NativeIOFileManager(
     ExecutionContext* execution_context,
     HeapMojoRemote<mojom::blink::NativeIOHost> backend)
     : ExecutionContextClient(execution_context),
@@ -129,14 +129,14 @@
           execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI)),
       backend_(std::move(backend)) {
   backend_.set_disconnect_handler(WTF::Bind(
-      &NativeIOManager::OnBackendDisconnect, WrapWeakPersistent(this)));
+      &NativeIOFileManager::OnBackendDisconnect, WrapWeakPersistent(this)));
 }
 
-NativeIOManager::~NativeIOManager() = default;
+NativeIOFileManager::~NativeIOFileManager() = default;
 
-ScriptPromise NativeIOManager::open(ScriptState* script_state,
-                                    String name,
-                                    ExceptionState& exception_state) {
+ScriptPromise NativeIOFileManager::open(ScriptState* script_state,
+                                        String name,
+                                        ExceptionState& exception_state) {
   if (!IsValidNativeIOName(name)) {
     exception_state.ThrowTypeError("Invalid file name");
     return ScriptPromise();
@@ -166,9 +166,9 @@
   return resolver->Promise();
 }
 
-ScriptPromise NativeIOManager::Delete(ScriptState* script_state,
-                                      String name,
-                                      ExceptionState& exception_state) {
+ScriptPromise NativeIOFileManager::Delete(ScriptState* script_state,
+                                          String name,
+                                          ExceptionState& exception_state) {
   if (!IsValidNativeIOName(name)) {
     exception_state.ThrowTypeError("Invalid file name");
     return ScriptPromise();
@@ -188,8 +188,8 @@
   return resolver->Promise();
 }
 
-ScriptPromise NativeIOManager::getAll(ScriptState* script_state,
-                                      ExceptionState& exception_state) {
+ScriptPromise NativeIOFileManager::getAll(ScriptState* script_state,
+                                          ExceptionState& exception_state) {
   if (!backend_.is_bound()) {
     ThrowNativeIOWithError(exception_state,
                            mojom::blink::NativeIOError::New(
@@ -204,10 +204,10 @@
   return resolver->Promise();
 }
 
-ScriptPromise NativeIOManager::rename(ScriptState* script_state,
-                                      String old_name,
-                                      String new_name,
-                                      ExceptionState& exception_state) {
+ScriptPromise NativeIOFileManager::rename(ScriptState* script_state,
+                                          String old_name,
+                                          String new_name,
+                                          ExceptionState& exception_state) {
   if (!IsValidNativeIOName(old_name) || !IsValidNativeIOName(new_name)) {
     exception_state.ThrowTypeError("Invalid file name");
     return ScriptPromise();
@@ -227,8 +227,9 @@
   return resolver->Promise();
 }
 
-NativeIOFileSync* NativeIOManager::openSync(String name,
-                                            ExceptionState& exception_state) {
+NativeIOFileSync* NativeIOFileManager::openSync(
+    String name,
+    ExceptionState& exception_state) {
   if (!IsValidNativeIOName(name)) {
     exception_state.ThrowTypeError("Invalid file name");
     return nullptr;
@@ -266,7 +267,8 @@
       std::move(backing_file), std::move(backend_file), execution_context);
 }
 
-void NativeIOManager::deleteSync(String name, ExceptionState& exception_state) {
+void NativeIOFileManager::deleteSync(String name,
+                                     ExceptionState& exception_state) {
   if (!IsValidNativeIOName(name)) {
     exception_state.ThrowTypeError("Invalid file name");
     return;
@@ -290,7 +292,8 @@
   DCHECK(call_succeeded) << "Mojo call failed";
 }
 
-Vector<String> NativeIOManager::getAllSync(ExceptionState& exception_state) {
+Vector<String> NativeIOFileManager::getAllSync(
+    ExceptionState& exception_state) {
   Vector<String> result;
   if (!backend_.is_bound()) {
     ThrowNativeIOWithError(exception_state,
@@ -311,9 +314,9 @@
   return result;
 }
 
-void NativeIOManager::renameSync(String old_name,
-                                 String new_name,
-                                 ExceptionState& exception_state) {
+void NativeIOFileManager::renameSync(String old_name,
+                                     String new_name,
+                                     ExceptionState& exception_state) {
   if (!IsValidNativeIOName(old_name) || !IsValidNativeIOName(new_name)) {
     exception_state.ThrowTypeError("Invalid file name");
     return;
@@ -338,13 +341,13 @@
   DCHECK(call_succeeded) << "Mojo call failed";
 }
 
-void NativeIOManager::Trace(Visitor* visitor) const {
+void NativeIOFileManager::Trace(Visitor* visitor) const {
   visitor->Trace(backend_);
   ScriptWrappable::Trace(visitor);
   ExecutionContextClient::Trace(visitor);
 }
 
-void NativeIOManager::OnBackendDisconnect() {
+void NativeIOFileManager::OnBackendDisconnect() {
   backend_.reset();
 }
 
diff --git a/third_party/blink/renderer/modules/native_io/native_io_manager.h b/third_party/blink/renderer/modules/native_io/native_io_file_manager.h
similarity index 78%
rename from third_party/blink/renderer/modules/native_io/native_io_manager.h
rename to third_party/blink/renderer/modules/native_io/native_io_file_manager.h
index 52d0771f..f85808ad 100644
--- a/third_party/blink/renderer/modules/native_io/native_io_manager.h
+++ b/third_party/blink/renderer/modules/native_io/native_io_file_manager.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_NATIVE_IO_NATIVE_IO_MANAGER_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_MANAGER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_MANAGER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_MANAGER_H_
 
 #include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -21,20 +21,21 @@
 class NativeIOFileSync;
 class ScriptState;
 
-class NativeIOManager final : public ScriptWrappable,
-                              public ExecutionContextClient {
+class NativeIOFileManager final : public ScriptWrappable,
+                                  public ExecutionContextClient {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit NativeIOManager(ExecutionContext*,
-                           HeapMojoRemote<mojom::blink::NativeIOHost> backend);
+  explicit NativeIOFileManager(
+      ExecutionContext*,
+      HeapMojoRemote<mojom::blink::NativeIOHost> backend);
 
-  NativeIOManager(const NativeIOManager&) = delete;
-  NativeIOManager& operator=(const NativeIOManager&) = delete;
+  NativeIOFileManager(const NativeIOFileManager&) = delete;
+  NativeIOFileManager& operator=(const NativeIOFileManager&) = delete;
 
   // Needed because of the
   // mojo::Remote<blink::mojom::NativeIOHost>
-  ~NativeIOManager() override;
+  ~NativeIOFileManager() override;
 
   ScriptPromise open(ScriptState*, String name, ExceptionState&);
   ScriptPromise Delete(ScriptState*, String name, ExceptionState&);
@@ -65,4 +66,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_MANAGER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_FILE_MANAGER_H_
diff --git a/third_party/blink/renderer/modules/native_io/native_io_file_manager.idl b/third_party/blink/renderer/modules/native_io/native_io_file_manager.idl
new file mode 100644
index 0000000..911642d
--- /dev/null
+++ b/third_party/blink/renderer/modules/native_io/native_io_file_manager.idl
@@ -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.
+
+// https://github.com/fivedots/nativeio-explainer
+
+[Exposed = (Window, Worker),
+ RuntimeEnabled = NativeIO,
+ SecureContext] interface NativeIOFileManager {
+  [ CallWith = ScriptState, RaisesException ] Promise<NativeIOFile> open(
+      DOMString name);
+  [ Exposed = DedicatedWorker, RaisesException ] NativeIOFileSync openSync(
+      DOMString name);
+
+  [ CallWith = ScriptState, ImplementedAs = Delete,
+    RaisesException ] Promise<void> delete (DOMString name);
+  [ Exposed = DedicatedWorker, RaisesException ] void deleteSync(
+      DOMString name);
+
+  [ CallWith = ScriptState, RaisesException ] Promise<sequence<DOMString>>
+  getAll();
+  [ Exposed = DedicatedWorker, RaisesException ] sequence<DOMString>
+  getAllSync();
+
+  [ CallWith = ScriptState, RaisesException ] Promise<void> rename(
+      DOMString old_name, DOMString new_name);
+  [ Exposed = DedicatedWorker, RaisesException ] void renameSync(
+      DOMString old_name, DOMString new_name);
+};
diff --git a/third_party/blink/renderer/modules/native_io/native_io_manager.idl b/third_party/blink/renderer/modules/native_io/native_io_manager.idl
deleted file mode 100644
index 860eff12..0000000
--- a/third_party/blink/renderer/modules/native_io/native_io_manager.idl
+++ /dev/null
@@ -1,33 +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.
-
-// https://github.com/fivedots/nativeio-explainer
-
-[
-  Exposed=(Window,Worker),
-  RuntimeEnabled=NativeIO,
-  SecureContext
-] interface NativeIOManager {
-  [
-    CallWith=ScriptState, RaisesException
-  ] Promise<NativeIOFile> open(DOMString name);
-  [
-    Exposed=DedicatedWorker, RaisesException
-  ] NativeIOFileSync openSync(DOMString name);
-
-  [
-    CallWith=ScriptState, ImplementedAs=Delete, RaisesException
-  ] Promise<void> delete(DOMString name);
-  [Exposed=DedicatedWorker, RaisesException] void deleteSync(DOMString name);
-
-  [
-    CallWith=ScriptState, RaisesException
-  ] Promise<sequence<DOMString>> getAll();
-  [Exposed=DedicatedWorker, RaisesException] sequence<DOMString> getAllSync();
-
-  [
-    CallWith=ScriptState, RaisesException
-   ] Promise<void> rename(DOMString old_name, DOMString new_name);
-  [Exposed=DedicatedWorker, RaisesException] void renameSync(DOMString old_name, DOMString new_name);
-};
diff --git a/third_party/blink/renderer/modules/native_io/window_native_io.idl b/third_party/blink/renderer/modules/native_io/window_native_io.idl
index 1b78cc42..45844fa 100644
--- a/third_party/blink/renderer/modules/native_io/window_native_io.idl
+++ b/third_party/blink/renderer/modules/native_io/window_native_io.idl
@@ -9,5 +9,5 @@
   RuntimeEnabled=NativeIO,
   SecureContext
 ] partial interface Window {
-  readonly attribute NativeIOManager nativeIO;
+  readonly attribute NativeIOFileManager nativeIO;
 };
diff --git a/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl b/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl
index aa03a6c..a960868 100644
--- a/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl
+++ b/third_party/blink/renderer/modules/native_io/worker_global_scope_native_io.idl
@@ -9,5 +9,5 @@
   RuntimeEnabled=NativeIO,
   SecureContext
 ] partial interface WorkerGlobalScope {
-  readonly attribute NativeIOManager nativeIO;
+  readonly attribute NativeIOFileManager nativeIO;
 };
diff --git a/third_party/blink/renderer/modules/webid/web_id.cc b/third_party/blink/renderer/modules/webid/web_id.cc
index 85a0622..119757dd 100644
--- a/third_party/blink/renderer/modules/webid/web_id.cc
+++ b/third_party/blink/renderer/modules/webid/web_id.cc
@@ -21,14 +21,59 @@
 void OnRequestIdToken(ScriptPromiseResolver* resolver,
                       mojom::blink::RequestIdTokenStatus status,
                       const WTF::String& id_token) {
-  // TODO(kenrb): Provide better messages for different error codes.
-  if (status != mojom::blink::RequestIdTokenStatus::kSuccess) {
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kNetworkError,
-        "Error loading the identity provider."));
-    return;
+  switch (status) {
+    case mojom::blink::RequestIdTokenStatus::kApprovalDeclined: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError, "User declined the sign-in attempt."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kErrorTooManyRequests: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError,
+          "Only one WebID request may be outstanding at one time."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::
+        kErrorWebIdNotSupportedByProvider: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The indicated provider does not support WebID."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kErrorFetchingWellKnown: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "Error fetching the provider's .well-known configuration."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kErrorInvalidWellKnown: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "Provider's .well-known configuration is invalid."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kErrorFetchingSignin: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "Error attempting to reach the provider's sign-in endpoint."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kErrorInvalidSigninResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "Provider's sign-in response is invalid"));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kError: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError, "Error retrieving an id token."));
+      return;
+    }
+    case mojom::blink::RequestIdTokenStatus::kSuccess: {
+      resolver->Resolve();
+      return;
+    }
   }
-  resolver->Resolve(id_token);
 }
 
 void OnProvideIdToken(ScriptPromiseResolver* resolver,
diff --git a/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc b/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc
index 5bc0a994..026b6634 100644
--- a/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc
+++ b/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc
@@ -92,6 +92,13 @@
                            all_thread_caches_stats.alloc_count);
       base::UmaHistogramPercentage("Memory.PartitionAlloc.ThreadCache.HitRate",
                                    hit_rate_percent);
+
+      int batch_fill_rate_percent =
+          static_cast<int>((100 * all_thread_caches_stats.batch_fill_count) /
+                           all_thread_caches_stats.alloc_count);
+      base::UmaHistogramPercentage(
+          "Memory.PartitionAlloc.ThreadCache.BatchFillRate",
+          batch_fill_rate_percent);
     }
 
     if (thread_cache_stats.alloc_count) {
@@ -101,6 +108,13 @@
       base::UmaHistogramPercentage(
           "Memory.PartitionAlloc.ThreadCache.HitRate.MainThread",
           hit_rate_percent);
+
+      int batch_fill_rate_percent =
+          static_cast<int>((100 * thread_cache_stats.batch_fill_count) /
+                           thread_cache_stats.alloc_count);
+      base::UmaHistogramPercentage(
+          "Memory.PartitionAlloc.ThreadCache.BatchFillRate.MainThread",
+          batch_fill_rate_percent);
     }
   }
 }
diff --git a/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider_test.cc b/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider_test.cc
index b15bf31a..7010e7b 100644
--- a/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider_test.cc
+++ b/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider_test.cc
@@ -30,11 +30,20 @@
                                     1);
   histogram_tester.ExpectTotalCount(
       "Memory.PartitionAlloc.ThreadCache.HitRate.MainThread", 1);
+
+  histogram_tester.ExpectTotalCount(
+      "Memory.PartitionAlloc.ThreadCache.BatchFillRate", 1);
+  histogram_tester.ExpectTotalCount(
+      "Memory.PartitionAlloc.ThreadCache.HitRate.MainThread", 1);
 #else
   histogram_tester.ExpectTotalCount("Memory.PartitionAlloc.ThreadCache.HitRate",
                                     0);
   histogram_tester.ExpectTotalCount(
       "Memory.PartitionAlloc.ThreadCache.HitRate.MainThread", 0);
+  histogram_tester.ExpectTotalCount(
+      "Memory.PartitionAlloc.ThreadCache.BatchFillRate", 0);
+  histogram_tester.ExpectTotalCount(
+      "Memory.PartitionAlloc.ThreadCache.BatchFillRate.MainThread", 0);
 #endif  // !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
         // defined(PA_THREAD_CACHE_SUPPORTED) &&
         // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h b/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h
index 8cb14a2..81bf5e0 100644
--- a/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h
+++ b/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h
@@ -44,6 +44,16 @@
   HeapMojoReceiverSet& operator=(const HeapMojoReceiverSet&) = delete;
 
   // Methods to redirect to mojo::ReceiverSet:
+  void set_disconnect_handler(base::RepeatingClosure handler) {
+    wrapper_->receiver_set().set_disconnect_handler(std::move(handler));
+  }
+
+  void set_disconnect_with_reason_handler(
+      mojo::RepeatingConnectionErrorWithReasonCallback handler) {
+    wrapper_->receiver_set().set_disconnect_with_reason_handler(
+        std::move(handler));
+  }
+
   mojo::ReceiverId Add(mojo::PendingReceiver<Interface> receiver,
                        scoped_refptr<base::SequencedTaskRunner> task_runner) {
     DCHECK(task_runner);
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index ef61940..64d19c23 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -210,6 +210,9 @@
     bool is_secure;
     const char* url;
   } inputs[] = {
+      // TODO(crbug.com/1153336): Should SecurityOrigin::IsSecure be aligned
+      // with network::IsURLPotentiallyTrustworthy?
+      // https://w3c.github.io/webappsec-secure-contexts/#is-url-trustworthy
       {false, "blob:ftp://evil:99/578223a1-8c13-17b3-84d5-eca045ae384a"},
       {false, "blob:http://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
       {false, "file:///etc/passwd"},
@@ -219,8 +222,25 @@
       {true, "blob:https://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
       {true, "https://example.com/"},
       {true, "wss://example.com/"},
-
       {true, "about:blank"},
+      {true, "about:srcdoc"},
+      {true, "about:about"},
+      {true, "data:text/html,Hello"},
+      {false,
+       "filesystem:http://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
+      {true,
+       "filesystem:https://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
+      {true, "blob:data:text/html,Hello"},
+      {true, "blob:about:blank"},
+      {false, "filesystem:data:text/html,Hello"},
+      {false, "filesystem:about:blank"},
+      {false,
+       "blob:blob:https://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
+      {false,
+       "filesystem:blob:https://example.com/"
+       "578223a1-8c13-17b3-84d5-eca045ae384a"},
+      {false, "custom-scheme://example.com"},
+      {true, "quic-transport://example.com/counter"},
       {false, ""},
       {false, "\0"},
   };
@@ -232,10 +252,19 @@
   EXPECT_FALSE(SecurityOrigin::IsSecure(NullURL()));
 }
 
+TEST_F(SecurityOriginTest, IsCustomSchemeSecure) {
+  url::ScopedSchemeRegistryForTests scoped_registry;
+  url::AddSecureScheme("custom-scheme");
+  EXPECT_TRUE(SecurityOrigin::IsSecure(KURL("custom-scheme://example.com")));
+}
+
 TEST_F(SecurityOriginTest, IsSecureViaTrustworthy) {
+  // TODO(crbug.com/1153336): Should SecurityOrigin::IsSecure be aligned with
+  // network::IsURLPotentiallyTrustworthy?
+  // https://w3c.github.io/webappsec-secure-contexts/#is-url-trustworthy
   const char* urls[] = {"http://localhost/", "http://localhost:8080/",
                         "http://127.0.0.1/", "http://127.0.0.1:8080/",
-                        "http://[::1]/"};
+                        "http://[::1]/",     "http://vhost.localhost/"};
 
   for (const char* test : urls) {
     KURL url(test);
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index f0c5d33..c31694d2 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -13,6 +13,7 @@
 # Tests that fail in legacy but pass in NG
 
 # accessibility/
+crbug.com/1159730 accessibility/inline-text-box-next-on-line.html [ Failure ]
 crbug.com/591099 accessibility/listitem-presentation-inherited.html [ Failure ]
 crbug.com/591099 accessibility/presentation-owned-elements.html [ Failure ]
 crbug.com/591099 accessibility/role-attribute.html [ Failure ]
@@ -59,16 +60,24 @@
 ### external/wpt/css/css-flexbox/
 crbug.com/591099 external/wpt/css/css-flexbox/dynamic-baseline-change.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/dynamic-baseline-change-nested.html [ Failure ]
+crbug.com/1132627 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ]
+crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-horiz-002.xhtml [ Failure ]
+crbug.com/553838 external/wpt/css/css-flexbox/flexbox-min-height-auto-002a.html [ Failure ]
+crbug.com/553838 external/wpt/css/css-flexbox/flexbox-min-height-auto-002c.html [ Failure ]
+crbug.com/1132627 external/wpt/css/css-flexbox/flexbox-min-width-auto-002a.html [ Failure ]
+crbug.com/1132627 external/wpt/css/css-flexbox/flexbox-min-width-auto-002c.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/image-as-flexitem-size-003.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/image-as-flexitem-size-003v.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/image-as-flexitem-size-004.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-flexbox/image-as-flexitem-size-004v.html [ Failure ]
-crbug.com/1132627 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ]
 
 ### external/wpt/css/css-fonts/
 crbug.com/591099 external/wpt/css/css-fonts/font-features-across-space-1.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-fonts/font-features-across-space-3.html [ Failure ]
 
+### external/wpt/css/css-images
+crbug.com/1076121 external/wpt/css/css-images/image-orientation/image-orientation-list-style-image.html [ Failure ]
+
 ### external/wpt/css/css-layout-api/
 crbug.com/591099 external/wpt/css/css-layout-api/* [ Skip ]
 
@@ -226,8 +235,17 @@
 crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-002.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/white-space/pre-line-with-space-and-newline.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/break-spaces-with-ideographic-space-001.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/break-spaces-with-ideographic-space-005.html [ Failure ]
 crbug.com/1151784 external/wpt/css/css-text/white-space/seg-break-transformation-016.tentative.html [ Failure ]
 crbug.com/1151784 external/wpt/css/css-text/white-space/seg-break-transformation-017.tentative.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-001.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-002.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-003.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-004.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-005.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-006.html [ Failure ]
+crbug.com/1155633 external/wpt/css/css-text/white-space/trailing-ideographic-space-break-spaces-007.html [ Failure ]
 
 ### external/wpt/css/css-text/word-break/
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Failure ]
@@ -373,10 +391,7 @@
 crbug.com/591099 external/wpt/css/css-content/quotes-033.html [ Failure ]
 
 ### external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/
-crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Failure ]
 crbug.com/886592 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/align3/flex-abspos-staticpos-margin-002.html [ Failure ]
-crbug.com/1132627 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html [ Failure ]
-crbug.com/1132627 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html [ Failure ]
 
 ### external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/emptyspan-1.html [ Failure ]
@@ -384,6 +399,10 @@
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/remove-from-split-inline-6.html [ Failure ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/ib-split/split-inner-inline-2.html [ Failure ]
 
+### external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/
+crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-01a.html [ Failure ]
+crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-01b.html [ Failure ]
+
 ### external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-1a.html [ Failure ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-1b.html [ Failure ]
@@ -498,6 +517,9 @@
 ### virtual/layout_ng_printing/
 virtual/layout_ng_printing/* [ Skip ]
 
+### virtual/layout_ng_table/
+virtual/layout_ng_table/* [ Skip ]
+
 ### virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/
 crbug.com/591099 virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/registration-updateviacache.https.html [ Failure ]
 
@@ -558,8 +580,6 @@
 crbug.com/987000 external/wpt/css/css-flexbox/flex-aspect-ratio-img-column-012.html [ Failure ]
 crbug.com/987000 external/wpt/css/css-flexbox/flex-aspect-ratio-img-column-015.html [ Failure ]
 crbug.com/987000 external/wpt/css/css-flexbox/svg-root-as-flex-item-002.html [ Failure ]
-crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html [ Failure ]
-crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html [ Failure ]
 
 # These would need a rebaseline back from LayoutNGBlockFlow to LayoutBlockFlow
 crbug.com/864567 paint/float/float-under-inline-self-painting-change.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c1e15f2..d565972 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5906,3 +5906,11 @@
 
 # Sheriff 2020-12-14
 crbug.com/1046784 http/tests/devtools/console/console-context-selector.js [ Pass Timeout ]
+
+# Sheriff 2020-12-17
+crbug.com/1043488 [ Win7 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-with-scroll-bar.html [ Pass Failure ]
+crbug.com/1043488 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar.html [ Pass Failure ]
+crbug.com/1043488 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew.html [ Pass Failure ]
+crbug.com/1043488 [ Win7 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl.html [ Pass Failure ]
+crbug.com/1043488 [ Win7 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl.html [ Pass Failure ]
+crbug.com/1043488 [ Win7 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl.html [ Pass Failure ]
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 946a89c..6b0b6033 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
@@ -1074,6 +1074,13 @@
        {}
       ]
      ],
+     "mmultiscripts-with-two-prescripts.html": [
+      "54f938460636bb351d9da20611fd872d6b888ca6",
+      [
+       null,
+       {}
+      ]
+     ],
      "multicol-on-token-elements.html": [
       "9fc00eb6911b625a7443a3e1ec65ada7daf25105",
       [
@@ -255690,11 +255697,11 @@
         []
        ],
        "android_weblayer.py": [
-        "6110450b7282c6cfa4afaca1ec2b2ef580f7a714",
+        "ca93004b6e75822a6dbae7d2d4beacacd94f86e7",
         []
        ],
        "android_webview.py": [
-        "d0773873508fe7f327afe751d32eaddbecea9a3f",
+        "27a09f5d2cdeb5557118a059e0dcb2a88f967c1b",
         []
        ],
        "base.py": [
@@ -255702,15 +255709,15 @@
         []
        ],
        "chrome.py": [
-        "fd251a431c306f3bb3b12b848f548d8eff07a466",
+        "d3e27a9c5f65f7aa754263cb57846285d2857540",
         []
        ],
        "chrome_android.py": [
-        "485b96f31e7deafdddf82fd375aea7d7497e9fa7",
+        "607b783566e1b751dc9d37936a6c64f4d843e3df",
         []
        ],
        "chrome_ios.py": [
-        "6db0188e55ebff8d3a40f9a15dc714419edacbeb",
+        "ecdbc3a17623907dc9bd99505d1e5ee0bc747c7b",
         []
        ],
        "chrome_spki_certs.py": [
@@ -255718,7 +255725,7 @@
         []
        ],
        "edge.py": [
-        "bdb37b3f84c2cdc4010a7a289a53564822a3a34c",
+        "b908684bbb3e7eae9982173ed26a9a9e39b6b437",
         []
        ],
        "edge_webdriver.py": [
@@ -255726,51 +255733,51 @@
         []
        ],
        "edgechromium.py": [
-        "9e1ef3836bebffd1f21ea1fb1aea458a78f12a1f",
+        "67105e425c794c32cd99f8216418d47e640dc7ba",
         []
        ],
        "epiphany.py": [
-        "f6c4c602a38c043637cf9dbc8bbb5350ec94527e",
+        "a3384436a1d643c6576ebb731db5c458f8ad2687",
         []
        ],
        "firefox.py": [
-        "e95bc93deca0ef0f825e9d7997450f16a7353819",
+        "1fa0bceb17bd436d5463ebc5938e41d511009e38",
         []
        ],
        "firefox_android.py": [
-        "b219079de3df793ba5c74de8706d4c9ad0526194",
+        "ca6ea96013b67ed548f5487520253ca61e0ae32c",
         []
        ],
        "ie.py": [
-        "78c22b909acf410038ff06769366662ab0c589a0",
+        "3a86c1b6ccb5aabfe210158f6be613f690543375",
         []
        ],
        "opera.py": [
-        "805fedee636cd22155ed143cf1148f3c2f5d98e7",
+        "a34f41955383c08e98c4d38d7804231c17e9297a",
         []
        ],
        "safari.py": [
-        "cb2b175d4dfbc3166e4ad5a6afe174ffdbc6ff8a",
+        "312d4db9c6382f7aff5cd75772f0edf73e538565",
         []
        ],
        "sauce.py": [
-        "c57ac942ed4e7624a0a7d115df3a210aec5d9ee1",
+        "3497c5c277288e51f6677000e57057ebcd99a210",
         []
        ],
        "servo.py": [
-        "b9476540ac7c53162e70669c4b7ba4883fd75f75",
+        "a65ed5ead4e55c45cfd7aa7921f0717216ce6a45",
         []
        ],
        "servodriver.py": [
-        "43794e41706d6ccc1f686655c8edeca95e3c4c62",
+        "ed85cbf3b9bd04ea192641b480ace44dc6d433bf",
         []
        ],
        "webkit.py": [
-        "da93cf1643995d95b8d6de1f70c7731b5559768b",
+        "590e472aca961b865bf0dbc836c942f62b506ae8",
         []
        ],
        "webkitgtk_minibrowser.py": [
-        "d8b9744bd743e39ce19f008e7f40cbc80e80107c",
+        "5b7a360fb608352e26971570af06c5bb8466255b",
         []
        ]
       },
@@ -255792,7 +255799,7 @@
         []
        ],
        "base.py": [
-        "55518685494e2464354250ae6e72babdef46dcf7",
+        "51d752cdebc61976066a15c5dd9e778f9378ff00",
         []
        ],
        "executorchrome.py": [
@@ -255857,7 +255864,7 @@
          []
         ],
         "runner.py": [
-         "3d32917659b3900f9e464670d2cdc39b8ff180a0",
+         "113eff99fee8d4a126d9c741b36cbfb8e8a1eebd",
          []
         ]
        },
@@ -256099,7 +256106,7 @@
        }
       },
       "wptrunner.py": [
-       "e1d9ef8b1a91dd2d44458c8c9d027537991f4cc9",
+       "b02805ff7e3df6645722e63fbc2d9715959c1ce6",
        []
       ],
       "wpttest.py": [
diff --git a/third_party/blink/web_tests/external/wpt/mathml/crashtests/mmultiscripts-with-two-prescripts.html b/third_party/blink/web_tests/external/wpt/mathml/crashtests/mmultiscripts-with-two-prescripts.html
new file mode 100644
index 0000000..54f9384
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/crashtests/mmultiscripts-with-two-prescripts.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<math>
+  <mmultiscripts>
+    <mrow></mrow>
+    <mprescripts></mprescripts>
+    <mprescripts></mprescripts>
+  </mmultiscripts>
+</math>
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py
index 6110450..ca93004b 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py
@@ -28,14 +28,14 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     # Use update() to modify the global list in place.
     _wptserve_ports.update(set(
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
index d0773873..27a09f5 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
@@ -28,14 +28,14 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     # Use update() to modify the global list in place.
     _wptserve_ports.update(set(
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
index fd251a431..d3e27a9 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -28,13 +28,13 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data,
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py
index 485b96f..607b7835 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py
@@ -29,14 +29,14 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"package_name": kwargs["package_name"],
             "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     # Use update() to modify the global list in place.
     _wptserve_ports.update(set(
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py
index 6db0188e..ecdbc3a 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py
@@ -22,12 +22,12 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config, cache_manager, run_info_data,
                                            **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py
index bdb37b3f..b908684 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py
@@ -34,7 +34,7 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args"),
             "timeout_multiplier": get_timeout_multiplier(test_type,
@@ -42,7 +42,7 @@
                                                          **kwargs)}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py
index 9e1ef38..1d45983 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py
@@ -24,13 +24,13 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data,
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py
index f6c4c602..a338443 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py
@@ -25,7 +25,7 @@
     pass
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     # Workaround for https://gitlab.gnome.org/GNOME/libsoup/issues/172
     webdriver_required_args = ["--host=127.0.0.1"]
     webdriver_args = maybe_add_args(webdriver_required_args, kwargs.get("webdriver_args"))
@@ -49,7 +49,7 @@
             "certificates": certificate_domain_list(server_config.domains_set, kwargs["host_cert_path"])}}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
index e95bc93..1fa0bce 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -76,7 +76,7 @@
     require_arg(kwargs, "binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "prefs_root": kwargs["prefs_root"],
             "extra_prefs": kwargs["extra_prefs"],
@@ -105,7 +105,7 @@
             "specialpowers_path": kwargs["specialpowers_path"]}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data,
@@ -138,6 +138,16 @@
         for pref, value in kwargs["extra_prefs"]:
             options["prefs"].update({pref: Preferences.cast(value)})
         capabilities["moz:firefoxOptions"] = options
+
+        environ = get_environ(logger,
+                              kwargs["binary"],
+                              kwargs["debug_info"],
+                              kwargs["stylo_threads"],
+                              kwargs["headless"],
+                              kwargs["enable_webrender"],
+                              kwargs["chaos_mode_flags"])
+
+        executor_kwargs["environ"] = environ
     if kwargs["certutil_binary"] is None:
         capabilities["acceptInsecureCerts"] = True
     if capabilities:
@@ -213,6 +223,26 @@
             {"os": ["version"], "processor": ["bits"]})
 
 
+def get_environ(logger, binary, debug_info, stylo_threads, headless, enable_webrender,
+                chaos_mode_flags=None):
+    env = test_environment(xrePath=os.path.abspath(os.path.dirname(binary)),
+                           debugger=debug_info is not None,
+                           useLSan=True,
+                           log=logger)
+
+    env["STYLO_THREADS"] = str(stylo_threads)
+    if chaos_mode_flags is not None:
+        env["MOZ_CHAOSMODE"] = str(chaos_mode_flags)
+    if headless:
+        env["MOZ_HEADLESS"] = "1"
+    if enable_webrender:
+        env["MOZ_WEBRENDER"] = "1"
+        env["MOZ_ACCELERATED"] = "1"
+    else:
+        env["MOZ_WEBRENDER"] = "0"
+    return env
+
+
 class FirefoxInstanceManager(object):
     __metaclass__ = ABCMeta
 
@@ -268,20 +298,8 @@
         marionette_port = get_free_port()
         profile.set_preferences({"marionette.port": marionette_port})
 
-        env = test_environment(xrePath=os.path.abspath(os.path.dirname(self.binary)),
-                               debugger=self.debug_info is not None,
-                               useLSan=True, log=self.logger)
-
-        env["STYLO_THREADS"] = str(self.stylo_threads)
-        if self.chaos_mode_flags is not None:
-            env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
-        if self.headless:
-            env["MOZ_HEADLESS"] = "1"
-        if self.enable_webrender:
-            env["MOZ_WEBRENDER"] = "1"
-            env["MOZ_ACCELERATED"] = "1"
-        else:
-            env["MOZ_WEBRENDER"] = "0"
+        env = get_environ(self.logger, self.binary, self.debug_info, self.stylo_threads,
+                          self.headless, self.enable_webrender, self.chaos_mode_flags)
 
         args = self.binary_args[:] if self.binary_args else []
         args += [cmd_arg("marionette"), "about:blank"]
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
index b219079d..ca6ea96 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
@@ -38,7 +38,7 @@
     pass
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"package_name": kwargs["package_name"],
             "device_serial": kwargs["device_serial"],
             "prefs_root": kwargs["prefs_root"],
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/ie.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/ie.py
index 78c22b9..3a86c1b6 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/ie.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/ie.py
@@ -18,11 +18,11 @@
 def check_args(**kwargs):
     require_arg(kwargs, "webdriver_binary")
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     options = {}
     options["requireWindowFocus"] = True
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/opera.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/opera.py
index 805fede..a34f419 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/opera.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/opera.py
@@ -24,13 +24,13 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     from selenium.webdriver import DesiredCapabilities
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/safari.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/safari.py
index cb2b175..312d4db9 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/safari.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/safari.py
@@ -26,12 +26,12 @@
     require_arg(kwargs, "webdriver_binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/sauce.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/sauce.py
index c57ac94..3497c5c 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/sauce.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/sauce.py
@@ -95,13 +95,13 @@
     require_arg(kwargs, "sauce_key")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     sauce_config = get_sauce_config(**kwargs)
 
     return {"sauce_config": sauce_config}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servo.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servo.py
index b947654..a65ed5ea 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servo.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servo.py
@@ -33,7 +33,7 @@
     require_arg(kwargs, "binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {
         "binary": kwargs["binary"],
         "debug_info": kwargs["debug_info"],
@@ -43,7 +43,7 @@
     }
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     rv = base_executor_kwargs(test_type, server_config,
                               cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py
index 43794e4..ed85cbf 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py
@@ -36,7 +36,7 @@
     require_arg(kwargs, "binary")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {
         "binary": kwargs["binary"],
         "binary_args": kwargs["binary_args"],
@@ -47,7 +47,7 @@
     }
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, **kwargs):
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data, **kwargs):
     rv = base_executor_kwargs(test_type, server_config,
                               cache_manager, run_info_data, **kwargs)
     return rv
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkit.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkit.py
index da93cf1..590e472 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkit.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkit.py
@@ -29,7 +29,7 @@
     require_arg(kwargs, "webkit_port")
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
@@ -54,7 +54,7 @@
     return {}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py
index d8b9744..5b7a360f 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py
@@ -25,7 +25,7 @@
     pass
 
 
-def browser_kwargs(test_type, run_info_data, config, **kwargs):
+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     # Workaround for https://gitlab.gnome.org/GNOME/libsoup/issues/172
     webdriver_required_args = ["--host=127.0.0.1"]
     webdriver_args = maybe_add_args(webdriver_required_args, kwargs.get("webdriver_args"))
@@ -53,7 +53,7 @@
             "certificates": certificate_domain_list(server_config.domains_set, kwargs["host_cert_path"])}}
 
 
-def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, run_info_data, **kwargs)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py
index 5551868..51d752cd 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/base.py
@@ -598,7 +598,7 @@
 
     def __init__(self, logger, browser, server_config, webdriver_binary,
                  webdriver_args, timeout_multiplier=1, capabilities=None,
-                 debug_info=None, **kwargs):
+                 debug_info=None, environ=None, **kwargs):
         self.do_delayed_imports()
         TestExecutor.__init__(self, logger, browser, server_config,
                               timeout_multiplier=timeout_multiplier,
@@ -607,6 +607,7 @@
         self.webdriver_args = webdriver_args
         self.timeout_multiplier = timeout_multiplier
         self.capabilities = capabilities
+        self.environ = environ if environ is not None else {}
         self.protocol = self.protocol_cls(self, browser)
 
     def is_alive(self):
@@ -632,7 +633,8 @@
         return pytestrunner.run(path,
                                 self.server_config,
                                 session_config,
-                                timeout=timeout)
+                                timeout=timeout,
+                                environ=self.environ)
 
     def do_delayed_imports(self):
         global pytestrunner
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
index 3d32917..113eff99 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
@@ -24,7 +24,7 @@
     import pytest
 
 
-def run(path, server_config, session_config, timeout=0):
+def run(path, server_config, session_config, timeout=0, environ=None):
     """
     Run Python test at ``path`` in pytest.  The provided ``session``
     is exposed as a fixture available in the scope of the test functions.
@@ -41,28 +41,35 @@
     if pytest is None:
         do_delayed_imports()
 
-    os.environ["WD_HOST"] = session_config["host"]
-    os.environ["WD_PORT"] = str(session_config["port"])
-    os.environ["WD_CAPABILITIES"] = json.dumps(session_config["capabilities"])
-    os.environ["WD_SERVER_CONFIG"] = json.dumps(server_config.as_dict_for_wd_env_variable())
+    old_environ = os.environ.copy()
+    try:
+        os.environ["WD_HOST"] = session_config["host"]
+        os.environ["WD_PORT"] = str(session_config["port"])
+        os.environ["WD_CAPABILITIES"] = json.dumps(session_config["capabilities"])
+        os.environ["WD_SERVER_CONFIG"] = json.dumps(server_config.as_dict_for_wd_env_variable())
+        if environ:
+            os.environ.update(environ)
 
-    harness = HarnessResultRecorder()
-    subtests = SubtestResultRecorder()
+        harness = HarnessResultRecorder()
+        subtests = SubtestResultRecorder()
 
-    with TemporaryDirectory() as cache:
-        try:
-            pytest.main(["--strict",  # turn warnings into errors
-                         "-vv",  # show each individual subtest and full failure logs
-                         "--capture", "no",  # enable stdout/stderr from tests
-                         "--basetemp", cache,  # temporary directory
-                         "--showlocals",  # display contents of variables in local scope
-                         "-p", "no:mozlog",  # use the WPT result recorder
-                         "-p", "no:cacheprovider",  # disable state preservation across invocations
-                         "-o=console_output_style=classic",  # disable test progress bar
-                         path],
-                        plugins=[harness, subtests])
-        except Exception as e:
-            harness.outcome = ("INTERNAL-ERROR", str(e))
+        with TemporaryDirectory() as cache:
+            try:
+                pytest.main(["--strict",  # turn warnings into errors
+                             "-vv",  # show each individual subtest and full failure logs
+                             "--capture", "no",  # enable stdout/stderr from tests
+                             "--basetemp", cache,  # temporary directory
+                             "--showlocals",  # display contents of variables in local scope
+                             "-p", "no:mozlog",  # use the WPT result recorder
+                             "-p", "no:cacheprovider",  # disable state preservation across invocations
+                             "-o=console_output_style=classic",  # disable test progress bar
+                             path],
+                            plugins=[harness, subtests])
+            except Exception as e:
+                harness.outcome = ("INTERNAL-ERROR", str(e))
+
+    finally:
+        os.environ = old_environ
 
     return (harness.outcome, subtests.results)
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptrunner.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptrunner.py
index e1d9ef8..b02805f 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptrunner.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptrunner.py
@@ -275,13 +275,15 @@
                     else:
                         browser_cls = product.browser_cls
 
-                    browser_kwargs = product.get_browser_kwargs(test_type,
+                    browser_kwargs = product.get_browser_kwargs(logger,
+                                                                test_type,
                                                                 run_info,
                                                                 config=test_environment.config,
                                                                 **kwargs)
 
                     executor_cls = product.executor_classes.get(test_type)
-                    executor_kwargs = product.get_executor_kwargs(test_type,
+                    executor_kwargs = product.get_executor_kwargs(logger,
+                                                                  test_type,
                                                                   test_environment.config,
                                                                   test_environment.cache_manager,
                                                                   run_info,
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/element-role-mapping-normal-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/element-role-mapping-normal-expected.txt
index e524bc1..17e176d 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/element-role-mapping-normal-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/accessibility/element-role-mapping-normal-expected.txt
@@ -61,11 +61,9 @@
         AXRole: AXStaticText "Paragraph"
             AXRole: AXInlineTextBox "Paragraph"
     AXRole: AXRuby
-        AXRole: AXRubyAnnotation
-            AXRole: AXStaticText "한국"
-                AXRole: AXInlineTextBox "한국"
         AXRole: AXStaticText "韓國"
             AXRole: AXInlineTextBox "韓國"
+        AXRole: AXRubyAnnotation "한국"
     AXRole: AXDescriptionList
         AXRole: AXDescriptionListTerm
             AXRole: AXStaticText "Coffee"
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-009-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-009-expected.txt
index 1b32cf43..10a17be7 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-009-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-009-expected.txt
@@ -12,7 +12,7 @@
   <div data-offset-x="155" data-offset-y="0" data-expected-width="40" data-expected-height="80" class="firstRowFirstColumn verticalLR">X XX X</div>
   <div data-offset-x="80" data-offset-y="60" data-expected-width="60" data-expected-height="90" class="firstRowSecondColumn verticalRL">XX X<br>X XXX X<br>XX XXX</div>
   <div data-offset-x="160" data-offset-y="160" data-expected-width="90" data-expected-height="30" class="secondRowFirstColumn LTR">X XX X</div>
-  <div data-offset-x="35" data-offset-y="160" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalRL RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="5" data-offset-y="160" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalRL RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
 offsetLeft expected 80 but got 110
 PASS .grid 3
@@ -21,7 +21,7 @@
   <div data-offset-x="105" data-offset-y="70" data-expected-width="90" data-expected-height="30" class="firstRowFirstColumn horizontal">X XX X</div>
   <div data-offset-x="140" data-offset-y="150" data-expected-width="100" data-expected-height="50" class="firstRowSecondColumn horizontal">XX X<br>X XXX X<br>XX XXX</div>
   <div data-offset-x="5" data-offset-y="10" data-expected-width="90" data-expected-height="30" class="secondRowFirstColumn horizontal RTL">X XX X</div>
-  <div data-offset-x="5" data-offset-y="115" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalLR">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="5" data-offset-y="110" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalLR">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
 offsetLeft expected 105 but got 75
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-010-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-010-expected.txt
index 1b32cf43..10a17be7 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-010-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-010-expected.txt
@@ -12,7 +12,7 @@
   <div data-offset-x="155" data-offset-y="0" data-expected-width="40" data-expected-height="80" class="firstRowFirstColumn verticalLR">X XX X</div>
   <div data-offset-x="80" data-offset-y="60" data-expected-width="60" data-expected-height="90" class="firstRowSecondColumn verticalRL">XX X<br>X XXX X<br>XX XXX</div>
   <div data-offset-x="160" data-offset-y="160" data-expected-width="90" data-expected-height="30" class="secondRowFirstColumn LTR">X XX X</div>
-  <div data-offset-x="35" data-offset-y="160" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalRL RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="5" data-offset-y="160" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalRL RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
 offsetLeft expected 80 but got 110
 PASS .grid 3
@@ -21,7 +21,7 @@
   <div data-offset-x="105" data-offset-y="70" data-expected-width="90" data-expected-height="30" class="firstRowFirstColumn horizontal">X XX X</div>
   <div data-offset-x="140" data-offset-y="150" data-expected-width="100" data-expected-height="50" class="firstRowSecondColumn horizontal">XX X<br>X XXX X<br>XX XXX</div>
   <div data-offset-x="5" data-offset-y="10" data-expected-width="90" data-expected-height="30" class="secondRowFirstColumn horizontal RTL">X XX X</div>
-  <div data-offset-x="5" data-offset-y="115" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalLR">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="5" data-offset-y="110" data-expected-width="70" data-expected-height="80" class="secondRowSecondColumn verticalLR">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
 offsetLeft expected 105 but got 75
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-011-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-011-expected.txt
index dcdaf1f..34a027a 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-011-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/external/wpt/css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-011-expected.txt
@@ -2,34 +2,27 @@
 FAIL .grid 1 assert_equals: 
 <div class="grid">
   <div data-offset-x="13" data-offset-y="2" data-expected-width="26" data-expected-height="70" class="firstRowFirstColumn verticalLR">X XX X</div>
-  <div data-offset-x="180" data-offset-y="60" data-expected-width="46" data-expected-height="80" class="firstRowSecondColumn verticalRL">XX X<br>X XXX X<br>XX XXX</div>
-  <div data-offset-x="0" data-offset-y="160" data-expected-width="76" data-expected-height="20" class="secondRowFirstColumn RTL">X XX X</div>
-  <div data-offset-x="105" data-offset-y="160" data-expected-width="56" data-expected-height="70" class="secondRowSecondColumn verticalLR RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="190" data-offset-y="64" data-expected-width="46" data-expected-height="80" class="firstRowSecondColumn verticalRL">XX X<br>X XXX X<br>XX XXX</div>
+  <div data-offset-x="8" data-offset-y="162" data-expected-width="76" data-expected-height="20" class="secondRowFirstColumn RTL">X XX X</div>
+  <div data-offset-x="113" data-offset-y="162" data-expected-width="56" data-expected-height="70" class="secondRowSecondColumn verticalLR RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
-offsetLeft expected 180 but got 210
+offsetLeft expected 190 but got 210
 FAIL .grid 2 assert_equals: 
 <div class="grid RTL">
-  <div data-offset-x="155" data-offset-y="0" data-expected-width="26" data-expected-height="70" class="firstRowFirstColumn verticalLR">X XX X</div>
-  <div data-offset-x="80" data-offset-y="60" data-expected-width="46" data-expected-height="80" class="firstRowSecondColumn verticalRL">XX X<br>X XXX X<br>XX XXX</div>
-  <div data-offset-x="160" data-offset-y="160" data-expected-width="76" data-expected-height="20" class="secondRowFirstColumn LTR">X XX X</div>
-  <div data-offset-x="35" data-offset-y="160" data-expected-width="56" data-expected-height="70" class="secondRowSecondColumn verticalRL RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="163" data-offset-y="2" data-expected-width="26" data-expected-height="70" class="firstRowFirstColumn verticalLR">X XX X</div>
+  <div data-offset-x="90" data-offset-y="64" data-expected-width="46" data-expected-height="80" class="firstRowSecondColumn verticalRL">XX X<br>X XXX X<br>XX XXX</div>
+  <div data-offset-x="170" data-offset-y="162" data-expected-width="76" data-expected-height="20" class="secondRowFirstColumn LTR">X XX X</div>
+  <div data-offset-x="13" data-offset-y="162" data-expected-width="56" data-expected-height="70" class="secondRowSecondColumn verticalRL RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
-offsetLeft expected 155 but got 163
-FAIL .grid 3 assert_equals: 
-<div class="grid verticalLR">
-  <div data-offset-x="5" data-offset-y="70" data-expected-width="76" data-expected-height="20" class="firstRowFirstColumn horizontal">X XX X</div>
-  <div data-offset-x="40" data-offset-y="150" data-expected-width="86" data-expected-height="40" class="firstRowSecondColumn horizontal">XX X<br>X XXX X<br>XX XXX</div>
-  <div data-offset-x="180" data-offset-y="10" data-expected-width="26" data-expected-height="20" class="secondRowFirstColumn verticalRL">X XX X</div>
-  <div data-offset-x="155" data-offset-y="110" data-expected-width="76" data-expected-height="50" class="secondRowSecondColumn horizontal RTL">XX X<br>X XXX<br>X<br>XX XXX</div>
-</div>
-offsetLeft expected 5 but got 13
+offsetLeft expected 90 but got 110
+PASS .grid 3
 FAIL .grid 4 assert_equals: 
 <div class="grid verticalRL">
-  <div data-offset-x="105" data-offset-y="70" data-expected-width="76" data-expected-height="20" class="firstRowFirstColumn horizontal">X XX X</div>
-  <div data-offset-x="140" data-offset-y="150" data-expected-width="86" data-expected-height="40" class="firstRowSecondColumn horizontal">XX X<br>X XXX X<br>XX XXX</div>
-  <div data-offset-x="5" data-offset-y="10" data-expected-width="76" data-expected-height="20" class="secondRowFirstColumn horizontal RTL">X XX X</div>
-  <div data-offset-x="5" data-offset-y="115" data-expected-width="56" data-expected-height="70" class="secondRowSecondColumn verticalLR">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="113" data-offset-y="74" data-expected-width="76" data-expected-height="20" class="firstRowFirstColumn horizontal">X XX X</div>
+  <div data-offset-x="150" data-offset-y="153" data-expected-width="86" data-expected-height="40" class="firstRowSecondColumn horizontal">XX X<br>X XXX X<br>XX XXX</div>
+  <div data-offset-x="14" data-offset-y="12" data-expected-width="76" data-expected-height="20" class="secondRowFirstColumn horizontal RTL">X XX X</div>
+  <div data-offset-x="13" data-offset-y="112" data-expected-width="56" data-expected-height="70" class="secondRowSecondColumn verticalLR">XX X<br>X XXX<br>X<br>XX XXX</div>
 </div>
-offsetLeft expected 105 but got 101
+offsetLeft expected 113 but got 101
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-alignment-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-alignment-expected.txt
new file mode 100644
index 0000000..de34f2a8
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-alignment-expected.txt
@@ -0,0 +1,783 @@
+This test verifies the flex self-alignment value sent by the backend.
+
+flex-start{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        0,
+        "L",
+        800,
+        0,
+        "L",
+        800,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "flex-start",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        0,
+        "L",
+        100,
+        0,
+        "L",
+        100,
+        100,
+        "L",
+        0,
+        100,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": false,
+      "alignItemsStyle": "flex-start",
+      "mainGap": 0,
+      "crossGap": 0,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+flex-end{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        100,
+        "L",
+        800,
+        100,
+        "L",
+        800,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "flex-end",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        100,
+        "L",
+        100,
+        100,
+        "L",
+        100,
+        200,
+        "L",
+        0,
+        200,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": false,
+      "alignItemsStyle": "flex-end",
+      "mainGap": 0,
+      "crossGap": 0,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+center{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        200,
+        "L",
+        800,
+        200,
+        "L",
+        800,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "center",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        200,
+        "L",
+        100,
+        200,
+        "L",
+        100,
+        300,
+        "L",
+        0,
+        300,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": false,
+      "alignItemsStyle": "center",
+      "mainGap": 0,
+      "crossGap": 0,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+stretch{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        300,
+        "L",
+        800,
+        300,
+        "L",
+        800,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "stretch",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        300,
+        "L",
+        100,
+        300,
+        "L",
+        100,
+        400,
+        "L",
+        0,
+        400,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": false,
+      "alignItemsStyle": "stretch",
+      "mainGap": 0,
+      "crossGap": 0,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+normal{
+  "paths": [
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        0,
+        400,
+        "L",
+        800,
+        400,
+        "L",
+        800,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "normal",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        0,
+        400,
+        "L",
+        100,
+        400,
+        "L",
+        100,
+        500,
+        "L",
+        0,
+        500,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": false,
+      "alignItemsStyle": "normal",
+      "mainGap": 0,
+      "crossGap": 0,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-gap-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-gap-expected.txt
new file mode 100644
index 0000000..93afb58b
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-gap-expected.txt
@@ -0,0 +1,627 @@
+This test verifies the gap information sent to the overlay frontend for flex contains with gaps.
+
+test-1{
+  "paths": [
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "test-1",
+    "className": ".container",
+    "nodeWidth": "400",
+    "nodeHeight": "400",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": false,
+      "alignItemsStyle": "normal",
+      "mainGap": 10,
+      "crossGap": 20,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+test-2{
+  "paths": [
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "test-2",
+    "className": ".container",
+    "nodeWidth": "400",
+    "nodeHeight": "400",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": true,
+      "alignItemsStyle": "normal",
+      "mainGap": 10,
+      "crossGap": 20,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+test-3{
+  "paths": [
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "test-3",
+    "className": ".container",
+    "nodeWidth": "400",
+    "nodeHeight": "400",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": false,
+      "isReverse": false,
+      "alignItemsStyle": "normal",
+      "mainGap": 20,
+      "crossGap": 10,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+test-4{
+  "paths": [
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "test-4",
+    "className": ".container",
+    "nodeWidth": "400",
+    "nodeHeight": "400",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        100,
+        100,
+        "L",
+        500,
+        100,
+        "L",
+        500,
+        500,
+        "L",
+        100,
+        500,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": false,
+      "isReverse": true,
+      "alignItemsStyle": "normal",
+      "mainGap": 20,
+      "crossGap": 10,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-reverse-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-reverse-expected.txt
new file mode 100644
index 0000000..232c024
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-css-flex-reverse-expected.txt
@@ -0,0 +1,315 @@
+This test verifies that the flex overlay creates the right lines and items for reverse direction flex containers. See crbug.com/1153272.
+
+test-1{
+  "paths": [
+    {
+      "path": [
+        "M",
+        8,
+        8,
+        "L",
+        108,
+        8,
+        "L",
+        108,
+        108,
+        "L",
+        8,
+        108,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        8,
+        "L",
+        108,
+        8,
+        "L",
+        108,
+        108,
+        "L",
+        8,
+        108,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        8,
+        "L",
+        108,
+        8,
+        "L",
+        108,
+        108,
+        "L",
+        8,
+        108,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        8,
+        "L",
+        792,
+        8,
+        "L",
+        792,
+        108,
+        "L",
+        8,
+        108,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "test-1",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        8,
+        8,
+        "L",
+        108,
+        8,
+        "L",
+        108,
+        108,
+        "L",
+        8,
+        108,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": true,
+      "isReverse": true,
+      "alignItemsStyle": "flex-start",
+      "mainGap": 10,
+      "crossGap": 10,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+test-2{
+  "paths": [
+    {
+      "path": [
+        "M",
+        8,
+        108,
+        "L",
+        108,
+        108,
+        "L",
+        108,
+        208,
+        "L",
+        8,
+        208,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 0, 0, 0)",
+      "outlineColor": "rgba(128, 0, 0, 0)",
+      "name": "content"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        108,
+        "L",
+        108,
+        108,
+        "L",
+        108,
+        208,
+        "L",
+        8,
+        208,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 255, 0, 0)",
+      "name": "padding"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        108,
+        "L",
+        108,
+        108,
+        "L",
+        108,
+        208,
+        "L",
+        8,
+        208,
+        "Z"
+      ],
+      "fillColor": "rgba(0, 0, 255, 0)",
+      "name": "border"
+    },
+    {
+      "path": [
+        "M",
+        8,
+        108,
+        "L",
+        792,
+        108,
+        "L",
+        792,
+        208,
+        "L",
+        8,
+        208,
+        "Z"
+      ],
+      "fillColor": "rgba(255, 255, 255, 0)",
+      "name": "margin"
+    }
+  ],
+  "showRulers": true,
+  "showExtensionLines": true,
+  "showAccessibilityInfo": true,
+  "colorFormat": "hex",
+  "elementInfo": {
+    "tagName": "div",
+    "idValue": "test-2",
+    "className": ".container",
+    "nodeWidth": "100",
+    "nodeHeight": "100",
+    "isKeyboardFocusable": false,
+    "accessibleName": "",
+    "accessibleRole": "generic",
+    "layoutObjectName": "LayoutFlexibleBox",
+    "showAccessibilityInfo": true
+  },
+  "flexInfo": [
+    {
+      "containerBorder": [
+        "M",
+        8,
+        108,
+        "L",
+        108,
+        108,
+        "L",
+        108,
+        208,
+        "L",
+        8,
+        208,
+        "Z"
+      ],
+      "lines": [],
+      "isHorizontalFlow": false,
+      "isReverse": true,
+      "alignItemsStyle": "flex-start",
+      "mainGap": 10,
+      "crossGap": 10,
+      "flexContainerHighlightConfig": {
+        "containerBorder": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "lineSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "itemSeparator": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        },
+        "mainDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossDistributedSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "rowGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "columnGapSpace": {
+          "fillColor": "rgba(255, 0, 0, 0)",
+          "hatchColor": "rgba(255, 0, 0, 0)"
+        },
+        "crossAlignment": {
+          "color": "rgba(255, 0, 0, 0)",
+          "pattern": "solid"
+        }
+      }
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-display-locked-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-display-locked-expected.txt
index 7313b14..d7c3b61 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-display-locked-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/http/tests/devtools/elements/highlight/highlight-display-locked-expected.txt
@@ -93,6 +93,7 @@
     "accessibleName": "",
     "accessibleRole": "generic",
     "layoutObjectName": "LayoutBlockFlow",
+    "isLocked": true,
     "showAccessibilityInfo": true
   }
 }
@@ -189,7 +190,7 @@
     "accessibleName": "",
     "accessibleRole": "generic",
     "layoutObjectName": "LayoutBlockFlow",
-    "isLockedAncestor": "true",
+    "isLockedAncestor": true,
     "showAccessibilityInfo": true
   }
 }
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 743963e9..f1fcb0f 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -901,7 +901,7 @@
     method read
     method setLength
     method write
-interface NativeIOManager
+interface NativeIOFileManager
     attribute @@toStringTag
     method constructor
     method delete
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index a4a864d..95b5641 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -902,16 +902,7 @@
 [Worker]     method read
 [Worker]     method setLength
 [Worker]     method write
-[Worker] interface NativeIOFileSync
-[Worker]     attribute @@toStringTag
-[Worker]     method close
-[Worker]     method constructor
-[Worker]     method flush
-[Worker]     method getLength
-[Worker]     method read
-[Worker]     method setLength
-[Worker]     method write
-[Worker] interface NativeIOManager
+[Worker] interface NativeIOFileManager
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
 [Worker]     method delete
@@ -922,6 +913,15 @@
 [Worker]     method openSync
 [Worker]     method rename
 [Worker]     method renameSync
+[Worker] interface NativeIOFileSync
+[Worker]     attribute @@toStringTag
+[Worker]     method close
+[Worker]     method constructor
+[Worker]     method flush
+[Worker]     method getLength
+[Worker]     method read
+[Worker]     method setLength
+[Worker]     method write
 [Worker] interface NavigationPreloadManager
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index e2c052b38..9f13f56b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5417,7 +5417,7 @@
     method read
     method setLength
     method write
-interface NativeIOManager
+interface NativeIOFileManager
     attribute @@toStringTag
     method constructor
     method delete
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 8e58c16..7daabc8 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -827,7 +827,7 @@
 [Worker]     method read
 [Worker]     method setLength
 [Worker]     method write
-[Worker] interface NativeIOManager
+[Worker] interface NativeIOFileManager
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
 [Worker]     method delete
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index b5bc062d..7bb37ec 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-4-49-gf6be92767
-Revision: f6be92767d1d002f5e90a4673ee4e6d4f1e1744f
+Version: VER-2-10-4-50-g84b3616c9
+Revision: 84b3616c94f48726c596ead4150218d4431b3412
 CPEPrefix: cpe:/a:freetype:freetype:2.10.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8359490..ebd131f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -74707,6 +74707,7 @@
   <int value="60" label="kErrorCodeNotEnoughSpace"/>
   <int value="61" label="kErrorCodeDeviceCorrupted"/>
   <int value="62" label="kErrorCodePackageExcludedFromUpdate"/>
+  <int value="63" label="kErrorCodeDownloadCancelledPerPolicy"/>
 </enum>
 
 <enum name="UpdateEngineInstallDateProvisioningSource">
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 491274c..17ae8fc 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -14669,6 +14669,9 @@
 <histogram_suffixes name="ProcessMemoryAllocatorSmall2" separator=".">
   <suffix name="Discardable.FreelistSize"
       label="Freelist size used by ClientDiscardableMemoryManager."/>
+  <suffix name="Discardable.ResidentSize"
+      label="Amount of resident memory held by
+             ClientDiscardableSharedMemoryManager."/>
   <suffix name="Discardable.VirtualSize"
       label="Virtual memory used by ClientDiscardableMemoryManager."/>
   <suffix name="DownloadService"
diff --git a/tools/metrics/histograms/histograms_xml/memory/histograms.xml b/tools/metrics/histograms/histograms_xml/memory/histograms.xml
index fcc09261..2beabbd 100644
--- a/tools/metrics/histograms/histograms_xml/memory/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/memory/histograms.xml
@@ -388,6 +388,16 @@
   <summary>TBD.</summary>
 </histogram>
 
+<histogram name="Memory.Discardable.FreeListReleaseTime" units="ms"
+    expires_after="2021-08-01">
+  <owner>thiabaud@google.com</owner>
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Records how long it takes for memory to be released from the freelist of the
+    discardable shared memory allocator with |madvise|.
+  </summary>
+</histogram>
+
 <histogram name="Memory.Discardable.FreelistSize.Foreground" units="KiB"
     expires_after="2021-05-30">
   <owner>thiabaud@google.com</owner>
@@ -1995,6 +2005,22 @@
   <summary>Throughput of a ParkableString disk write.</summary>
 </histogram>
 
+<histogram name="Memory.PartitionAlloc.ThreadCache.BatchFillRate{ThreadType}"
+    units="%" expires_after="M92">
+  <owner>lizeb@chromium.org</owner>
+  <owner>bartekn@chromium.org</owner>
+  <summary>
+    Fraction of PartitionAlloc's thread cache allocations requests that required
+    a batch fill, that is cache hits touching the central allocator. Recorded
+    during memory dumps, at the same time as the Memory.*.PartitionAlloc.*
+    histograms. Data is collected for {ThreadType}.
+  </summary>
+  <token key="ThreadType">
+    <variant name="" summary="all threads"/>
+    <variant name=".MainThread" summary="the main thread only"/>
+  </token>
+</histogram>
+
 <histogram name="Memory.PartitionAlloc.ThreadCache.HitRate{ThreadType}"
     units="%" expires_after="M92">
   <owner>lizeb@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 26730dda8..322ecb3 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -123,7 +123,7 @@
 </histogram>
 
 <histogram name="AccountManager.ManageAccountsServiceType"
-    enum="GaiaServiceType" expires_after="2021-01-24">
+    enum="GaiaServiceType" expires_after="2022-01-24">
   <owner>sinhak@chromium.org</owner>
   <owner>anastasiian@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/signin/histograms.xml b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
index 4439565e..d05ec0aa 100644
--- a/tools/metrics/histograms/histograms_xml/signin/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
@@ -40,6 +40,27 @@
   </summary>
 </histogram>
 
+<histogram name="Signin.AccountConsistencyPromoAction.Shown.Count"
+    units="counts" expires_after="2021-08-31">
+  <owner>aliceywang@chromium.org</owner>
+  <owner>triploblastic@chromium.org</owner>
+  <summary>
+    Every time the account picker bottom sheet is shown as part of the web
+    sign-in flow this histogram records the number of times bottom sheet was
+    shown (Android only).
+  </summary>
+</histogram>
+
+<histogram name="Signin.AccountConsistencyPromoAction.SignedIn.Count"
+    units="counts" expires_after="2021-08-31">
+  <owner>aliceywang@chromium.org</owner>
+  <owner>triploblastic@chromium.org</owner>
+  <summary>
+    This histogram records the number of times the account picker bottom sheet
+    was shown before the user signed-in through the bottom sheet (Android only).
+  </summary>
+</histogram>
+
 <histogram name="Signin.AccountEquality" enum="SigninAccountEquality"
     expires_after="2021-03-06">
   <owner>droger@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/sync/histograms.xml b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
index 5f1e5ca0..d288a7c 100644
--- a/tools/metrics/histograms/histograms_xml/sync/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
@@ -302,7 +302,11 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.CustomSync2" enum="SyncModelTypes" expires_after="M90">
+<histogram name="Sync.CustomSync2" enum="SyncModelTypes"
+    expires_after="2020-12-16">
+  <obsolete>
+    Replaced with Sync.CustomSync3 in 12/2020 (M89) due to crbug.com/1154396.
+  </obsolete>
   <owner>treib@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -313,6 +317,22 @@
   </summary>
 </histogram>
 
+<histogram name="Sync.CustomSync3" enum="SyncModelTypes"
+    expires_after="2021-12-16">
+  <owner>treib@chromium.org</owner>
+  <owner>mastiz@chromium.org</owner>
+  <summary>
+    For users who have *not* selected the &quot;Sync Everything&quot; option,
+    this records all the data types they have selected to sync. Samples are
+    taken every time the Sync data types are (re)configured, which typically
+    happens during startup and when the user changes any Sync settings.
+
+    NOTE: this may stop showing OS datatypes like WIFI_CONFIGURATIONS once the
+    SplitSettingsSync feature is enabled. New metrics will be added for those
+    (crbug.com/1105956).
+  </summary>
+</histogram>
+
 <histogram name="Sync.DataTypeRunFailures2" enum="SyncModelTypes"
     expires_after="2021-06-13">
   <owner>jkrcal@chromium.org</owner>
@@ -818,7 +838,10 @@
 </histogram>
 
 <histogram name="Sync.Preferences.ClearedLocalPrefOnTypeMismatch"
-    units="BooleanHit" expires_after="M88">
+    units="BooleanHit" expires_after="2020-12-16">
+  <obsolete>
+    Deprecated in 12/2020.
+  </obsolete>
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -849,16 +872,31 @@
 </histogram>
 
 <histogram name="Sync.RedundantInvalidationPerModelType" enum="SyncModelTypes"
-    expires_after="2021-06-13">
+    expires_after="2020-12-16">
+  <obsolete>
+    Replaced with Sync.RedundantInvalidationPerModelType2 in M89 (12/2020).
+  </obsolete>
   <owner>melandory@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
-    WARNING: The recordings here are incorrect (crbug.com/1158476).
+    WARNING: The recordings here are incorrect (crbug.com/1158476), see
+    Sync.RedundantInvalidationPerModelType2 instead.
 
     The sync datatype of the received invalidation with not-fresh version.
   </summary>
 </histogram>
 
+<histogram name="Sync.RedundantInvalidationPerModelType2" enum="SyncModelTypes"
+    expires_after="2021-12-16">
+  <owner>melandory@chromium.org</owner>
+  <owner>treib@chromium.org</owner>
+  <summary>
+    Recorded whenever Sync receives an invalidation with a non-fresh version,
+    i.e. a smaller version number than Sync already knew about. The recorded
+    value is the data type of the invalidation.
+  </summary>
+</histogram>
+
 <histogram name="Sync.SessionTabs" units="tabs" expires_after="2021-01-31">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
@@ -904,12 +942,16 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.Startup.TimeDeferred2" units="ms" expires_after="M88">
+<histogram name="Sync.Startup.TimeDeferred2" units="ms"
+    expires_after="2021-12-16">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
-    Time spent after ProfileSyncService *creation* but before SyncEngine
-    initialization.
+    Recorded on browser startup if the SyncEngine initialization is deferred by
+    a fixed (configurable) delay due to performance reasons. In some cases, a
+    data type may force sync to start before the delay finishes, causing the
+    recorded time to be smaller. This histogram records the time spent after the
+    ProfileSyncService *creation* but before the SyncEngine initialization.
   </summary>
 </histogram>
 
@@ -1047,7 +1089,10 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.URLFetchTime" units="ms" expires_after="M88">
+<histogram name="Sync.URLFetchTime" units="ms" expires_after="2020-12-16">
+  <obsolete>
+    Deprecated in 12/2020.
+  </obsolete>
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
diff --git a/ui/base/clipboard/clipboard_non_backed.cc b/ui/base/clipboard/clipboard_non_backed.cc
index d8315cb..6cf921a 100644
--- a/ui/base/clipboard/clipboard_non_backed.cc
+++ b/ui/base/clipboard/clipboard_non_backed.cc
@@ -38,12 +38,13 @@
 
 namespace {
 
+using InstanceRegistry = std::set<const ClipboardNonBacked*, std::less<>>;
 // Returns the registry which tracks all instances of ClipboardNonBacked in
 // existence. This allows us to determine if any arbitrary Clipboard pointer in
 // fact points to a ClipboardNonBacked instance. Only if a pointer exists in
 // this registry is it safe to cast to ClipboardNonBacked*.
-std::set<const ClipboardNonBacked*>* GetInstanceRegistry() {
-  static base::NoDestructor<std::set<const ClipboardNonBacked*>> registry;
+InstanceRegistry* GetInstanceRegistry() {
+  static base::NoDestructor<InstanceRegistry> registry;
   return registry.get();
 }
 
diff --git a/ui/file_manager/BUILD.gn b/ui/file_manager/BUILD.gn
index 6652eda..048f10a 100644
--- a/ui/file_manager/BUILD.gn
+++ b/ui/file_manager/BUILD.gn
@@ -87,6 +87,7 @@
   in_files = [
     "audio_player/js/main.m.js",
     "audio_player/js/main_background.m.js",
+    "video_player/js/main_background.m.js",
   ]
 
   deps = [ "//ui/file_manager/audio_player/js:main.m" ]
@@ -122,6 +123,7 @@
     "file_manager/common/js/file_type.m.js",
     "file_manager/common/js/files_app_entry_types.m.js",
     "file_manager/common/js/lru_cache.m.js",
+    "file_manager/common/js/metrics_base.m.js",
 
     # Externs:
     "externs/entry_location.m.js",
@@ -177,6 +179,12 @@
     "file_manager/foreground/js/metadata/function_parallel.m.js",
     "file_manager/foreground/js/metadata/function_sequence.m.js",
     "file_manager/foreground/js/metadata/exif_constants.m.js",
+
+    # Video Player:
+    "video_player/js/error_util.m.js",
+    "video_player/js/test_util.m.js",
+    "video_player/js/background.m.js",
+    "video_player/js/video_player_metrics.m.js",
   ]
 
   deps = [
@@ -191,6 +199,7 @@
     "//ui/file_manager/file_manager/foreground/js:modulize",
     "//ui/file_manager/file_manager/foreground/js/metadata:modulize",
     "//ui/file_manager/image_loader:modulize",
+    "//ui/file_manager/video_player/js:modulize",
   ]
 }
 
@@ -209,12 +218,15 @@
     "file_manager/foreground/elements/files_icon_button.m.js",
     "file_manager/foreground/elements/files_toggle_ripple.m.js",
     "file_manager/foreground/elements/files_ripple.m.js",
+
+    "video_player/js/main_background.m.rollup.js",
   ]
 
   resource_path_rewrites = [
     "audio_player/js/main.m.rollup.js|audio_player/js/main.m.js",
     "audio_player/js/main_background.m.rollup.js|audio_player/js/main_background.m.js",
     "audio_player/js/metadata_worker.m.rollup.js|audio_player/js/metadata_worker.m.js",
+    "video_player/js/main_background.m.rollup.js|video_player/js/main_background.m.js",
   ]
 
   grdp_files = [
@@ -229,6 +241,7 @@
     "//ui/file_manager/audio_player/js:build_background",
     "//ui/file_manager/audio_player/js:build_worker",
     "//ui/file_manager/file_manager/foreground/elements:elements",
+    "//ui/file_manager/video_player/js:build_background",
   ]
 }
 
diff --git a/ui/file_manager/externs/background/BUILD.gn b/ui/file_manager/externs/background/BUILD.gn
index 5ee3283..d0a5d3e 100644
--- a/ui/file_manager/externs/background/BUILD.gn
+++ b/ui/file_manager/externs/background/BUILD.gn
@@ -16,7 +16,6 @@
     "file_browser_background_full.js",
     "file_operation_manager.js",
     "import_history.js",
-    "import_runner.js",
     "media_import_handler.js",
     "media_scanner.js",
     "progress_center.js",
@@ -43,6 +42,18 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("duplicate_finder.m") {
+  sources = [
+    "$root_gen_dir/ui/file_manager/externs/background/duplicate_finder.m.js",
+  ]
+  deps = [
+    ":import_history.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("crostini.m") {
   sources = [ "$root_gen_dir/ui/file_manager/externs/background/crostini.m.js" ]
   deps = [ "..:volume_manager.m" ]
@@ -68,6 +79,29 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("media_import_handler.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/externs/background/media_import_handler.m.js" ]
+  deps = [
+    ":drive_sync_handler.m",
+    ":duplicate_finder.m",
+    ":import_history.m",
+    ":media_scanner.m",
+    ":progress_center.m",
+    ":task_queue.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_library("media_scanner.m") {
+  sources =
+      [ "$root_gen_dir/ui/file_manager/externs/background/media_scanner.m.js" ]
+  deps = [ "//ui/file_manager/file_manager/common/js:importer_common.m" ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("progress_center.m") {
   sources = [
     "$root_gen_dir/ui/file_manager/externs/background/progress_center.m.js",
@@ -93,8 +127,11 @@
     "background_base.js",
     "crostini.js",
     "drive_sync_handler.js",
+    "duplicate_finder.js",
     "file_operation_manager.js",
     "import_history.js",
+    "media_import_handler.js",
+    "media_scanner.js",
     "progress_center.js",
     "task_queue.js",
   ]
diff --git a/ui/file_manager/externs/background/duplicate_finder.js b/ui/file_manager/externs/background/duplicate_finder.js
index 67d5b63..b0d475ba 100644
--- a/ui/file_manager/externs/background/duplicate_finder.js
+++ b/ui/file_manager/externs/background/duplicate_finder.js
@@ -2,21 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {importer} from '../../file_manager/common/js/importer_common.m.js';
+// #import {importerHistoryInterfaces} from './import_history.m.js';
+
 // Namespace
-// eslint-disable-next-line no-var
-var importer = importer || {};
+/* #export */ const duplicateFinderInterfaces = {};
 
 /**
  * Declare DispositionChecker class.
  * @interface
  */
-importer.DispositionChecker = class {
+duplicateFinderInterfaces.DispositionChecker = class {
   /**
    * Factory for a function that returns a file entry's content disposition.
    *
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
    *
-   * @return {!importer.DispositionChecker.CheckerFunction}
+   * @return {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
    */
   static createChecker(historyLoader) {}
 };
@@ -28,4 +30,4 @@
  * @typedef {function(!FileEntry, !importer.Destination, !importer.ScanMode):
  *     !Promise<!importer.Disposition>}
  */
-importer.DispositionChecker.CheckerFunction;
+duplicateFinderInterfaces.DispositionChecker.CheckerFunction;
diff --git a/ui/file_manager/externs/background/file_browser_background_full.js b/ui/file_manager/externs/background/file_browser_background_full.js
index aa47640..b5e6435 100644
--- a/ui/file_manager/externs/background/file_browser_background_full.js
+++ b/ui/file_manager/externs/background/file_browser_background_full.js
@@ -29,12 +29,12 @@
     this.fileOperationManager;
 
     /**
-     * @type {!importer.ImportRunner}
+     * @type {!mediaImportInterfaces.ImportRunner}
      */
     this.mediaImportHandler;
 
     /**
-     * @type {!importer.MediaScanner}
+     * @type {!mediaScannerInterfaces.MediaScanner}
      */
     this.mediaScanner;
 
diff --git a/ui/file_manager/externs/background/import_runner.js b/ui/file_manager/externs/background/import_runner.js
deleted file mode 100644
index e10514d7..0000000
--- a/ui/file_manager/externs/background/import_runner.js
+++ /dev/null
@@ -1,24 +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.
-
-// Namespace
-var importer = importer || {};
-
-/**
- * Interface providing access to information about active import processes.
- *
- * @interface
- */
-importer.ImportRunner = class {
-  /**
-   * Imports all media identified by a scanResult.
-   *
-   * @param {!importer.ScanResult} scanResult
-   * @param {!importer.Destination} destination
-   * @param {!Promise<!DirectoryEntry>} directoryPromise
-   *
-   * @return {!importer.MediaImportHandler.ImportTask} The media import task.
-   */
-  importFromScanResult(scanResult, destination, directoryPromise) {}
-};
diff --git a/ui/file_manager/externs/background/media_import_handler.js b/ui/file_manager/externs/background/media_import_handler.js
index ef82133..83a359bf 100644
--- a/ui/file_manager/externs/background/media_import_handler.js
+++ b/ui/file_manager/externs/background/media_import_handler.js
@@ -2,30 +2,60 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Namespace
-// eslint-disable-next-line no-var
-var importer = importer || {};
-
-/*
+/**
+ * @fileoverview
  * Externs definition for  MediaImportHandler to allow for Closure compilation
  * of its media import unittest and caller sites.
  */
 
+// #import {DriveSyncHandler} from './drive_sync_handler.m.js';
+// #import {importer} from '../../file_manager/common/js/importer_common.m.js';
+// #import {importerHistoryInterfaces} from './import_history.m.js';
+// #import {duplicateFinderInterfaces} from './duplicate_finder.m.js';
+// #import {mediaScannerInterfaces} from './media_scanner.m.js';
+// #import {taskQueueInterfaces} from './task_queue.m.js';
+// #import {ProgressCenter} from './progress_center.m.js';
+
+/* #export */ const mediaImportInterfaces = {};
+
+/**
+ * Interface providing access to information about active import processes.
+ *
+ * @interface
+ */
+mediaImportInterfaces.ImportRunner = class {
+  /**
+   * Imports all media identified by a scanResult.
+   *
+   * @param {!mediaScannerInterfaces.ScanResult} scanResult
+   * @param {!importer.Destination} destination
+   * @param {!Promise<!DirectoryEntry>} directoryPromise
+   *
+   * @return {!mediaImportInterfaces.MediaImportHandler.ImportTask} The media
+   *     import task.
+   */
+  importFromScanResult(scanResult, destination, directoryPromise) {}
+};
+
 /**
  * Define MediaImportHandler constructor: handler for importing media from
  * removable devices into the user's Drive.
  *
  * @interface
  */
-importer.MediaImportHandler = class extends importer.ImportRunner {
+mediaImportInterfaces.MediaImportHandler =
+    class extends mediaImportInterfaces.ImportRunner {
   /**
    * @param {!ProgressCenter} progressCenter
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
-   * @param {!importer.DispositionChecker.CheckerFunction} dispositionChecker
+   * @param {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
+   *     dispositionChecker
    * @param {!DriveSyncHandler} driveSyncHandler
    */
   constructor(
-      progressCenter, historyLoader, dispositionChecker, driveSyncHandler) {}
+      progressCenter, historyLoader, dispositionChecker, driveSyncHandler) {
+    super();
+  }
 };
 
 /**
@@ -38,14 +68,15 @@
  * @extends {taskQueueInterfaces.BaseTask}
  * @interface
  */
-importer.MediaImportHandler.ImportTask = class {
+mediaImportInterfaces.MediaImportHandler.ImportTask = class {
   /**
    * @param {string} taskId
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
-   * @param {!importer.ScanResult} scanResult
+   * @param {!mediaScannerInterfaces.ScanResult} scanResult
    * @param {!Promise<!DirectoryEntry>} directoryPromise
    * @param {!importer.Destination} destination The logical destination.
-   * @param {!importer.DispositionChecker.CheckerFunction} dispositionChecker
+   * @param {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
+   *     dispositionChecker
    */
   constructor(
       taskId, historyLoader, scanResult, directoryPromise, destination,
@@ -71,4 +102,4 @@
  *   destination: !Entry
  * }}
  */
-importer.MediaImportHandler.ImportTask.EntryChangedInfo;
+mediaImportInterfaces.MediaImportHandler.ImportTask.EntryChangedInfo;
diff --git a/ui/file_manager/externs/background/media_scanner.js b/ui/file_manager/externs/background/media_scanner.js
index b4be080..f40525b 100644
--- a/ui/file_manager/externs/background/media_scanner.js
+++ b/ui/file_manager/externs/background/media_scanner.js
@@ -2,21 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var importer = importer || {};
+// #import {importer} from '../../file_manager/common/js/importer_common.m.js';
 
+/* #export */ const mediaScannerInterfaces = {};
 /**
  * Class representing the results of a scan operation.
  *
  * @interface
  */
-importer.MediaScanner = class {
+mediaScannerInterfaces.MediaScanner = class {
   /**
    * Initiates scanning.
    *
    * @param {!DirectoryEntry} directory
    * @param {!importer.ScanMode} mode
-   * @return {!importer.ScanResult} ScanResult object representing the scan
-   *     job both while in-progress and when completed.
+   * @return {!mediaScannerInterfaces.ScanResult} ScanResult object representing
+   *     the scan job both while in-progress and when completed.
    */
   scanDirectory(directory, mode) {}
 
@@ -27,37 +28,37 @@
    *     must be of a supported media type. Individually supplied files
    *     are not subject to deduplication.
    * @param {!importer.ScanMode} mode The method to detect new files.
-   * @return {!importer.ScanResult} ScanResult object representing the scan
-   *     job for the explicitly supplied entries.
+   * @return {!mediaScannerInterfaces.ScanResult} ScanResult object representing
+   *     the scan job for the explicitly supplied entries.
    */
   scanFiles(entries, mode) {}
 
   /**
    * Adds an observer, which will be notified on scan events.
    *
-   * @param {!importer.ScanObserver} observer
+   * @param {!mediaScannerInterfaces.ScanObserver} observer
    */
   addObserver(observer) {}
 
   /**
    * Remove a previously registered observer.
    *
-   * @param {!importer.ScanObserver} observer
+   * @param {!mediaScannerInterfaces.ScanObserver} observer
    */
   removeObserver(observer) {}
 };
 
 /**
- * @typedef {function(!importer.ScanEvent, importer.ScanResult)}
+ * @typedef {function(!importer.ScanEvent, mediaScannerInterfaces.ScanResult)}
  */
-importer.ScanObserver;
+mediaScannerInterfaces.ScanObserver;
 
 /**
  * Class representing the results of a scan operation.
  *
  * @interface
  */
-importer.ScanResult = class {
+mediaScannerInterfaces.ScanResult = class {
   /**
    * @return {boolean} true if scanning is complete.
    */
@@ -116,12 +117,12 @@
    * Returns a promise that fires when scanning is finished
    * normally or has been canceled.
    *
-   * @return {!Promise<!importer.ScanResult>}
+   * @return {!Promise<!mediaScannerInterfaces.ScanResult>}
    */
   whenFinal() {}
 
   /**
-   * @return {!importer.ScanResult.Statistics}
+   * @return {!mediaScannerInterfaces.ScanResult.Statistics}
    */
   getStatistics() {}
 };
@@ -139,4 +140,4 @@
  *   progress: number
  * }}
  */
-importer.ScanResult.Statistics;
+mediaScannerInterfaces.ScanResult.Statistics;
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 3a0aeac..56fb50cf 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -78,9 +78,12 @@
     ":background_base.m",
     ":crostini.m",
     ":drive_sync_handler.m",
+    ":duplicate_finder.m",
     ":entry_location_impl.m",
     ":file_operation_util.m",
     ":import_history.m",
+    ":media_import_handler.m",
+    ":media_scanner.m",
     ":metadata_proxy.m",
     ":mock_drive_sync_handler.m",
     ":mock_file_operation_manager.m",
@@ -109,6 +112,7 @@
   uses_js_modules = true
   deps = [
     ":mock_crostini.m",
+    ":mock_media_scanner.m",
     ":mock_volume_manager.m",
     ":test_import_history.m",
   ]
@@ -144,7 +148,6 @@
     "//ui/file_manager/externs/background/file_browser_background_full.js",
     "//ui/file_manager/externs/background/file_operation_manager.js",
     "//ui/file_manager/externs/background/import_history.js",
-    "//ui/file_manager/externs/background/import_runner.js",
     "//ui/file_manager/externs/background/media_import_handler.js",
     "//ui/file_manager/externs/background/media_scanner.js",
     "//ui/file_manager/externs/background/progress_center.js",
@@ -387,14 +390,36 @@
   externs_list = [ "//ui/file_manager/externs/background/duplicate_finder.js" ]
 }
 
-js_unittest("duplicate_finder_unittest") {
+js_library("duplicate_finder.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/background/js/duplicate_finder.m.js" ]
   deps = [
-    ":duplicate_finder",
-    ":mock_volume_manager",
-    ":test_import_history",
-    "//ui/file_manager/base/js:mock_chrome",
-    "//ui/file_manager/file_manager/common/js:mock_entry",
-    "//ui/file_manager/file_manager/common/js:test_importer_common",
+    ":import_history.m",
+    ":volume_manager_factory.m",
+    "//ui/file_manager/base/js:volume_manager_types.m",
+    "//ui/file_manager/externs:volume_manager.m",
+    "//ui/file_manager/externs/background:duplicate_finder.m",
+    "//ui/file_manager/externs/background:import_history.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+    "//ui/file_manager/file_manager/common/js:lru_cache.m",
+    "//ui/file_manager/file_manager/common/js:metrics.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_unittest("duplicate_finder_unittest.m") {
+  deps = [
+    ":duplicate_finder.m",
+    ":mock_volume_manager.m",
+    ":test_import_history.m",
+    "//chrome/test/data/webui:chai_assert",
+    "//ui/file_manager/base/js:mock_chrome.m",
+    "//ui/file_manager/base/js:test_error_reporting.m",
+    "//ui/file_manager/base/js:volume_manager_types.m",
+    "//ui/file_manager/externs:volume_info.m",
+    "//ui/file_manager/externs/background:duplicate_finder.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+    "//ui/file_manager/file_manager/common/js:mock_entry.m",
   ]
 }
 
@@ -549,6 +574,7 @@
     "//ui/file_manager/base/js:mock_chrome.m",
     "//ui/file_manager/base/js:test_error_reporting.m",
     "//ui/file_manager/externs/background:import_history.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
     "//ui/file_manager/file_manager/common/js:mock_entry.m",
     "//ui/file_manager/file_manager/common/js:test_importer_common.m",
     "//ui/file_manager/file_manager/common/js:unittest_util.m",
@@ -582,26 +608,55 @@
     "//ui/file_manager/file_manager/common/js:metrics",
   ]
   externs_list = [
-    "//ui/file_manager/externs/background/import_runner.js",
     "//ui/file_manager/externs/background/duplicate_finder.js",
     "//ui/file_manager/externs/background/task_queue.js",
     "//ui/file_manager/externs/background/media_import_handler.js",
   ]
 }
 
-js_unittest("media_import_handler_unittest") {
+js_library("media_import_handler.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/background/js/media_import_handler.m.js" ]
   deps = [
-    ":file_operation_util",
-    ":media_import_handler",
-    ":mock_drive_sync_handler",
-    ":mock_media_scanner",
-    ":mock_progress_center",
-    ":mock_volume_manager",
-    ":test_import_history",
-    "//ui/file_manager/base/js:mock_chrome",
-    "//ui/file_manager/base/js:test_error_reporting",
-    "//ui/file_manager/file_manager/common/js:mock_entry",
-    "//ui/file_manager/file_manager/common/js:test_importer_common",
+    ":file_operation_util.m",
+    ":task_queue.m",
+    "//ui/file_manager/externs/background:drive_sync_handler.m",
+    "//ui/file_manager/externs/background:duplicate_finder.m",
+    "//ui/file_manager/externs/background:import_history.m",
+    "//ui/file_manager/externs/background:media_import_handler.m",
+    "//ui/file_manager/externs/background:media_scanner.m",
+    "//ui/file_manager/externs/background:progress_center.m",
+    "//ui/file_manager/externs/background:task_queue.m",
+    "//ui/file_manager/file_manager/common/js:async_util.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+    "//ui/file_manager/file_manager/common/js:metrics.m",
+    "//ui/file_manager/file_manager/common/js:progress_center_common.m",
+    "//ui/file_manager/file_manager/common/js:util.m",
+    "//ui/webui/resources/js:assert.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_unittest("media_import_handler_unittest.m") {
+  deps = [
+    ":file_operation_util.m",
+    ":media_import_handler.m",
+    ":mock_drive_sync_handler.m",
+    ":mock_media_scanner.m",
+    ":mock_progress_center.m",
+    ":mock_volume_manager.m",
+    ":test_import_history.m",
+    "//chrome/test/data/webui:chai_assert",
+    "//ui/file_manager/base/js:mock_chrome.m",
+    "//ui/file_manager/base/js:test_error_reporting.m",
+    "//ui/file_manager/base/js:volume_manager_types.m",
+    "//ui/file_manager/externs:volume_info.m",
+    "//ui/file_manager/externs/background:duplicate_finder.m",
+    "//ui/file_manager/externs/background:import_history.m",
+    "//ui/file_manager/externs/background:media_import_handler.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+    "//ui/file_manager/file_manager/common/js:mock_entry.m",
+    "//ui/file_manager/file_manager/common/js:test_importer_common.m",
   ]
 }
 
@@ -616,6 +671,18 @@
   visibility = [ "//ui/file_manager/file_manager/*" ]
 }
 
+js_library("mock_media_scanner.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/background/js/mock_media_scanner.m.js" ]
+  deps = [
+    ":media_scanner.m",
+    "//chrome/test/data/webui:chai_assert",
+    "//ui/file_manager/externs/background:media_scanner.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("media_scanner") {
   deps = [
     ":file_operation_util",
@@ -624,11 +691,30 @@
   externs_list = [ "//ui/file_manager/externs/background/media_scanner.js" ]
 }
 
-js_unittest("media_scanner_unittest") {
+js_library("media_scanner.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/background/js/media_scanner.m.js" ]
   deps = [
-    ":media_scanner",
-    ":mock_media_scanner",
-    "//ui/file_manager/file_manager/common/js:test_importer_common",
+    ":file_operation_util.m",
+    ":metadata_proxy.m",
+    "//ui/file_manager/externs/background:media_scanner.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+    "//ui/webui/resources/js:assert.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_unittest("media_scanner_unittest.m") {
+  deps = [
+    ":file_operation_util.m",
+    ":media_scanner.m",
+    ":mock_media_scanner.m",
+    ":test_import_history.m",
+    "//chrome/test/data/webui:chai_assert",
+    "//ui/file_manager/base/js:test_error_reporting.m",
+    "//ui/file_manager/externs/background:media_scanner.m",
+    "//ui/file_manager/file_manager/common/js:importer_common.m",
+    "//ui/file_manager/file_manager/common/js:unittest_util.m",
   ]
 }
 
@@ -734,7 +820,6 @@
     "//ui/file_manager/externs/background_window.js",
     "//ui/file_manager/externs/background/file_operation_manager.js",
     "//ui/file_manager/externs/background/import_history.js",
-    "//ui/file_manager/externs/background/import_runner.js",
     "//ui/file_manager/externs/background/background_base.js",
     "//ui/file_manager/externs/background/file_browser_background_full.js",
   ]
@@ -952,7 +1037,10 @@
   deps = [
     ":crostini_unittest.m",
     ":drive_sync_handler_unittest.m",
+    ":duplicate_finder_unittest.m",
     ":import_history_unittest.m",
+    ":media_import_handler_unittest.m",
+    ":media_scanner_unittest.m",
     ":metadata_proxy_unittest.m",
     ":mount_metrics_unittest.m",
     ":task_queue_unittest.m",
@@ -971,11 +1059,8 @@
 js_test_gen_html("js_test_gen_html") {
   deps = [
     ":device_handler_unittest",
-    ":duplicate_finder_unittest",
     ":file_operation_handler_unittest",
     ":file_operation_manager_unittest",
-    ":media_import_handler_unittest",
-    ":media_scanner_unittest",
     ":trash_unittest",
     ":volume_manager_unittest",
   ]
@@ -1006,6 +1091,10 @@
     "test_import_history.js",
     "import_history.js",
     "drive_sync_handler.js",
+    "media_scanner.js",
+    "mock_media_scanner.js",
+    "duplicate_finder.js",
+    "media_import_handler.js",
   ]
 
   namespace_rewrites = cr_namespace_rewrites
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index 11b99a4..c2cbf62 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -56,25 +56,26 @@
     this.driveSyncHandler = new DriveSyncHandlerImpl(this.progressCenter);
 
     /**
-     * @type {!importer.DispositionChecker.CheckerFunction}
+     * @type {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
      */
     this.dispositionChecker_ =
-        importer.DispositionCheckerImpl.createChecker(this.historyLoader);
+        duplicateFinder.DispositionCheckerImpl.createChecker(
+            this.historyLoader);
 
     /**
      * Provides support for scanning media devices as part of Cloud Import.
-     * @type {!importer.MediaScanner}
+     * @type {!mediaScannerInterfaces.MediaScanner}
      */
-    this.mediaScanner = new importer.DefaultMediaScanner(
+    this.mediaScanner = new mediaScanner.DefaultMediaScanner(
         importerHistory.createMetadataHashcode, this.dispositionChecker_,
-        importer.DefaultDirectoryWatcher.create);
+        mediaScanner.DefaultDirectoryWatcher.create);
 
     /**
      * Handles importing of user media (e.g. photos, videos) from removable
      * devices.
-     * @type {!importer.MediaImportHandler}
+     * @type {!mediaImportInterfaces.MediaImportHandler}
      */
-    this.mediaImportHandler = new importer.MediaImportHandlerImpl(
+    this.mediaImportHandler = new mediaImport.MediaImportHandlerImpl(
         this.progressCenter, this.historyLoader, this.dispositionChecker_,
         this.driveSyncHandler);
 
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder.js b/ui/file_manager/file_manager/background/js/duplicate_finder.js
index 46da4164..d1c0317 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder.js
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder.js
@@ -2,14 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Namespace
-window.importer = window.importer || {};
+/**
+ * @fileoverview
+ * @suppress {uselessCode} Temporary suppress because of the line exporting.
+ */
+
+// clang-format off
+// #import {VolumeManagerCommon} from '../../../base/js/volume_manager_types.m.js';
+
+// #import {VolumeManager} from '../../../externs/volume_manager.m.js';
+// #import {importerHistoryInterfaces} from '../../../externs/background/import_history.m.js';
+// #import {duplicateFinderInterfaces} from '../../../externs/background/duplicate_finder.m.js';
+// #import {volumeManagerFactory} from './volume_manager_factory.m.js';
+// #import {metrics} from '../../common/js/metrics.m.js';
+// #import {importerHistory} from './import_history.m.js';
+// #import {LRUCache} from '../../common/js/lru_cache.m.js';
+// #import {importer} from '../../common/js/importer_common.m.js';
+// clang-format on
+
+// eslint-disable-next-line no-var
+var duplicateFinder = {};
 
 /**
  * A duplicate finder for Google Drive.
  *
  */
-importer.DriveDuplicateFinder = class DriveDuplicateFinder {
+duplicateFinder.DriveDuplicateFinder = class {
   constructor() {
     /** @private {Promise<string>} */
     this.driveIdPromise_ = null;
@@ -18,8 +36,8 @@
      * An bounded cache of most recently calculated file content hashcodes.
      * @private {!LRUCache<!Promise<string>>}
      */
-    this.hashCache_ =
-        new LRUCache(importer.DriveDuplicateFinder.MAX_CACHED_HASHCODES_);
+    this.hashCache_ = new LRUCache(
+        duplicateFinder.DriveDuplicateFinder.MAX_CACHED_HASHCODES_);
   }
 
   /**
@@ -54,22 +72,22 @@
       }
 
       const hashPromise = new Promise(
-          /** @this {importer.DriveDuplicateFinder} */
+          /** @this {duplicateFinder.DriveDuplicateFinder} */
           (resolve, reject) => {
             const startTime = new Date().getTime();
             chrome.fileManagerPrivate.computeChecksum(
                 entry,
                 /**
                  * @param {string|undefined} result The content hash.
-                 * @this {importer.DriveDuplicateFinder}
+                 * @this {duplicateFinder.DriveDuplicateFinder}
                  */
                 result => {
                   const elapsedTime = new Date().getTime() - startTime;
                   // Send the timing to GA only if it is sorta exceptionally
                   // long. A one second, CPU intensive operation, is pretty
                   // long.
-                  if (elapsedTime >=
-                      importer.DriveDuplicateFinder.HASH_EVENT_THRESHOLD_) {
+                  if (elapsedTime >= duplicateFinder.DriveDuplicateFinder
+                                         .HASH_EVENT_THRESHOLD_) {
                     console.info(
                         'Content hash computation took ' + elapsedTime +
                         ' ms.');
@@ -131,7 +149,7 @@
    */
   searchFilesByHash_(hash, volumeId) {
     return new Promise(
-        /** @this {importer.DriveDuplicateFinder} */
+        /** @this {duplicateFinder.DriveDuplicateFinder} */
         (resolve, reject) => {
           const startTime = new Date().getTime();
           chrome.fileManagerPrivate.searchFilesByHashes(
@@ -142,8 +160,8 @@
               urls => {
                 const elapsedTime = new Date().getTime() - startTime;
                 // Send the timing to GA only if it is sorta exceptionally long.
-                if (elapsedTime >=
-                    importer.DriveDuplicateFinder.SEARCH_EVENT_THRESHOLD_) {
+                if (elapsedTime >= duplicateFinder.DriveDuplicateFinder
+                                       .SEARCH_EVENT_THRESHOLD_) {
                   metrics.recordTime(
                       'DriveDuplicateFinder.LongSearchByHash', elapsedTime);
                 }
@@ -158,13 +176,13 @@
 };
 
 /** @private @const {number} */
-importer.DriveDuplicateFinder.HASH_EVENT_THRESHOLD_ = 5000;
+duplicateFinder.DriveDuplicateFinder.HASH_EVENT_THRESHOLD_ = 5000;
 
 /** @private @const {number} */
-importer.DriveDuplicateFinder.SEARCH_EVENT_THRESHOLD_ = 1000;
+duplicateFinder.DriveDuplicateFinder.SEARCH_EVENT_THRESHOLD_ = 1000;
 
 /** @private @const {number} */
-importer.DriveDuplicateFinder.MAX_CACHED_HASHCODES_ = 10000;
+duplicateFinder.DriveDuplicateFinder.MAX_CACHED_HASHCODES_ = 10000;
 
 
 /**
@@ -173,18 +191,18 @@
  * primary source for duplicate checking (with the exception
  * of in-scan deduplication, where duplicate results that
  * are within the scan are ignored).
- * @implements {importer.DispositionChecker}
+ * @implements {duplicateFinderInterfaces.DispositionChecker}
  */
-importer.DispositionCheckerImpl = class {
+duplicateFinder.DispositionCheckerImpl = class {
   /**
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
-   * @param {!importer.DriveDuplicateFinder} contentMatcher
+   * @param {!duplicateFinder.DriveDuplicateFinder} contentMatcher
    */
   constructor(historyLoader, contentMatcher) {
     /** @private {!importerHistoryInterfaces.HistoryLoader} */
     this.historyLoader_ = historyLoader;
 
-    /** @private {!importer.DriveDuplicateFinder} */
+    /** @private {!duplicateFinder.DriveDuplicateFinder} */
     this.contentMatcher_ = contentMatcher;
   }
 
@@ -200,7 +218,7 @@
     }
 
     return new Promise(
-        /** @this {importer.DispositionChecker} */
+        /** @this {duplicateFinderInterfaces.DispositionChecker} */
         (resolve, reject) => {
           this.hasHistoryDuplicate_(entry, destination)
               .then(
@@ -264,11 +282,14 @@
    *
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
    *
-   * @return {!importer.DispositionChecker.CheckerFunction}
+   * @return {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
    */
   static createChecker(historyLoader) {
-    const checker = new importer.DispositionCheckerImpl(
-        historyLoader, new importer.DriveDuplicateFinder());
+    const checker = new duplicateFinder.DispositionCheckerImpl(
+        historyLoader, new duplicateFinder.DriveDuplicateFinder());
     return checker.getDisposition.bind(checker);
   }
 };
+
+// eslint-disable-next-line semi,no-extra-semi
+/* #export */ {duplicateFinder};
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.m.js
similarity index 73%
rename from ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js
rename to ui/file_manager/file_manager/background/js/duplicate_finder_unittest.m.js
index 14c06a8e..c559168 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.js
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.m.js
@@ -2,8 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @type {!importer.DriveDuplicateFinder} */
-let duplicateFinder;
+import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
+
+import {installMockChrome, MockCommandLinePrivate} from '../../../base/js/mock_chrome.m.js';
+import {reportPromise} from '../../../base/js/test_error_reporting.m.js';
+import {VolumeManagerCommon} from '../../../base/js/volume_manager_types.m.js';
+import {duplicateFinderInterfaces} from '../../../externs/background/duplicate_finder.m.js';
+import {VolumeInfo} from '../../../externs/volume_info.m.js';
+import {importer} from '../../common/js/importer_common.m.js';
+import {MockFileSystem} from '../../common/js/mock_entry.m.js';
+
+import {duplicateFinder} from './duplicate_finder.m.js';
+import {MockVolumeManager} from './mock_volume_manager.m.js';
+import {importerTestHistory} from './test_import_history.m.js';
+
+/** @type {!duplicateFinder.DriveDuplicateFinder} */
+let duplicateFinderTest;
 
 /** @type {VolumeInfo} */
 let drive;
@@ -26,14 +40,14 @@
 /** @type {!importerTestHistory.TestImportHistory} */
 let testHistory;
 
-/** @type {importer.DispositionChecker.CheckerFunction} */
+/** @type {duplicateFinderInterfaces.DispositionChecker.CheckerFunction} */
 let getDisposition;
 
 window.metrics = {
   recordTime: function() {},
 };
 
-function setUp() {
+export function setUp() {
   window.loadTimeData.getString = id => id;
   const mockChrome = {
     fileManagerPrivate: {
@@ -64,7 +78,6 @@
 
   installMockChrome(mockChrome);
   new MockCommandLinePrivate();
-  // importer.setupTestLogger();
   fileSystem = new MockFileSystem('fake-filesystem');
 
   const volumeManager = new MockVolumeManager();
@@ -75,25 +88,26 @@
   MockVolumeManager.installMockSingleton(volumeManager);
 
   testHistory = new importerTestHistory.TestImportHistory();
-  duplicateFinder = new importer.DriveDuplicateFinder();
-  getDisposition = importer.DispositionCheckerImpl.createChecker(testHistory);
+  duplicateFinderTest = new duplicateFinder.DriveDuplicateFinder();
+  getDisposition =
+      duplicateFinder.DispositionCheckerImpl.createChecker(testHistory);
 }
 
 // Verifies the correct result when a duplicate exists.
-function testCheckDuplicateTrue(callback) {
+export function testCheckDuplicateTrue(callback) {
   const filePaths = ['/foo.txt'];
   const fileHashes = ['abc123'];
   const files = setupHashes(filePaths, fileHashes);
 
   reportPromise(
-      duplicateFinder.isDuplicate(files[0]).then(isDuplicate => {
+      duplicateFinderTest.isDuplicate(files[0]).then(isDuplicate => {
         assertTrue(isDuplicate);
       }),
       callback);
 }
 
 // Verifies the correct result when a duplicate doesn't exist.
-function testCheckDuplicateFalse(callback) {
+export function testCheckDuplicateFalse(callback) {
   const filePaths = ['/foo.txt'];
   const fileHashes = ['abc123'];
   const files = setupHashes(filePaths, fileHashes);
@@ -104,13 +118,13 @@
   const newFile = /** @type {!FileEntry} */ (fileSystem.entries[newFilePath]);
 
   reportPromise(
-      duplicateFinder.isDuplicate(newFile).then(isDuplicate => {
+      duplicateFinderTest.isDuplicate(newFile).then(isDuplicate => {
         assertFalse(isDuplicate);
       }),
       callback);
 }
 
-function testDispositionChecker_ContentDupe(callback) {
+export function testDispositionChecker_ContentDupe(callback) {
   const filePaths = ['/foo.txt'];
   const fileHashes = ['abc123'];
   const files = setupHashes(filePaths, fileHashes);
@@ -125,7 +139,7 @@
       callback);
 }
 
-function testDispositionChecker_HistoryDupe(callback) {
+export function testDispositionChecker_HistoryDupe(callback) {
   const filePaths = ['/foo.txt'];
   const fileHashes = ['abc123'];
   const files = setupHashes(filePaths, fileHashes);
@@ -142,7 +156,7 @@
       callback);
 }
 
-function testDispositionChecker_Original(callback) {
+export function testDispositionChecker_Original(callback) {
   const filePaths = ['/foo.txt'];
   const fileHashes = ['abc123'];
   const files = setupHashes(filePaths, fileHashes);
diff --git a/ui/file_manager/file_manager/background/js/import_history_unittest.m.js b/ui/file_manager/file_manager/background/js/import_history_unittest.m.js
index af103199..a5e5c70 100644
--- a/ui/file_manager/file_manager/background/js/import_history_unittest.m.js
+++ b/ui/file_manager/file_manager/background/js/import_history_unittest.m.js
@@ -8,8 +8,9 @@
 import {MockChromeStorageAPI} from '../../../base/js/mock_chrome.m.js';
 import {reportPromise} from '../../../base/js/test_error_reporting.m.js';
 import {importerHistoryInterfaces} from '../../../externs/background/import_history.m.js';
-import { MockFileEntry,MockFileSystem} from '../../common/js/mock_entry.m.js';
-import {importer} from '../../common/js/test_importer_common.m.js';
+import {importer} from '../../common/js/importer_common.m.js';
+import {MockFileEntry,MockFileSystem} from '../../common/js/mock_entry.m.js';
+import {importerTest} from '../../common/js/test_importer_common.m.js';
 import {TestCallRecorder} from '../../common/js/unittest_util.m.js';
 
 import {importerHistory} from './import_history.m.js';
@@ -42,7 +43,7 @@
 /** @type {!FileEntry} */
 let testFileEntry;
 
-/** @type {!importer.TestLogger} */
+/** @type {!importerTest.TestLogger} */
 let testLogger;
 
 /** @type {!importerHistory.RecordStorage} */
@@ -297,8 +298,8 @@
  * Installs importer test logger.
  */
 function installTestLogger() {
-  testLogger = new importer.TestLogger();
-  importer.getLogger = () => {
+  testLogger = new importerTest.TestLogger();
+  importerTest.getLogger = () => {
     return testLogger;
   };
 }
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index 2c24b2f..e47190a 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -2,24 +2,41 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Namespace
-window.importer = window.importer || {};
+// clang-format off
+// #import {assert} from 'chrome://resources/js/assert.m.js';
+// #import {AsyncUtil} from '../../common/js/async_util.m.js';
+// #import {strf, util} from '../../common/js/util.m.js';
+// #import {ProgressCenterItem, ProgressItemType, ProgressItemState} from '../../common/js/progress_center_common.m.js';
+// #import {fileOperationUtil} from './file_operation_util.m.js';
+// #import {taskQueue} from './task_queue.m.js';
+// #import {metrics} from '../../common/js/metrics.m.js';
 
-importer.MediaImportHandler = importer.MediaImportHandler || {};
-importer.MediaImportHandler.ImportTask =
-    importer.MediaImportHandler.ImportTask || {};
+// #import {importer} from '../../common/js/importer_common.m.js';
+// #import {DriveSyncHandler} from '../../../externs/background/drive_sync_handler.m.js';
+// #import {importerHistoryInterfaces} from '../../../externs/background/import_history.m.js';
+// #import {duplicateFinderInterfaces} from '../../../externs/background/duplicate_finder.m.js';
+// #import {mediaScannerInterfaces} from '../../../externs/background/media_scanner.m.js';
+// #import {mediaImportInterfaces} from '../../../externs/background/media_import_handler.m.js';
+// #import {taskQueueInterfaces} from '../../../externs/background/task_queue.m.js';
+// #import {ProgressCenter} from '../../../externs/background/progress_center.m.js';
+// clang-format on
+
+
+// Namespace
+/* #export */ const mediaImport = {};
 
 /**
  * Handler for importing media from removable devices into the user's Drive.
  *
- * @implements {importer.ImportRunner}
- * @implements {importer.MediaImportHandler}
+ * @implements {mediaImportInterfaces.ImportRunner}
+ * @implements {mediaImportInterfaces.MediaImportHandler}
  */
-importer.MediaImportHandlerImpl = class {
+mediaImport.MediaImportHandlerImpl = class {
   /**
    * @param {!ProgressCenter} progressCenter
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
-   * @param {!importer.DispositionChecker.CheckerFunction} dispositionChecker
+   * @param {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
+   *     dispositionChecker
    * @param {!DriveSyncHandler} driveSyncHandler
    */
   constructor(
@@ -44,7 +61,9 @@
     /** @private {number} */
     this.nextTaskId_ = 0;
 
-    /** @private {!importer.DispositionChecker.CheckerFunction} */
+    /**
+     * @private {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
+     */
     this.getDisposition_ = dispositionChecker;
 
     /**
@@ -71,7 +90,7 @@
 
   /** @override */
   importFromScanResult(scanResult, destination, directoryPromise) {
-    const task = new importer.MediaImportHandler.ImportTaskImpl(
+    const task = new mediaImport.ImportTaskImpl(
         this.generateTaskId_(), this.historyLoader_, scanResult,
         directoryPromise, destination, this.getDisposition_);
 
@@ -99,7 +118,7 @@
   /**
    * Sends updates to the ProgressCenter when an import is happening.
    *
-   * @param {!importer.MediaImportHandler.ImportTaskImpl} task
+   * @param {!mediaImport.ImportTaskImpl} task
    * @param {string} updateType
    * @private
    */
@@ -175,7 +194,7 @@
 
   /**
    * Restarts a task with failed entries.
-   * @param {!importer.MediaImportHandler.ImportTaskImpl} task
+   * @param {!mediaImport.ImportTaskImpl} task
    */
   retryTaskFailedEntries_(task) {
     // Reset the entry lists.
@@ -198,17 +217,17 @@
  * the FileOperationManager (and thus *spawns* an associated
  * FileOperationManager.CopyTask) but this is a temporary state of affairs.
  *
- * @implements {importer.MediaImportHandler.ImportTask}
+ * @implements {mediaImportInterfaces.MediaImportHandler.ImportTask}
  */
-importer.MediaImportHandler.ImportTaskImpl =
-    class extends taskQueue.BaseTaskImpl {
+mediaImport.ImportTaskImpl = class extends taskQueue.BaseTaskImpl {
   /**
    * @param {string} taskId
    * @param {!importerHistoryInterfaces.HistoryLoader} historyLoader
-   * @param {!importer.ScanResult} scanResult
+   * @param {!mediaScannerInterfaces.ScanResult} scanResult
    * @param {!Promise<!DirectoryEntry>} directoryPromise
    * @param {!importer.Destination} destination The logical destination.
-   * @param {!importer.DispositionChecker.CheckerFunction} dispositionChecker
+   * @param {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
+   *     dispositionChecker
    */
   constructor(
       taskId, historyLoader, scanResult, directoryPromise, destination,
@@ -224,7 +243,7 @@
     /** @private {!Promise<!DirectoryEntry>} */
     this.directoryPromise_ = directoryPromise;
 
-    /** @private {!importer.ScanResult} */
+    /** @private {!mediaScannerInterfaces.ScanResult} */
     this.scanResult_ = scanResult;
 
     /** @private {!importerHistoryInterfaces.HistoryLoader} */
@@ -254,7 +273,9 @@
     /** @private {number} */
     this.errorCount_ = 0;
 
-    /** @private {!importer.DispositionChecker.CheckerFunction} */
+    /**
+     * @private {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction}
+     */
     this.getDisposition_ = dispositionChecker;
 
     /**
@@ -381,7 +402,7 @@
               this.scanResult_.getDuplicateFileEntries().forEach(
                   /**
                    * @param {!FileEntry} entry
-                   * @this {importer.MediaImportHandler.ImportTask}
+                   * @this {mediaImportInterfaces.MediaImportHandler.ImportTask}
                    */
                   entry => {
                     history.markImported(entry, this.destination_);
@@ -450,7 +471,7 @@
      * Updates the task when the copy code reports progress.
      * @param {string} sourceUrl
      * @param {number} processedBytes
-     * @this {importer.MediaImportHandler.ImportTask}
+     * @this {mediaImportInterfaces.MediaImportHandler.ImportTask}
      */
     const onProgress = (sourceUrl, processedBytes) => {
       // Update the running total, then send a progress update.
@@ -464,7 +485,7 @@
      * Updates the task when the new file has been created.
      * @param {string} sourceUrl
      * @param {Entry} destinationEntry
-     * @this {importer.MediaImportHandler.ImportTask}
+     * @this {mediaImportInterfaces.MediaImportHandler.ImportTask}
      */
     const onEntryChanged = (sourceUrl, destinationEntry) => {
       this.processedBytes_ -= currentBytes;
@@ -472,7 +493,7 @@
       destinationEntry.size = entry.size;
       this.notify(
           /** @type {importer.UpdateType} */
-          (importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED), {
+          (mediaImport.UpdateType.ENTRY_CHANGED), {
             sourceUrl: sourceUrl,
             destination: destinationEntry,
           });
@@ -481,7 +502,7 @@
 
     /**
      * @param {Entry} destinationEntry The new destination entry.
-     * @this {importer.MediaImportHandler.ImportTask}
+     * @this {mediaImportInterfaces.MediaImportHandler.ImportTask}
      */
     const onComplete = destinationEntry => {
       this.cancelCallback_ = null;
@@ -490,7 +511,7 @@
       resolver.resolve(destinationEntry);
     };
 
-    /** @this {importer.MediaImportHandler.ImportTask} */
+    /** @this {mediaImportInterfaces.MediaImportHandler.ImportTask} */
     const onError = error => {
       this.cancelCallback_ = null;
       if (error.name === util.FileError.ABORT_ERR) {
@@ -599,6 +620,6 @@
  * ImportTask to listen for these kinds of updates.
  * @enum {string}
  */
-importer.MediaImportHandler.ImportTask.UpdateType = {
+mediaImport.UpdateType = {
   ENTRY_CHANGED: 'ENTRY_CHANGED'
 };
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.m.js
similarity index 88%
rename from ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
rename to ui/file_manager/file_manager/background/js/media_import_handler_unittest.m.js
index ff7d9c19..a852add9 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.m.js
@@ -2,18 +2,37 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-'use strict';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
+
+import {installMockChrome, MockCommandLinePrivate} from '../../../base/js/mock_chrome.m.js';
+import {reportPromise} from '../../../base/js/test_error_reporting.m.js';
+import {VolumeManagerCommon} from '../../../base/js/volume_manager_types.m.js';
+import {duplicateFinderInterfaces} from '../../../externs/background/duplicate_finder.m.js';
+import {importerHistoryInterfaces} from '../../../externs/background/import_history.m.js';
+import {mediaImportInterfaces} from '../../../externs/background/media_import_handler.m.js';
+import {VolumeInfo} from '../../../externs/volume_info.m.js';
+import {importer} from '../../common/js/importer_common.m.js';
+import {MockDirectoryEntry, MockFileSystem} from '../../common/js/mock_entry.m.js';
+import {importerTest} from '../../common/js/test_importer_common.m.js';
+
+import {fileOperationUtil} from './file_operation_util.m.js';
+import {mediaImport} from './media_import_handler.m.js';
+import {MockDriveSyncHandler} from './mock_drive_sync_handler.m.js';
+import {TestScanResult} from './mock_media_scanner.m.js';
+import {MockProgressCenter} from './mock_progress_center.m.js';
+import {MockVolumeManager} from './mock_volume_manager.m.js';
+import {importerTestHistory} from './test_import_history.m.js';
 
 /** @type {!MockProgressCenter} */
 let progressCenter;
 
-/** @type {!importer.MediaImportHandler} */
+/** @type {!mediaImportInterfaces.MediaImportHandler} */
 let mediaImporter;
 
 /** @type {!importerTestHistory.TestImportHistory} */
 let importHistory;
 
-/** @type {!importer.DispositionChecker.CheckerFunction} */
+/** @type {!duplicateFinderInterfaces.DispositionChecker.CheckerFunction} */
 let dispositionChecker;
 
 /** @type {!MockCopyTo} */
@@ -46,7 +65,7 @@
 let mockChrome;
 
 // Set up the test components.
-function setUp() {
+export function setUp() {
   // Mock loadTimeData strings.
   window.loadTimeData.getString = id => id;
 
@@ -93,8 +112,8 @@
   progressCenter = new MockProgressCenter();
   importHistory = new importerTestHistory.TestImportHistory();
   driveSyncHandler = new MockDriveSyncHandler();
-  importer.setupTestLogger();
-  mediaImporter = new importer.MediaImportHandlerImpl(
+  importerTest.setupTestLogger();
+  mediaImporter = new mediaImport.MediaImportHandlerImpl(
       progressCenter, importHistory, dispositionChecker, driveSyncHandler);
 
   // Setup the copy destination.
@@ -105,7 +124,7 @@
 /**
  * Tests media imports.
  */
-function testImportMedia(callback) {
+export function testImportMedia(callback) {
   const media = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
     '/DCIM/photos0/IMG00002.jpg',
@@ -152,7 +171,7 @@
 /**
  * Tests media import duplicate detection.
  */
-function testImportMedia_skipAndMarkDuplicatedFiles(callback) {
+export function testImportMedia_skipAndMarkDuplicatedFiles(callback) {
   const DUPLICATED_FILE_PATH_1 = '/DCIM/photos0/duplicated_1.jpg';
   const DUPLICATED_FILE_PATH_2 = '/DCIM/photos0/duplicated_2.jpg';
   const ORIGINAL_FILE_NAME = 'new_image.jpg';
@@ -173,7 +192,7 @@
     }
     return Promise.resolve(importer.Disposition.ORIGINAL);
   };
-  mediaImporter = new importer.MediaImportHandlerImpl(
+  mediaImporter = new mediaImport.MediaImportHandlerImpl(
       progressCenter, importHistory, dispositionChecker, driveSyncHandler);
   const scanResult = new TestScanResult(media);
   const importTask = mediaImporter.importFromScanResult(
@@ -223,7 +242,7 @@
 /**
  * Tests media import uses encoded URLs.
  */
-function testImportMedia_EmploysEncodedUrls(callback) {
+export function testImportMedia_EmploysEncodedUrls(callback) {
   const media = setupFileSystem([
     '/DCIM/photos0/Mom and Dad.jpg',
   ]);
@@ -267,7 +286,7 @@
  * Tests that when files with duplicate names are imported, that they don't
  * overwrite one another.
  */
-function testImportMediaWithDuplicateFilenames(callback) {
+export function testImportMediaWithDuplicateFilenames(callback) {
   const media = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
     '/DCIM/photos0/IMG00002.jpg',
@@ -315,7 +334,7 @@
 /**
  * Tests that active media imports keep chrome.power awake.
  */
-function testKeepAwakeDuringImport(callback) {
+export function testKeepAwakeDuringImport(callback) {
   const media = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
     '/DCIM/photos0/IMG00002.jpg',
@@ -366,7 +385,7 @@
 /**
  * Tests that media imports update import history.
  */
-function testUpdatesHistoryAfterImport(callback) {
+export function testUpdatesHistoryAfterImport(callback) {
   const entries = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
     '/DCIM/photos1/IMG00003.jpg',
@@ -422,7 +441,7 @@
 /**
  * Tests cancelling a media import.
  */
-function testImportCancellation(callback) {
+export function testImportCancellation(callback) {
   const media = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
     '/DCIM/photos0/IMG00002.jpg',
@@ -455,8 +474,7 @@
   // Simulate cancellation after the expected number of copies is done.
   let copyCount = 0;
   importTask.addObserver(updateType => {
-    if (updateType ===
-        importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
+    if (updateType === mediaImport.UpdateType.ENTRY_CHANGED) {
       copyCount++;
       if (copyCount === EXPECTED_COPY_COUNT) {
         importTask.requestCancel();
@@ -479,10 +497,10 @@
 /**
  * Tests media imports with errors.
  */
-function testImportWithErrors(callback) {
+export function testImportWithErrors(callback) {
   // Quieten the logger just in this test, since we expect errors.
   // Elsewhere, it's better for errors to be seen by test authors.
-  importer.setupTestLogger().quiet();
+  importerTest.setupTestLogger().quiet();
 
   const media = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
@@ -516,8 +534,7 @@
   // Simulate an error after 3 imports.
   let copyCount = 0;
   importTask.addObserver(updateType => {
-    if (updateType ===
-        importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
+    if (updateType === mediaImport.UpdateType.ENTRY_CHANGED) {
       copyCount++;
       if (copyCount === 3) {
         mockCopier.simulateOneError();
diff --git a/ui/file_manager/file_manager/background/js/media_scanner.js b/ui/file_manager/file_manager/background/js/media_scanner.js
index 18b3205..51db1f1a 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner.js
@@ -2,32 +2,43 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {assert} from 'chrome://resources/js/assert.m.js';
+// #import {metadataProxy} from './metadata_proxy.m.js';
+// #import {fileOperationUtil} from './file_operation_util.m.js';
+// #import {importer} from '../../common/js/importer_common.m.js';
+// #import {mediaScannerInterfaces} from '../../../externs/background/media_scanner.m.js';
+// clang-format on
+
+// Namespace
+/* #export */ const mediaScanner = {};
+
 /**
  * Recursively scans through a list of given files and directories, and creates
  * a list of media files.
  *
- * @implements {importer.MediaScanner}
+ * @implements {mediaScannerInterfaces.MediaScanner}
  */
-importer.DefaultMediaScanner = class {
+mediaScanner.DefaultMediaScanner = class {
   /**
    * @param {function(!FileEntry): !Promise<string>} hashGenerator
    * @param {function(!FileEntry, !importer.Destination,
    *                  !importer.ScanMode):
    *     !Promise<!importer.Disposition>} dispositionChecker
-   * @param {!importer.DirectoryWatcherFactory} watcherFactory
+   * @param {!mediaScanner.DirectoryWatcherFactory} watcherFactory
    */
   constructor(hashGenerator, dispositionChecker, watcherFactory) {
     /**
      * A little factory for DefaultScanResults which allows us to forgo
      * the saving it's dependencies in our fields.
      * @param {importer.ScanMode} mode Mode of the scan to find new files.
-     * @return {!importer.DefaultScanResult}
+     * @return {!mediaScanner.DefaultScanResult}
      */
     this.createScanResult_ = mode => {
-      return new importer.DefaultScanResult(mode, hashGenerator);
+      return new mediaScanner.DefaultScanResult(mode, hashGenerator);
     };
 
-    /** @private {!Array<!importer.ScanObserver>} */
+    /** @private {!Array<!mediaScannerInterfaces.ScanObserver>} */
     this.observers_ = [];
 
     /**
@@ -39,7 +50,7 @@
     this.getDisposition_ = dispositionChecker;
 
     /**
-     * @private {!importer.DirectoryWatcherFactory}
+     * @private {!mediaScanner.DirectoryWatcherFactory}
      * @const
      */
     this.watcherFactory_ = watcherFactory;
@@ -67,7 +78,7 @@
     console.info(scan.name + ': Scanning directory ' + directory.fullPath);
 
     const watcher = this.watcherFactory_(
-        /** @this {importer.DefaultMediaScanner} */
+        /** @this {mediaScanner.DefaultMediaScanner} */
         () => {
           scan.cancel();
           this.notify_(importer.ScanEvent.INVALIDATED, scan);
@@ -119,7 +130,7 @@
 
 
   /**
-   * @param {!importer.DefaultScanResult} scan
+   * @param {!mediaScanner.DefaultScanResult} scan
    * @param  {!Array<!FileEntry>} entries
    * @return {!Promise} Resolves when scanning is finished normally
    *     or canceled.
@@ -143,7 +154,7 @@
       }
 
       // the second arg to slice is an exclusive end index, so we +1 batch size.
-      const end = begin + importer.DefaultMediaScanner.SCAN_BATCH_SIZE;
+      const end = begin + mediaScanner.DefaultMediaScanner.SCAN_BATCH_SIZE;
       console.log(scan.name + ': Processing batch ' + begin + '-' + (end - 1));
       const batch = entries.slice(begin, end);
 
@@ -161,12 +172,12 @@
    * Notifies all listeners at some point in the near future.
    *
    * @param {!importer.ScanEvent} event
-   * @param {!importer.DefaultScanResult} result
+   * @param {!mediaScanner.DefaultScanResult} result
    * @private
    */
   notify_(event, result) {
     this.observers_.forEach(
-        /** @param {!importer.ScanObserver} observer */
+        /** @param {!mediaScannerInterfaces.ScanObserver} observer */
         observer => {
           observer(event, result);
         });
@@ -177,7 +188,7 @@
    * watchers for each encountered directory.
    *
    * @param {!DirectoryEntry} directory
-   * @param {!importer.DirectoryWatcher} watcher
+   * @param {!mediaScanner.DirectoryWatcher} watcher
    * @return {!Promise<!Array<!FileEntry>>}
    * @private
    */
@@ -210,7 +221,7 @@
   /**
    * Finds all files beneath directory.
    *
-   * @param {!importer.DefaultScanResult} scan
+   * @param {!mediaScanner.DefaultScanResult} scan
    * @param {!FileEntry} entry
    * @return {!Promise}
    * @private
@@ -234,7 +245,7 @@
   /**
    * Adds a newly discovered file to the given scan result.
    *
-   * @param {!importer.DefaultScanResult} scan
+   * @param {!mediaScanner.DefaultScanResult} scan
    * @param {!FileEntry} entry
    * @return {!Promise}
    * @private
@@ -261,7 +272,7 @@
    * Adds a duplicate file to the given scan result.  This is to track the
    * number of duplicates that are being encountered.
    *
-   * @param {!importer.DefaultScanResult} scan
+   * @param {!mediaScanner.DefaultScanResult} scan
    * @param {!FileEntry} entry
    * @param {!importer.Disposition} disposition
    * @return {!Promise}
@@ -276,7 +287,7 @@
 };
 
 /** @const {number} */
-importer.DefaultMediaScanner.SCAN_BATCH_SIZE = 1;
+mediaScanner.DefaultMediaScanner.SCAN_BATCH_SIZE = 1;
 
 /**
  * Results of a scan operation. The object is "live" in that data can and
@@ -288,9 +299,9 @@
  * Note that classes implementing this should provide a read-only
  * {@code name} field.
  *
- * @implements {importer.ScanResult}
+ * @implements {mediaScannerInterfaces.ScanResult}
  */
-importer.DefaultScanResult = class {
+mediaScanner.DefaultScanResult = class {
   /**
    * @param {importer.ScanMode} mode The scan mode applied for finding new
    *     files.
@@ -365,7 +376,7 @@
      */
     this.canceled_ = false;
 
-    /** @private {!importer.Resolver.<!importer.ScanResult>} */
+    /** @private {!importer.Resolver.<!mediaScannerInterfaces.ScanResult>} */
     this.resolver_ = new importer.Resolver();
   }
 
@@ -520,7 +531,7 @@
  * Watcher for directories.
  * @interface
  */
-importer.DirectoryWatcher = class {
+mediaScanner.DirectoryWatcher = class {
   constructor() {
     /** @type {boolean} */
     this.triggered = false;
@@ -536,19 +547,19 @@
 /**
  * @typedef {function()}
  */
-importer.DirectoryWatcherFactoryCallback;
+mediaScanner.DirectoryWatcherFactoryCallback;
 
 /**
- * @typedef {function(importer.DirectoryWatcherFactoryCallback):
- *     !importer.DirectoryWatcher}
+ * @typedef {function(mediaScanner.DirectoryWatcherFactoryCallback):
+ *     !mediaScanner.DirectoryWatcher}
  */
-importer.DirectoryWatcherFactory;
+mediaScanner.DirectoryWatcherFactory;
 
 /**
  * Watcher for directories.
- * @implements {importer.DirectoryWatcher}
+ * @implements {mediaScanner.DirectoryWatcher}
  */
-importer.DefaultDirectoryWatcher = class {
+mediaScanner.DefaultDirectoryWatcher = class {
   /**
    * @param {function()} callback Callback to be invoked when one of watched
    *     directories is changed.
@@ -564,10 +575,10 @@
    * Creates new directory watcher.
    * @param {function()} callback Callback to be invoked when one of watched
    *     directories is changed.
-   * @return {!importer.DirectoryWatcher}
+   * @return {!mediaScanner.DirectoryWatcher}
    */
   static create(callback) {
-    return new importer.DefaultDirectoryWatcher(callback);
+    return new mediaScanner.DefaultDirectoryWatcher(callback);
   }
 
   /**
diff --git a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js b/ui/file_manager/file_manager/background/js/media_scanner_unittest.m.js
similarity index 88%
rename from ui/file_manager/file_manager/background/js/media_scanner_unittest.js
rename to ui/file_manager/file_manager/background/js/media_scanner_unittest.m.js
index 98c2c3c..8c21b90 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner_unittest.m.js
@@ -2,6 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {assertEquals, assertFalse, assertThrows} from 'chrome://test/chai_assert.js';
+
+import {reportPromise} from '../../../base/js/test_error_reporting.m.js';
+import {mediaScannerInterfaces} from '../../../externs/background/media_scanner.m.js';
+import {importer} from '../../common/js/importer_common.m.js';
+import {assertFileEntryPathsEqual} from '../../common/js/unittest_util.m.js';
+
+import {fileOperationUtil} from './file_operation_util.m.js';
+import {mediaScanner} from './media_scanner.m.js';
+import {TestDirectoryWatcher} from './mock_media_scanner.m.js';
+import {importerTestHistory} from './test_import_history.m.js';
+
 /**
  * Stub out the metrics package.
  * @type {!Object<!string, !Function>}
@@ -11,7 +23,7 @@
   recordValue: function() {}
 };
 
-/** @type {!importer.DefaultMediaScanner} */
+/** @type {!mediaScanner.DefaultMediaScanner} */
 let scanner;
 
 /**
@@ -32,7 +44,7 @@
 let dispositionChecker;
 
 // Set up the test components.
-function setUp() {
+export function setUp() {
   importHistory = new importerTestHistory.TestImportHistory();
 
   // Setup a default disposition checker. Tests can replace it at runtime
@@ -41,7 +53,7 @@
     return Promise.resolve(importer.Disposition.ORIGINAL);
   };
 
-  scanner = new importer.DefaultMediaScanner(
+  scanner = new mediaScanner.DefaultMediaScanner(
       /** @param {!FileEntry} entry */
       entry => {
         return Promise.resolve(entry.name);
@@ -58,13 +70,13 @@
 /**
  * Verifies that scanning an empty filesystem produces an empty list.
  */
-function testEmptySourceList() {
+export function testEmptySourceList() {
   assertThrows(() => {
     scanner.scanFiles([], scanMode);
   });
 }
 
-function testIsScanning(callback) {
+export function testIsScanning(callback) {
   const filenames = [
     'happy',
     'thoughts',
@@ -84,7 +96,7 @@
       callback);
 }
 
-function testObserverNotifiedOnScanFinish(callback) {
+export function testObserverNotifiedOnScanFinish(callback) {
   const filenames = [
     'happy',
     'thoughts',
@@ -116,7 +128,7 @@
 /**
  * Verifies that scanFiles slurps up all specified files.
  */
-function testScanFiles(callback) {
+export function testScanFiles(callback) {
   const filenames = [
     'foo',
     'foo.jpg',
@@ -144,7 +156,7 @@
 /**
  * Verifies that scanFiles skips duplicated files.
  */
-function testScanFilesIgnoresPreviousImports(callback) {
+export function testScanFilesIgnoresPreviousImports(callback) {
   const filenames = [
     'oldimage1234.jpg',    // a history duplicate
     'driveimage1234.jpg',  // a content duplicate
@@ -186,7 +198,7 @@
 /**
  * Verifies that scanning a simple single-level directory structure works.
  */
-function testEmptyScanResults(callback) {
+export function testEmptyScanResults(callback) {
   const filenames = [
     'happy',
     'thoughts',
@@ -209,7 +221,7 @@
 /**
  * Verifies that scanning a simple single-level directory structure works.
  */
-function testSingleLevel(callback) {
+export function testSingleLevel(callback) {
   const filenames = [
     'foo',
     'foo.jpg',
@@ -242,7 +254,7 @@
  * Verifies that scanning a simple single-level directory produces 100%
  * progress at completion.
  */
-function testProgress(callback) {
+export function testProgress(callback) {
   const filenames = [
     'foo',
     'foo.jpg',
@@ -274,7 +286,7 @@
 /**
  * Verifies that scanning ignores previously imported entries.
  */
-function testIgnoresPreviousImports(callback) {
+export function testIgnoresPreviousImports(callback) {
   importHistory.importedPaths['/testIgnoresPreviousImports/oldimage1234.jpg'] =
       [importer.Destination.GOOGLE_DRIVE];
   const filenames = [
@@ -319,7 +331,7 @@
   reportPromise(promise, callback);
 }
 
-function testTracksDuplicates(callback) {
+export function testTracksDuplicates(callback) {
   importHistory.importedPaths['/testTracksDuplicates/oldimage1234.jpg'] =
       [importer.Destination.GOOGLE_DRIVE];
   const filenames = [
@@ -366,7 +378,7 @@
   reportPromise(promise, callback);
 }
 
-function testMultiLevel(callback) {
+export function testMultiLevel(callback) {
   const filenames = [
     'foo.jpg', 'bar',
     [
@@ -404,7 +416,7 @@
       callback);
 }
 
-function testDedupesFilesInScanResult(callback) {
+export function testDedupesFilesInScanResult(callback) {
   const filenames = [
     'foo.jpg', 'bar.jpg',
     [
@@ -446,11 +458,11 @@
 /**
  * Verifies that scanning a simple single-level directory structure works.
  */
-function testDefaultScanResult() {
+export function testDefaultScanResult() {
   const hashGenerator = file => {
     return file.toURL();
   };
-  const scan = new importer.DefaultScanResult(scanMode, hashGenerator);
+  const scan = new mediaScanner.DefaultScanResult(scanMode, hashGenerator);
 
   // 0 before we set candidate count
   assertProgress(0, scan);
@@ -466,7 +478,7 @@
   assertProgress(100, scan);
 }
 
-function testInvalidation(callback) {
+export function testInvalidation(callback) {
   const invalidatePromise = new Promise(fulfill => {
     scanner.addObserver(fulfill);
   });
@@ -489,8 +501,8 @@
 /**
  * Verifies the results of the media scan are as expected.
  * @param {number} expected, 0-100
- * @param {!importer.ScanResult} scan
- * @return {!importer.ScanResult}
+ * @param {!mediaScannerInterfaces.ScanResult} scan
+ * @return {!mediaScannerInterfaces.ScanResult}
  */
 function assertProgress(expected, scan) {
   assertEquals(expected, scan.getStatistics().progress);
@@ -500,8 +512,8 @@
 /**
  * Verifies the results of the media scan are as expected.
  * @param {!Array<string>} expected
- * @param {!importer.ScanResult} scan
- * @return {!importer.ScanResult}
+ * @param {!mediaScannerInterfaces.ScanResult} scan
+ * @return {!mediaScannerInterfaces.ScanResult}
  */
 function assertFilesFound(expected, scan) {
   assertFileEntryPathsEqual(expected, scan.getFileEntries());
@@ -512,8 +524,8 @@
 /**
  * Verifies the results of the media scan are as expected.
  * @param {!Array<string>} expected
- * @param {!importer.ScanResult} scan
- * @return {!importer.ScanResult}
+ * @param {!mediaScannerInterfaces.ScanResult} scan
+ * @return {!mediaScannerInterfaces.ScanResult}
  */
 function assertDuplicatesFound(expected, scan) {
   assertFileEntryPathsEqual(expected, scan.getDuplicateFileEntries());
diff --git a/ui/file_manager/file_manager/background/js/mock_media_scanner.js b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
index ea9d677..ea9b14f 100644
--- a/ui/file_manager/file_manager/background/js/mock_media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
@@ -2,14 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {assertTrue, assertEquals} from 'chrome://test/chai_assert.js';
+// #import {importer} from '../../common/js/importer_common.m.js';
+// #import {mediaScannerInterfaces} from '../../../externs/background/media_scanner.m.js';
+// #import {mediaScanner} from './media_scanner.m.js';
+// clang-format on
+
 /**
- * importer.MediaScanner and importer.ScanResult test double.
+ * mediaScannerInterfaces.MediaScanner and mediaScannerInterfaces.ScanResult
+ * test double.
  *
- * @implements {importer.MediaScanner}
+ * @implements {mediaScannerInterfaces.MediaScanner}
  */
-class TestMediaScanner {
+/* #export */ class TestMediaScanner {
   constructor() {
-    /** @private {!Array<!importer.ScanResult>} */
+    /** @private {!Array<!mediaScannerInterfaces.ScanResult>} */
     this.scans_ = [];
 
     /**
@@ -26,7 +34,7 @@
 
     /**
      * List of scan observers.
-     * @private {!Array<!importer.ScanObserver>}
+     * @private {!Array<!mediaScannerInterfaces.ScanObserver>}
      */
     this.observers = [];
 
@@ -90,7 +98,7 @@
 
   /**
    * Notifies observers that a scan has finished.
-   * @param {!importer.ScanResult} scan
+   * @param {!mediaScannerInterfaces.ScanResult} scan
    */
   finalize(scan) {
     // Note the |scan| has {!TestScanResult} type in test, and needs a
@@ -120,11 +128,12 @@
 }
 
 /**
- * importer.MediaScanner and importer.ScanResult test double.
+ * mediaScannerInterfaces.MediaScanner and mediaScannerInterfaces.ScanResult
+ * test double.
  *
- * @implements {importer.ScanResult}
+ * @implements {mediaScannerInterfaces.ScanResult}
  */
-class TestScanResult {
+/* #export */ class TestScanResult {
   /**
    * @param {!Array<!FileEntry>} fileEntries
    */
@@ -162,7 +171,7 @@
     /** @private {boolean} */
     this.canceled_ = false;
 
-    /** @type {!Promise<!importer.ScanResult>} */
+    /** @type {!Promise<!mediaScannerInterfaces.ScanResult>} */
     this.whenFinal_ = new Promise((resolve, reject) => {
       this.resolveResult_ = result => {
         this.settled_ = true;
@@ -226,7 +235,7 @@
     duplicates[importer.Disposition.CONTENT_DUPLICATE] = 0;
     duplicates[importer.Disposition.HISTORY_DUPLICATE] = 0;
     duplicates[importer.Disposition.SCAN_DUPLICATE] = 0;
-    return /** @type {importer.ScanResult.Statistics} */ ({
+    return /** @type {mediaScannerInterfaces.ScanResult.Statistics} */ ({
       scanDuration: this.scanDuration,
       newFileCount: this.fileEntries.length,
       duplicates: duplicates,
@@ -243,9 +252,9 @@
 TestScanResult.lastId_ = 0;
 
 /**
- * @implements {importer.DirectoryWatcher}
+ * @implements {mediaScanner.DirectoryWatcher}
  */
-class TestDirectoryWatcher {
+/* #export */ class TestDirectoryWatcher {
   constructor(callback) {
     /**
      * @public {function()}
diff --git a/ui/file_manager/file_manager/common/js/importer_common_unittest.m.js b/ui/file_manager/file_manager/common/js/importer_common_unittest.m.js
index 6ffe1e6e..b38530cd 100644
--- a/ui/file_manager/file_manager/common/js/importer_common_unittest.m.js
+++ b/ui/file_manager/file_manager/common/js/importer_common_unittest.m.js
@@ -10,8 +10,9 @@
 import {VolumeInfo} from '../../../externs/volume_info.m.js';
 import {MockVolumeManager} from '../../background/js/mock_volume_manager.m.js';
 
+import {importer} from './importer_common.m.js';
 import {MockDirectoryEntry, MockFileEntry} from './mock_entry.m.js';
-import {importer} from './test_importer_common.m.js';
+import {importerTest} from './test_importer_common.m.js';
 
 /** @type {!MockVolumeManager} */
 let volumeManager;
@@ -42,7 +43,7 @@
   window.loadTimeData.getString = id => id;
   new MockCommandLinePrivate();
   new MockChromeStorageAPI();
-  importer.setupTestLogger();
+  importerTest.setupTestLogger();
 
   cameraVolume = MockVolumeManager.createMockVolumeInfo(
       VolumeManagerCommon.VolumeType.MTP, 'camera-fs', 'Some Camera');
diff --git a/ui/file_manager/file_manager/common/js/test_importer_common.js b/ui/file_manager/file_manager/common/js/test_importer_common.js
index 9ac429d..384bc21 100644
--- a/ui/file_manager/file_manager/common/js/test_importer_common.js
+++ b/ui/file_manager/file_manager/common/js/test_importer_common.js
@@ -2,26 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @fileoverview
- * @suppress {uselessCode} Temporary suppress because of the line exporting.
- */
-
 // #import {importer} from './importer_common.m.js';
 // #import {TestCallRecorder} from './unittest_util.m.js';
 
-// Shared cloud importer namespace
-// eslint-disable-next-line no-var
-/* #ignore */ var importer = importer || {};
+// Namespace
+/* #export */ const importerTest = {};
 
 /**
  * Sets up a logger for use in unit tests.  The test logger doesn't attempt to
  * access chrome's sync file system.  Call this during setUp.
- * @return {!importer.TestLogger}
+ * @return {!importerTest.TestLogger}
  * @suppress{accessControls} For testing.
  */
-importer.setupTestLogger = () => {
-  const logger = new importer.TestLogger();
+importerTest.setupTestLogger = () => {
+  const logger = new importerTest.TestLogger();
   importer.logger_ = logger;
   return logger;
 };
@@ -32,7 +26,7 @@
  * @implements {importer.Logger}
  * @final
  */
-importer.TestLogger = class {
+importerTest.TestLogger = class {
   constructor() {
     /** @public {!TestCallRecorder} */
     this.errorRecorder = new TestCallRecorder();
@@ -77,6 +71,3 @@
     };
   }
 };
-
-// eslint-disable-next-line semi,no-extra-semi
-/* #export */ {importer};
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 676d7e4..5233d72 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -103,7 +103,6 @@
     "//ui/file_manager/externs/background/background_base.js",
     "//ui/file_manager/externs/background/file_browser_background_full.js",
     "//ui/file_manager/externs/background/file_operation_manager.js",
-    "//ui/file_manager/externs/background/import_runner.js",
     "//ui/file_manager/externs/background/media_import_handler.js",
     "//ui/file_manager/externs/background/task_queue.js",
     "//ui/file_manager/externs/background/duplicate_finder.js",
@@ -576,7 +575,6 @@
     "//ui/file_manager/file_manager/common/js:importer_common",
   ]
   externs_list = [
-    "//ui/file_manager/externs/background/import_runner.js",
     "//ui/file_manager/externs/background/media_import_handler.js",
     "//ui/file_manager/externs/background/duplicate_finder.js",
     "//ui/file_manager/externs/background/task_queue.js",
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 4c2308a..b8d13a2 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -50,13 +50,13 @@
      */
     this.onHistoryChangedBound_ = this.onHistoryChanged_.bind(this);
 
-    /** @private {?importer.MediaScanner} */
+    /** @private {?mediaScannerInterfaces.MediaScanner} */
     this.mediaScanner_ = null;
 
     /** @private {?importer.ImportController} */
     this.importController_ = null;
 
-    /** @private {?importer.ImportRunner} */
+    /** @private {?mediaImportInterfaces.ImportRunner} */
     this.mediaImportHandler_ = null;
 
     /** @private {?MetadataModel} */
@@ -487,7 +487,7 @@
   }
 
   /**
-   * @return {importer.ImportRunner}
+   * @return {mediaImportInterfaces.ImportRunner}
    */
   get mediaImportHandler() {
     return this.mediaImportHandler_;
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 30383cc..8f0f8b4 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -26,8 +26,8 @@
    * @param {!importer.ControllerEnvironment} environment The class providing
    *     access to runtime environmental information, like the current
    * directory, volume lookup and so-on.
-   * @param {!importer.MediaScanner} scanner
-   * @param {!importer.ImportRunner} importRunner
+   * @param {!mediaScannerInterfaces.MediaScanner} scanner
+   * @param {!mediaImportInterfaces.ImportRunner} importRunner
    * @param {!importer.CommandWidget} commandWidget
    */
   constructor(environment, scanner, importRunner, commandWidget) {
@@ -37,10 +37,10 @@
     /** @private @const {!importer.ChromeLocalStorage} */
     this.storage_ = importer.ChromeLocalStorage.getInstance();
 
-    /** @private @const {!importer.ImportRunner} */
+    /** @private @const {!mediaImportInterfaces.ImportRunner} */
     this.importRunner_ = importRunner;
 
-    /** @private @const {!importer.MediaScanner} */
+    /** @private @const {!mediaScannerInterfaces.MediaScanner} */
     this.scanner_ = scanner;
 
     /** @private @const {!importer.CommandWidget} */
@@ -101,7 +101,7 @@
 
   /**
    * @param {!importer.ScanEvent} event Command event.
-   * @param {importer.ScanResult} scan
+   * @param {mediaScannerInterfaces.ScanResult} scan
    * @private
    */
   onScanEvent_(event, scan) {
@@ -181,7 +181,7 @@
   }
 
   /**
-   * @param {!importer.MediaImportHandler.ImportTask} task
+   * @param {!mediaImportInterfaces.MediaImportHandler.ImportTask} task
    * @private
    */
   onImportFinished_(task) {
@@ -305,7 +305,7 @@
 
   /**
    * Checks the environment and updates UI as needed.
-   * @param {importer.ScanResult=} opt_scan If supplied,
+   * @param {mediaScannerInterfaces.ScanResult=} opt_scan If supplied,
    * @private
    */
   checkState_(opt_scan) {
@@ -414,7 +414,7 @@
 
   /**
    * @param {importer.ActivityState} activityState
-   * @param {importer.ScanResult=} opt_scan
+   * @param {mediaScannerInterfaces.ScanResult=} opt_scan
    * @param {number=} opt_destinationSizeBytes specifies the destination size in
    *     bytes in case of space issues.
    * @private
@@ -439,7 +439,7 @@
    * Attempts to scan the current context.
    *
    * @param {importer.ScanMode} mode How to detect new files.
-   * @return {importer.ScanResult} A scan object,
+   * @return {mediaScannerInterfaces.ScanResult} A scan object,
    *     or null if scan is not possible in current context.
    * @private
    */
@@ -462,8 +462,8 @@
  * Collection of import task related details.
  *
  * @typedef {{
- *   scan: !importer.ScanResult,
- *   task: !importer.MediaImportHandler.ImportTask,
+ *   scan: !mediaScannerInterfaces.ScanResult,
+ *   task: !mediaImportInterfaces.MediaImportHandler.ImportTask,
  *   started: !Date
  * }}
  *
@@ -488,7 +488,7 @@
 
   /**
    * @param {importer.ActivityState} activityState
-   * @param {importer.ScanResult=} opt_scan
+   * @param {mediaScannerInterfaces.ScanResult=} opt_scan
    * @param {number=} opt_destinationSizeBytes specifies the destination size in
    *     bytes in case of space issues.
    */
@@ -919,24 +919,24 @@
 importer.ScanManager = class {
   /**
    * @param {!importer.ControllerEnvironment} environment
-   * @param {!importer.MediaScanner} scanner
+   * @param {!mediaScannerInterfaces.MediaScanner} scanner
    */
   constructor(environment, scanner) {
     /** @private {!importer.ControllerEnvironment} */
     this.environment_ = environment;
 
-    /** @private @const {!importer.MediaScanner} */
+    /** @private @const {!mediaScannerInterfaces.MediaScanner} */
     this.scanner_ = scanner;
 
     /**
      * The active files scan, if any.
-     * @private {?importer.ScanResult}
+     * @private {?mediaScannerInterfaces.ScanResult}
      */
     this.selectionScan_ = null;
 
     /**
      * The active directory scan, if any.
-     * @private {?importer.ScanResult}
+     * @private {?mediaScannerInterfaces.ScanResult}
      */
     this.directoryScan_ = null;
   }
@@ -968,13 +968,16 @@
     this.directoryScan_ = null;
   }
 
-  /** @return {importer.ScanResult} Current active scan, or null if none. */
+  /**
+   * @return {mediaScannerInterfaces.ScanResult} Current active scan, or null
+   *     if none.
+   */
   getActiveScan() {
     return this.selectionScan_ || this.directoryScan_;
   }
 
   /**
-   * @param {importer.ScanResult} scan
+   * @param {mediaScannerInterfaces.ScanResult} scan
    * @return {boolean} True if scan is the active scan...meaning the current
    *     selection scan or the scan for the current directory.
    */
@@ -989,7 +992,7 @@
    * @param {!Array<!FileEntry>} entries
    * @param {!importer.ScanMode} mode
    *
-   * @return {!importer.ScanResult}
+   * @return {!mediaScannerInterfaces.ScanResult}
    */
   getSelectionScan(entries, mode) {
     console.assert(
@@ -1003,7 +1006,7 @@
    * Returns a scan for the directory.
    *
    * @param {!importer.ScanMode} mode
-   * @return {importer.ScanResult}
+   * @return {mediaScannerInterfaces.ScanResult}
    */
   getDirectoryScan(mode) {
     if (!this.directoryScan_) {
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
index beb18fa..76d5dca1 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
@@ -9,7 +9,7 @@
 let volumeManager;
 
 /** @type {!TestMediaScanner} */
-let mediaScanner;
+let mediaScannerTest;
 
 /** @type {!TestImportRunner} */
 let mediaImporter;
@@ -61,7 +61,7 @@
   assert(downloads);
   destinationVolume = downloads;
 
-  mediaScanner = new TestMediaScanner();
+  mediaScannerTest = new TestMediaScanner();
   mediaImporter = new TestImportRunner();
 }
 
@@ -116,7 +116,7 @@
                         return widget.updateResolver.promise;
                       })
                       .then(() => {
-                        mediaScanner.assertScanCount(2);
+                        mediaScannerTest.assertScanCount(2);
                       });
 
   reportPromise(promise, callback);
@@ -160,8 +160,8 @@
                         environment.directoryChangedListener(EMPTY_EVENT);
                       })
                       .then(() => {
-                        mediaScanner.assertScanCount(1);
-                        mediaScanner.assertLastScanCanceled();
+                        mediaScannerTest.assertScanCount(1);
+                        mediaScannerTest.assertLastScanCanceled();
                       });
 
   reportPromise(promise, callback);
@@ -190,8 +190,8 @@
                         environment.windowCloseListener();
                       })
                       .then(() => {
-                        mediaScanner.assertScanCount(1);
-                        mediaScanner.assertLastScanCanceled();
+                        mediaScannerTest.assertScanCount(1);
+                        mediaScannerTest.assertLastScanCanceled();
                       });
 
   reportPromise(promise, callback);
@@ -213,7 +213,7 @@
 
   // Ensure there is some content in the scan so the code that depends
   // on this state doesn't croak which it finds it missing.
-  mediaScanner.fileEntries.push(MockFileEntry.create(
+  mediaScannerTest.fileEntries.push(MockFileEntry.create(
       fileSystem, '/DCIM/photos0/IMG00001.jpg', getDefaultMetadata()));
 
   // Make controller enter a scanning state.
@@ -225,7 +225,7 @@
                         // "scanning..."
                         assertFalse(widget.detailsVisible);
                         widget.resetPromises();
-                        mediaScanner.finalizeScans();
+                        mediaScannerTest.finalizeScans();
                         return widget.updateResolver.promise;
                       })
                       .then(() => {
@@ -276,7 +276,7 @@
       fileSystem, '/DCIM/photos0/IMG00001.jpg', getDefaultMetadata()));
 
   environment.selectionChangedListener();
-  mediaScanner.finalizeScans();
+  mediaScannerTest.finalizeScans();
   reportPromise(widget.updateResolver.promise, callback);
 }
 
@@ -294,12 +294,12 @@
 
   // Ensure there is some content in the scan so the code that depends
   // on this state doesn't croak which it finds it missing.
-  mediaScanner.fileEntries.push(MockFileEntry.create(
+  mediaScannerTest.fileEntries.push(MockFileEntry.create(
       fileSystem, '/DCIM/photos0/IMG00001.jpg', getDefaultMetadata()));
 
   environment.directoryChangedListener(EMPTY_EVENT);  // initiates a scan.
   widget.resetPromises();
-  mediaScanner.finalizeScans();
+  mediaScannerTest.finalizeScans();
 
   reportPromise(widget.updateResolver.promise, callback);
 }
@@ -345,7 +345,7 @@
 
   // Ensure there is some content in the scan so the code that depends
   // on this state doesn't croak which it finds it missing.
-  mediaScanner.fileEntries.push(MockFileEntry.create(
+  mediaScannerTest.fileEntries.push(MockFileEntry.create(
       fileSystem, '/DCIM/photos0/IMG00001.jpg', getDefaultMetadata()));
 
   // First we need to force the controller into a scanning state.
@@ -353,7 +353,7 @@
 
   return widget.updateResolver.promise.then(() => {
     widget.resetPromises();
-    mediaScanner.finalizeScans();
+    mediaScannerTest.finalizeScans();
     return widget.updateResolver.promise.then(() => {
       widget.resetPromises();
       widget.click(clickSource);
@@ -368,12 +368,12 @@
  */
 class TestImportTask {
   /**
-   * @param {!importer.ScanResult} scan
+   * @param {!mediaScannerInterfaces.ScanResult} scan
    * @param {!importer.Destination} destination
    * @param {!Promise<!DirectoryEntry>} destinationDirectory
    */
   constructor(scan, destination, destinationDirectory) {
-    /** @public {!importer.ScanResult} */
+    /** @public {!mediaScannerInterfaces.ScanResult} */
     this.scan = scan;
 
     /** @type {!importer.Destination} */
@@ -407,11 +407,11 @@
 /**
  * Test import runner.
  *
- * @implements {importer.ImportRunner}
+ * @implements {mediaImportInterfaces.ImportRunner}
  */
 class TestImportRunner {
   constructor() {
-    /** @public {!Array<!importer.ScanResult>} */
+    /** @public {!Array<!mediaScannerInterfaces.ScanResult>} */
     this.imported = [];
 
     /**
@@ -434,13 +434,14 @@
   }
 
   /**
-   * Returns |task| as importer.MediaImportHandler.ImportTask type.
+   * Returns |task| as mediaImportInterfaces.MediaImportHandler.ImportTask type.
    * @param {!Object} task
-   * @return {!importer.MediaImportHandler.ImportTask}
+   * @return {!mediaImportInterfaces.MediaImportHandler.ImportTask}
    * @private
    */
   toMediaImportTask_(task) {
-    return /** @type {!importer.MediaImportHandler.ImportTask} */ (task);
+    return /** @type {!mediaImportInterfaces.MediaImportHandler.ImportTask} */ (
+        task);
   }
 
   finishImportTasks() {
@@ -664,7 +665,7 @@
       sourceVolume.fileSystem.entries[currentDirectory]);
 
   return new importer.ImportController(
-      environment, mediaScanner, mediaImporter, widget);
+      environment, mediaScannerTest, mediaImporter, widget);
 }
 
 /**
diff --git a/ui/file_manager/video_player/background.html b/ui/file_manager/video_player/background.html
index 1f5034b..39b8e67 100644
--- a/ui/file_manager/video_player/background.html
+++ b/ui/file_manager/video_player/background.html
@@ -1,10 +1,4 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/cr/event_target.js"></script>
-<script src="chrome://resources/js/assert.js"></script>
-<script src="chrome://resources/js/cr/ui/array_data_model.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/background_common_scripts.js"></script>
-<script src="js/background_scripts.js"></script>
+<script type="module" src="js/main_background.m.js"></script>
diff --git a/ui/file_manager/video_player/js/BUILD.gn b/ui/file_manager/video_player/js/BUILD.gn
index 68045a35..a4698e6 100644
--- a/ui/file_manager/video_player/js/BUILD.gn
+++ b/ui/file_manager/video_player/js/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/browser/resources/optimize_webui.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//third_party/closure_compiler/js_unit_tests.gni")
 import("//ui/file_manager/base/gn/js_test_gen_html.gni")
@@ -202,3 +203,23 @@
     "video_player.js",
   ]
 }
+
+preprocess_folder =
+    rebase_path("$target_gen_dir/../../preprocessed/video_player/js",
+                root_build_dir)
+
+optimize_webui("build_background") {
+  host = "video_player"
+
+  input = preprocess_folder
+  js_module_in_files = [ "main_background.m.js" ]
+
+  js_out_files = [ "main_background.m.rollup.js" ]
+
+  deps = [
+    ":main_background.m",
+    "//ui/file_manager:preprocess_generated",
+    "//ui/file_manager:preprocess_static",
+    "//ui/webui/resources:preprocess",
+  ]
+}
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn
index 190efd1..13edc73 100644
--- a/ui/webui/resources/js/BUILD.gn
+++ b/ui/webui/resources/js/BUILD.gn
@@ -70,6 +70,13 @@
       "web_ui_listener_behavior.js",
     ]
   }
+
+  if (is_ios) {
+    in_files += [
+      "ios/mojo_api.js",
+      "ios/web_ui.js",
+    ]
+  }
 }
 
 preprocess_if_expr("preprocess_generated") {
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index ebb38c3..ab8cc7b 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -44,13 +44,6 @@
                    use_base_dir="false" />
       </if>
 
-      <if expr="is_ios">
-        <structure name="IDR_WEBUI_JS_IOS_WEB_UI"
-                   file="js/ios/web_ui.js" type="chrome_html" />
-        <structure name="IDR_WEBUI_JS_IOS_MOJO_API"
-                   file="js/ios/mojo_api.js" type="chrome_html" />
-      </if>
-
       <if expr="not is_android and not is_ios">
         <structure name="IDR_LOTTIE_LOTTIE_WORKER_MIN_JS"
                    file="../../../third_party/lottie/lottie_worker.min.js"
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
index 65eaa1c..c6ff6f0e 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/FullscreenCallbackTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.weblayer.test;
 
+import android.os.Build;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -12,6 +14,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.weblayer.Browser;
 import org.chromium.weblayer.BrowserControlsOffsetCallback;
@@ -111,6 +114,8 @@
 
     @Test
     @SmallTest
+    @DisableIf.
+    Build(sdk_is_less_than = Build.VERSION_CODES.M, message = "https://crbug.com/1159781")
     public void testTopViewRemainsHiddenOnFullscreenRotation() throws Exception {
         String url = mActivityTestRule.getTestDataURL("rotation2.html");
         mActivity = mActivityTestRule.launchShellWithUrl(url);
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index f457899b..28c3b85 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -437,7 +437,6 @@
   if (prerender_contents && prerender_contents->prerender_mode() !=
                                 prerender::mojom::PrerenderMode::kNoPrerender) {
     result.push_back(std::make_unique<prerender::PrerenderURLLoaderThrottle>(
-        prerender_contents->prerender_mode(),
         prerender::PrerenderHistograms::GetHistogramPrefix(
             prerender_contents->origin()),
         GetPrerenderCanceler(web_contents)));
diff --git a/weblayer/renderer/content_renderer_client_impl.cc b/weblayer/renderer/content_renderer_client_impl.cc
index 58e681d2..4cbac11 100644
--- a/weblayer/renderer/content_renderer_client_impl.cc
+++ b/weblayer/renderer/content_renderer_client_impl.cc
@@ -135,7 +135,6 @@
       // Avoid any race conditions from having the browser tell subframes that
       // they're prerendering.
       new prerender::PrerenderHelper(render_frame,
-                                     prerender_helper->prerender_mode(),
                                      prerender_helper->histogram_prefix());
     }
   }