diff --git a/DEPS b/DEPS
index 249851f6..e0235356 100644
--- a/DEPS
+++ b/DEPS
@@ -179,11 +179,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '6dee176c8d7d712a23d3841c26ff9e6738fdbe2b',
+  'angle_revision': '7f418fc2677a978c77f1a3aba4b163b7501ed960',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'a68a80a4dbf9dc7fab42a2734c854a41d23136cc',
+  'swiftshader_revision': '26c6c4a5eb838b67b2c8895406bb9fd07b69ac83',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -286,7 +286,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': 'e99b9182214fe7797803d89fa4d5f830d9a57bbb',
+  'spv_tools_revision': '3cdd6444cf91a5d74007eb0685885f0512066c4d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9fdb14b94056dfeeda68c3a9afbdece693ac9150',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'b582d244f3160b24d6b2edadc680fc0f03b7194d',
       'condition': 'checkout_linux',
   },
 
@@ -1280,7 +1280,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '45f9f6cebc3a5956cee84448fa90b499dc8a8bf4',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '87ede8895026c93bf0336f61d8cbb5954d42c19e',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '2701c130839edbeb226735b0775966b6423d9e83',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '7bf8699dd1ce209faaefa1c1176dc99c1252eef3',
+    Var('webrtc_git') + '/src.git' + '@' + '55d19e590d9a6ed6d8b3ad97b6ea62158e56705b',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1532,7 +1532,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@12fa2ef9c6d1da02a5c9e27974b428eca75a05c6',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1f4ca326d0db23bfe3e747bbc35f2c18adb4cffe',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/magnifier/docked_magnifier_controller_impl.cc b/ash/magnifier/docked_magnifier_controller_impl.cc
index cb5b7f7..68d0111 100644
--- a/ash/magnifier/docked_magnifier_controller_impl.cc
+++ b/ash/magnifier/docked_magnifier_controller_impl.cc
@@ -312,6 +312,10 @@
   viewport_magnifier_layer_->SetTransform(transform);
 }
 
+int DockedMagnifierControllerImpl::GetMagnifierHeightForTesting() const {
+  return GetTotalMagnifierHeight();
+}
+
 void DockedMagnifierControllerImpl::OnActiveUserPrefServiceChanged(
     PrefService* pref_service) {
   active_user_pref_service_ = pref_service;
diff --git a/ash/magnifier/docked_magnifier_controller_impl.h b/ash/magnifier/docked_magnifier_controller_impl.h
index 119b202f..cd5a78e 100644
--- a/ash/magnifier/docked_magnifier_controller_impl.h
+++ b/ash/magnifier/docked_magnifier_controller_impl.h
@@ -79,6 +79,7 @@
 
   // DockedMagnifierController:
   void CenterOnPoint(const gfx::Point& point_in_screen) override;
+  int GetMagnifierHeightForTesting() const override;
 
   // ash::SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
diff --git a/ash/public/cpp/docked_magnifier_controller.h b/ash/public/cpp/docked_magnifier_controller.h
index 88f9583..d3ae84c 100644
--- a/ash/public/cpp/docked_magnifier_controller.h
+++ b/ash/public/cpp/docked_magnifier_controller.h
@@ -26,6 +26,9 @@
   // by itself.
   virtual void CenterOnPoint(const gfx::Point& point_in_screen) = 0;
 
+  // Returns docked magnifier height.
+  virtual int GetMagnifierHeightForTesting() const = 0;
+
  protected:
   virtual ~DockedMagnifierController() = default;
 };
diff --git a/ash/shelf/shelf_application_menu_model.cc b/ash/shelf/shelf_application_menu_model.cc
index 91902d7a4..7219335 100644
--- a/ash/shelf/shelf_application_menu_model.cc
+++ b/ash/shelf/shelf_application_menu_model.cc
@@ -20,7 +20,7 @@
     Items items,
     ShelfItemDelegate* delegate)
     : ui::SimpleMenuModel(this), delegate_(delegate) {
-  AddTitle(title);
+  AddItem(std::numeric_limits<int>::max(), title);
   for (size_t i = 0; i < items.size(); i++)
     AddItemWithIcon(i, items[i].first, items[i].second);
   AddSeparator(ui::SPACING_SEPARATOR);
diff --git a/ash/shelf/shelf_application_menu_model_unittest.cc b/ash/shelf/shelf_application_menu_model_unittest.cc
index dc49e43..2436ce8 100644
--- a/ash/shelf/shelf_application_menu_model_unittest.cc
+++ b/ash/shelf/shelf_application_menu_model_unittest.cc
@@ -51,7 +51,7 @@
   ShelfApplicationMenuModel menu(title, {}, nullptr);
   // Expect the title and a separator.
   ASSERT_EQ(2, menu.GetItemCount());
-  EXPECT_EQ(ui::MenuModel::TYPE_TITLE, menu.GetTypeAt(0));
+  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
   EXPECT_EQ(title, menu.GetLabelAt(0));
   EXPECT_FALSE(menu.IsEnabledAt(0));
   EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, menu.GetTypeAt(1));
@@ -75,7 +75,7 @@
   ASSERT_EQ(static_cast<int>(5), menu.GetItemCount());
 
   // The label title should not be enabled.
-  EXPECT_EQ(ui::MenuModel::TYPE_TITLE, menu.GetTypeAt(0));
+  EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu.GetTypeAt(0));
   EXPECT_EQ(title, menu.GetLabelAt(0));
   EXPECT_FALSE(menu.IsEnabledAt(0));
 
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index d28e37f1..e6db5922 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -1698,7 +1698,7 @@
             overflow_shelf_view->GetPreferredSize().width());
 }
 
-TEST_F(ShelfViewTest, OverflowShelfColorIsDerivedFromWallpaper) {
+TEST_F(ShelfViewTest, DISABLED_OverflowShelfColorIsDerivedFromWallpaper) {
   // No overflow bubble when scrollable shelf enabled.
   // TODO(https://crbug.com/1002576): revisit when scrollable shelf is launched.
   if (chromeos::switches::ShouldShowScrollableShelf())
@@ -1905,7 +1905,7 @@
 
 // Checks various drag and drop operations from OverflowBubble to Shelf, and
 // vice versa.
-TEST_F(ShelfViewTest, CheckDragAndDropFromShelfToOtherShelf) {
+TEST_F(ShelfViewTest, DISABLED_CheckDragAndDropFromShelfToOtherShelf) {
   // No overflow bubble when scrollable shelf enabled.
   // TODO(https://crbug.com/1002576): revisit when scrollable shelf is launched.
   if (chromeos::switches::ShouldShowScrollableShelf())
diff --git a/base/allocator/allocator_shim_default_dispatch_to_glibc.cc b/base/allocator/allocator_shim_default_dispatch_to_glibc.cc
index aa19b7a..8915606 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_glibc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_glibc.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/allocator/allocator_shim.h"
+#include "base/memory/protected_memory_cfi.h"
 
 #include <dlfcn.h>
 #include <malloc.h>
@@ -22,6 +23,10 @@
 namespace {
 
 using base::allocator::AllocatorDispatch;
+using MallocUsableSizeFunction = decltype(malloc_usable_size)*;
+
+PROTECTED_MEMORY_SECTION base::ProtectedMemory<MallocUsableSizeFunction>
+    g_MallocUsableSizeFunction;
 
 void* GlibcMalloc(const AllocatorDispatch*, size_t size, void* context) {
   return __libc_malloc(size);
@@ -59,12 +64,11 @@
   // resolve it instead. This should be safe because glibc (and hence dlfcn)
   // does not use malloc_size internally and so there should not be a risk of
   // recursion.
-  using MallocUsableSizeFunction = decltype(malloc_usable_size)*;
-  static MallocUsableSizeFunction fn_ptr =
-      reinterpret_cast<MallocUsableSizeFunction>(
-          dlsym(RTLD_NEXT, "malloc_usable_size"));
+  static base::ProtectedMemory<MallocUsableSizeFunction>::Initializer init(
+      &g_MallocUsableSizeFunction, reinterpret_cast<MallocUsableSizeFunction>(
+                                       dlsym(RTLD_NEXT, "malloc_usable_size")));
 
-  return fn_ptr(address);
+  return base::UnsanitizedCfiCall(g_MallocUsableSizeFunction)(address);
 }
 
 }  // namespace
diff --git a/base/bind.h b/base/bind.h
index 7a400af..1070ce6d 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -187,18 +187,15 @@
 // well-formed. Using `Invoker::Run` with a OnceCallback triggers a
 // static_assert, which is why the ternary expression does not compile.
 // TODO(crbug.com/752720): Remove this indirection once we have `if constexpr`.
-template <bool is_once, typename Invoker>
-struct InvokeFuncImpl;
+template <typename Invoker>
+constexpr auto GetInvokeFunc(std::true_type) {
+  return Invoker::RunOnce;
+}
 
 template <typename Invoker>
-struct InvokeFuncImpl<true, Invoker> {
-  static constexpr auto Value = &Invoker::RunOnce;
-};
-
-template <typename Invoker>
-struct InvokeFuncImpl<false, Invoker> {
-  static constexpr auto Value = &Invoker::Run;
-};
+constexpr auto GetInvokeFunc(std::false_type) {
+  return Invoker::Run;
+}
 
 template <template <typename> class CallbackT,
           typename Functor,
@@ -229,7 +226,8 @@
   // InvokeFuncStorage, so that we can ensure its type matches to
   // PolymorphicInvoke, to which CallbackType will cast back.
   using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
-  PolymorphicInvoke invoke_func = InvokeFuncImpl<kIsOnce, Invoker>::Value;
+  PolymorphicInvoke invoke_func =
+      GetInvokeFunc<Invoker>(std::integral_constant<bool, kIsOnce>());
 
   using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
   return CallbackType(BindState::Create(
diff --git a/build/config/mac/BUILD.gn b/build/config/mac/BUILD.gn
index 780f752..797e8ce 100644
--- a/build/config/mac/BUILD.gn
+++ b/build/config/mac/BUILD.gn
@@ -77,7 +77,7 @@
   # macros that collide with common names, like 'check', 'require', and
   # 'verify'.
   # http://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/AssertMacros.h
-  defines = [ "__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE=0" ]
+  defines = [ "__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES=0" ]
 }
 
 # On Mac, this is used for everything except static libraries.
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 41ba5ef..b0d706f 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8899327402780790352
\ No newline at end of file
+8899266194319676112
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b83e199..3b033af 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8899333129287039888
\ No newline at end of file
+8899267142825427424
\ No newline at end of file
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 0f0dbf85..cb494e6 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -153,6 +153,17 @@
   EndCurrentStage(frame_termination_time_);
 }
 
+void CompositorFrameReporter::OnFinishImplFrame(base::TimeTicks timestamp) {
+  DCHECK(!did_finish_impl_frame_);
+
+  did_finish_impl_frame_ = true;
+  impl_frame_finish_time_ = timestamp;
+}
+
+void CompositorFrameReporter::OnAbortBeginMainFrame() {
+  did_abort_main_frame_ = false;
+}
+
 void CompositorFrameReporter::SetVizBreakdown(
     const viz::FrameTimingDetails& viz_breakdown) {
   DCHECK(current_stage_.viz_breakdown.received_compositor_frame_timestamp
@@ -161,7 +172,8 @@
 }
 
 void CompositorFrameReporter::TerminateReporter() {
-  DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
+  if (frame_termination_status_ != FrameTerminationStatus::kUnknown)
+    DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
   bool report_latency = false;
   const char* termination_status_str = nullptr;
   switch (frame_termination_status_) {
@@ -186,7 +198,7 @@
       termination_status_str = "did_not_produce_frame";
       break;
     case FrameTerminationStatus::kUnknown:
-      NOTREACHED();
+      termination_status_str = "terminated_before_ending";
       break;
   }
   const char* submission_status_str =
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index ad2b53e..79cbd089 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -111,6 +111,14 @@
 
   int StageHistorySizeForTesting() { return stage_history_.size(); }
 
+  void OnFinishImplFrame(base::TimeTicks timestamp);
+  void OnAbortBeginMainFrame();
+  bool did_finish_impl_frame() const { return did_finish_impl_frame_; }
+  bool did_abort_main_frame() const { return did_abort_main_frame_; }
+  base::TimeTicks impl_frame_finish_time() const {
+    return impl_frame_finish_time_;
+  }
+
  protected:
   struct StageData {
     StageType stage_type;
@@ -161,6 +169,14 @@
       FrameTerminationStatus::kUnknown;
 
   const base::flat_set<FrameSequenceTrackerType>* active_trackers_;
+
+  // Indicates if work on Impl frame is finished.
+  bool did_finish_impl_frame_ = false;
+  // Indicates if main frame is aborted after begin.
+  bool did_abort_main_frame_ = false;
+  // The time that work on Impl frame is finished. It's only valid if the
+  // reporter is in a stage other than begin impl frame.
+  base::TimeTicks impl_frame_finish_time_;
 };
 }  // namespace cc
 
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index 0fea655c..760207b 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -59,43 +59,60 @@
   std::unique_ptr<CompositorFrameReporter> reporter =
       std::make_unique<CompositorFrameReporter>(&active_trackers_,
                                                 is_single_threaded_);
-  reporter->StartStage(
-      CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame,
-      begin_time);
+  reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
+                       begin_time);
   reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter);
 }
 
 void CompositorFrameReportingController::WillBeginMainFrame() {
-  DCHECK(reporters_[PipelineStage::kBeginImplFrame]);
-  // We need to use .get() below because operator<< in std::unique_ptr is a
-  // C++20 feature.
-  DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(),
-            reporters_[PipelineStage::kBeginImplFrame].get());
-  reporters_[PipelineStage::kBeginImplFrame]->StartStage(
-      CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
-  AdvanceReporterStage(PipelineStage::kBeginImplFrame,
-                       PipelineStage::kBeginMainFrame);
+  if (reporters_[PipelineStage::kBeginImplFrame]) {
+    // We need to use .get() below because operator<< in std::unique_ptr is a
+    // C++20 feature.
+    DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(),
+              reporters_[PipelineStage::kBeginImplFrame].get());
+    reporters_[PipelineStage::kBeginImplFrame]->StartStage(
+        StageType::kSendBeginMainFrameToCommit, Now());
+    AdvanceReporterStage(PipelineStage::kBeginImplFrame,
+                         PipelineStage::kBeginMainFrame);
+  } else {
+    // In this case we have already submitted the ImplFrame, but we received
+    // beginMain frame before next BeginImplFrame (Not reached the ImplFrame
+    // deadline yet). So will start a new reporter at BeginMainFrame.
+    std::unique_ptr<CompositorFrameReporter> reporter =
+        std::make_unique<CompositorFrameReporter>(&active_trackers_,
+                                                  is_single_threaded_);
+    reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
+    reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter);
+  }
 }
 
 void CompositorFrameReportingController::BeginMainFrameAborted() {
   DCHECK(reporters_[PipelineStage::kBeginMainFrame]);
-  std::unique_ptr<CompositorFrameReporter> aborted_frame_reporter =
-      std::move(reporters_[PipelineStage::kBeginMainFrame]);
-  aborted_frame_reporter->TerminateFrame(
-      CompositorFrameReporter::FrameTerminationStatus::kMainFrameAborted,
-      Now());
+
+  auto& begin_main_reporter = reporters_[PipelineStage::kBeginMainFrame];
+  begin_main_reporter->OnAbortBeginMainFrame();
+
+  // If the main-frame was aborted (e.g. there was no visible update), then
+  // advance to activate stage if the compositor has already made changes to
+  // the active tree (i.e. if impl-frame has finished).
+  if (begin_main_reporter->did_finish_impl_frame()) {
+    begin_main_reporter->StartStage(
+        StageType::kEndActivateToSubmitCompositorFrame, Now());
+    AdvanceReporterStage(PipelineStage::kBeginMainFrame,
+                         PipelineStage::kActivate);
+  }
 }
 
 void CompositorFrameReportingController::WillCommit() {
   DCHECK(reporters_[PipelineStage::kBeginMainFrame]);
-  reporters_[PipelineStage::kBeginMainFrame]->StartStage(
-      CompositorFrameReporter::StageType::kCommit, Now());
+  reporters_[PipelineStage::kBeginMainFrame]->StartStage(StageType::kCommit,
+                                                         Now());
 }
 
 void CompositorFrameReportingController::DidCommit() {
   DCHECK(reporters_[PipelineStage::kBeginMainFrame]);
   reporters_[PipelineStage::kBeginMainFrame]->StartStage(
-      CompositorFrameReporter::StageType::kEndCommitToActivation, Now());
+      StageType::kEndCommitToActivation, Now());
   AdvanceReporterStage(PipelineStage::kBeginMainFrame, PipelineStage::kCommit);
 }
 
@@ -109,8 +126,7 @@
   DCHECK(reporters_[PipelineStage::kCommit] || next_activate_has_invalidation_);
   if (!reporters_[PipelineStage::kCommit])
     return;
-  reporters_[PipelineStage::kCommit]->StartStage(
-      CompositorFrameReporter::StageType::kActivation, Now());
+  reporters_[PipelineStage::kCommit]->StartStage(StageType::kActivation, Now());
 }
 
 void CompositorFrameReportingController::DidActivate() {
@@ -119,25 +135,57 @@
   if (!reporters_[PipelineStage::kCommit])
     return;
   reporters_[PipelineStage::kCommit]->StartStage(
-      CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame,
-      Now());
+      StageType::kEndActivateToSubmitCompositorFrame, Now());
   AdvanceReporterStage(PipelineStage::kCommit, PipelineStage::kActivate);
 }
 
 void CompositorFrameReportingController::DidSubmitCompositorFrame(
     uint32_t frame_token) {
+  // If there is no reporter in active stage and there exists a finished
+  // BeginImplFrame reporter (i.e. if impl-frame has finished), then advance it
+  // to the activate stage.
+  if (!reporters_[PipelineStage::kActivate] &&
+      reporters_[PipelineStage::kBeginImplFrame]) {
+    auto& begin_impl_frame = reporters_[PipelineStage::kBeginImplFrame];
+    if (begin_impl_frame->did_finish_impl_frame()) {
+      begin_impl_frame->StartStage(
+          StageType::kEndActivateToSubmitCompositorFrame,
+          begin_impl_frame->impl_frame_finish_time());
+      AdvanceReporterStage(PipelineStage::kBeginImplFrame,
+                           PipelineStage::kActivate);
+    }
+  }
+
   if (!reporters_[PipelineStage::kActivate])
     return;
+
   std::unique_ptr<CompositorFrameReporter> submitted_reporter =
       std::move(reporters_[PipelineStage::kActivate]);
   submitted_reporter->StartStage(
-      CompositorFrameReporter::StageType::
-          kSubmitCompositorFrameToPresentationCompositorFrame,
-      Now());
+      StageType::kSubmitCompositorFrameToPresentationCompositorFrame, Now());
   submitted_compositor_frames_.emplace_back(frame_token,
                                             std::move(submitted_reporter));
 }
 
+void CompositorFrameReportingController::OnFinishImplFrame() {
+  if (reporters_[PipelineStage::kBeginImplFrame]) {
+    reporters_[PipelineStage::kBeginImplFrame]->OnFinishImplFrame(Now());
+  } else if (reporters_[PipelineStage::kBeginMainFrame]) {
+    auto& begin_main_reporter = reporters_[PipelineStage::kBeginMainFrame];
+    begin_main_reporter->OnFinishImplFrame(Now());
+
+    // If the main-frame was aborted (e.g. there was no visible update), then
+    // advance to activate stage if the compositor has already made changes to
+    // the active tree (i.e. if impl-frame has finished).
+    if (begin_main_reporter->did_abort_main_frame()) {
+      begin_main_reporter->StartStage(
+          StageType::kEndActivateToSubmitCompositorFrame, Now());
+      AdvanceReporterStage(PipelineStage::kBeginMainFrame,
+                           PipelineStage::kActivate);
+    }
+  }
+}
+
 void CompositorFrameReportingController::DidPresentCompositorFrame(
     uint32_t frame_token,
     const viz::FrameTimingDetails& details) {
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 997d26b..80254c1d 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -57,6 +57,7 @@
   virtual void WillActivate();
   virtual void DidActivate();
   virtual void DidSubmitCompositorFrame(uint32_t frame_token);
+  virtual void OnFinishImplFrame();
   virtual void DidPresentCompositorFrame(
       uint32_t frame_token,
       const viz::FrameTimingDetails& details);
diff --git a/cc/metrics/compositor_timing_history.cc b/cc/metrics/compositor_timing_history.cc
index 6991812..d244237b 100644
--- a/cc/metrics/compositor_timing_history.cc
+++ b/cc/metrics/compositor_timing_history.cc
@@ -735,6 +735,8 @@
 void CompositorTimingHistory::WillFinishImplFrame(bool needs_redraw) {
   if (!needs_redraw)
     SetCompositorDrawingContinuously(false);
+
+  compositor_frame_reporting_controller_->OnFinishImplFrame();
 }
 
 void CompositorTimingHistory::BeginImplFrameNotExpectedSoon() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index be206ff..d21c62d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -11,6 +11,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler.VoiceResult;
@@ -54,7 +55,8 @@
 
     public AutocompleteController(Profile profile, OnSuggestionsReceivedListener listener) {
         if (profile != null) {
-            mNativeAutocompleteControllerAndroid = nativeInit(profile);
+            mNativeAutocompleteControllerAndroid =
+                    AutocompleteControllerJni.get().init(AutocompleteController.this, profile);
         }
         mListener = listener;
     }
@@ -76,7 +78,8 @@
             return;
         }
 
-        mNativeAutocompleteControllerAndroid = nativeInit(profile);
+        mNativeAutocompleteControllerAndroid =
+                AutocompleteControllerJni.get().init(AutocompleteController.this, profile);
     }
 
     /**
@@ -108,10 +111,12 @@
                 TextUtils.isEmpty(url));
         if (profile == null || TextUtils.isEmpty(url)) return;
 
-        mNativeAutocompleteControllerAndroid = nativeInit(profile);
+        mNativeAutocompleteControllerAndroid =
+                AutocompleteControllerJni.get().init(AutocompleteController.this, profile);
         // Initializing the native counterpart might still fail.
         if (mNativeAutocompleteControllerAndroid != 0) {
-            nativeStart(mNativeAutocompleteControllerAndroid, text, cursorPosition, null, url,
+            AutocompleteControllerJni.get().start(mNativeAutocompleteControllerAndroid,
+                    AutocompleteController.this, text, cursorPosition, null, url,
                     pageClassification, preventInlineAutocomplete, false, false, true);
             mWaitingForSuggestionsToCache = false;
         }
@@ -134,7 +139,8 @@
      */
     public OmniboxSuggestion classify(String text, boolean focusedFromFakebox) {
         if (mNativeAutocompleteControllerAndroid != 0) {
-            return nativeClassify(mNativeAutocompleteControllerAndroid, text, focusedFromFakebox);
+            return AutocompleteControllerJni.get().classify(mNativeAutocompleteControllerAndroid,
+                    AutocompleteController.this, text, focusedFromFakebox);
         }
         return null;
     }
@@ -157,11 +163,12 @@
             // especially if a Service Worker is used.
             WarmupManager.getInstance().createSpareRenderProcessHost(profile);
         }
-        mNativeAutocompleteControllerAndroid = nativeInit(profile);
+        mNativeAutocompleteControllerAndroid =
+                AutocompleteControllerJni.get().init(AutocompleteController.this, profile);
         if (mNativeAutocompleteControllerAndroid != 0) {
             if (mUseCachedZeroSuggestResults) mWaitingForSuggestionsToCache = true;
-            nativeOnOmniboxFocused(mNativeAutocompleteControllerAndroid, omniboxText, url,
-                    pageClassification, title);
+            AutocompleteControllerJni.get().onOmniboxFocused(mNativeAutocompleteControllerAndroid,
+                    AutocompleteController.this, omniboxText, url, pageClassification, title);
         }
     }
 
@@ -183,7 +190,8 @@
         if (mNativeAutocompleteControllerAndroid != 0) {
             // crbug.com/764749
             Log.w(TAG, "stopping autocomplete.");
-            nativeStop(mNativeAutocompleteControllerAndroid, clear);
+            AutocompleteControllerJni.get().stop(
+                    mNativeAutocompleteControllerAndroid, AutocompleteController.this, clear);
         }
     }
 
@@ -193,7 +201,8 @@
      */
     void resetSession() {
         if (mNativeAutocompleteControllerAndroid != 0) {
-            nativeResetSession(mNativeAutocompleteControllerAndroid);
+            AutocompleteControllerJni.get().resetSession(
+                    mNativeAutocompleteControllerAndroid, AutocompleteController.this);
         }
     }
 
@@ -203,7 +212,8 @@
      */
     void deleteSuggestion(int position, int hashCode) {
         if (mNativeAutocompleteControllerAndroid != 0) {
-            nativeDeleteSuggestion(mNativeAutocompleteControllerAndroid, position, hashCode);
+            AutocompleteControllerJni.get().deleteSuggestion(mNativeAutocompleteControllerAndroid,
+                    AutocompleteController.this, position, hashCode);
         }
     }
 
@@ -256,9 +266,9 @@
         assert mNativeAutocompleteControllerAndroid != 0;
         // Don't natively log voice suggestion results as we add them in Java.
         if (type == OmniboxSuggestionType.VOICE_SUGGEST) return;
-        nativeOnSuggestionSelected(mNativeAutocompleteControllerAndroid, selectedIndex, hashCode,
-                currentPageUrl, pageClassification, elapsedTimeSinceModified, completedLength,
-                webContents);
+        AutocompleteControllerJni.get().onSuggestionSelected(mNativeAutocompleteControllerAndroid,
+                AutocompleteController.this, selectedIndex, hashCode, currentPageUrl,
+                pageClassification, elapsedTimeSinceModified, completedLength, webContents);
     }
 
     /**
@@ -332,45 +342,49 @@
      */
     String updateMatchDestinationUrlWithQueryFormulationTime(
             int selectedIndex, int hashCode, long elapsedTimeSinceInputChange) {
-        return nativeUpdateMatchDestinationURLWithQueryFormulationTime(
-                mNativeAutocompleteControllerAndroid, selectedIndex, hashCode,
-                elapsedTimeSinceInputChange);
+        return AutocompleteControllerJni.get().updateMatchDestinationURLWithQueryFormulationTime(
+                mNativeAutocompleteControllerAndroid, AutocompleteController.this, selectedIndex,
+                hashCode, elapsedTimeSinceInputChange);
     }
 
-    @VisibleForTesting
-    protected native long nativeInit(Profile profile);
-    private native void nativeStart(long nativeAutocompleteControllerAndroid, String text,
-            int cursorPosition, String desiredTld, String currentUrl, int pageClassification,
-            boolean preventInlineAutocomplete, boolean preferKeyword,
-            boolean allowExactKeywordMatch, boolean wantAsynchronousMatches);
-    private native OmniboxSuggestion nativeClassify(
-            long nativeAutocompleteControllerAndroid, String text, boolean focusedFromFakebox);
-    private native void nativeStop(long nativeAutocompleteControllerAndroid, boolean clearResults);
-    private native void nativeResetSession(long nativeAutocompleteControllerAndroid);
-    private native void nativeOnSuggestionSelected(long nativeAutocompleteControllerAndroid,
-            int selectedIndex, int hashCode, String currentPageUrl, int pageClassification,
-            long elapsedTimeSinceModified, int completedLength, WebContents webContents);
-    private native void nativeOnOmniboxFocused(long nativeAutocompleteControllerAndroid,
-            String omniboxText, String currentUrl, int pageClassification, String currentTitle);
-    private native void nativeDeleteSuggestion(
-            long nativeAutocompleteControllerAndroid, int selectedIndex, int hashCode);
-    private native String nativeUpdateMatchDestinationURLWithQueryFormulationTime(
-            long nativeAutocompleteControllerAndroid, int selectedIndex, int hashCode,
-            long elapsedTimeSinceInputChange);
+    @NativeMethods
+    interface Natives {
+        long init(AutocompleteController caller, Profile profile);
+        void start(long nativeAutocompleteControllerAndroid, AutocompleteController caller,
+                String text, int cursorPosition, String desiredTld, String currentUrl,
+                int pageClassification, boolean preventInlineAutocomplete, boolean preferKeyword,
+                boolean allowExactKeywordMatch, boolean wantAsynchronousMatches);
+        OmniboxSuggestion classify(long nativeAutocompleteControllerAndroid,
+                AutocompleteController caller, String text, boolean focusedFromFakebox);
+        void stop(long nativeAutocompleteControllerAndroid, AutocompleteController caller,
+                boolean clearResults);
+        void resetSession(long nativeAutocompleteControllerAndroid, AutocompleteController caller);
+        void onSuggestionSelected(long nativeAutocompleteControllerAndroid,
+                AutocompleteController caller, int selectedIndex, int hashCode,
+                String currentPageUrl, int pageClassification, long elapsedTimeSinceModified,
+                int completedLength, WebContents webContents);
+        void onOmniboxFocused(long nativeAutocompleteControllerAndroid,
+                AutocompleteController caller, String omniboxText, String currentUrl,
+                int pageClassification, String currentTitle);
+        void deleteSuggestion(long nativeAutocompleteControllerAndroid,
+                AutocompleteController caller, int selectedIndex, int hashCode);
+        String updateMatchDestinationURLWithQueryFormulationTime(
+                long nativeAutocompleteControllerAndroid, AutocompleteController caller,
+                int selectedIndex, int hashCode, long elapsedTimeSinceInputChange);
+        /**
+         * Given a search query, this will attempt to see if the query appears to be portion of a
+         * properly formed URL.  If it appears to be a URL, this will return the fully qualified
+         * version (i.e. including the scheme, etc...).  If the query does not appear to be a URL,
+         * this will return null.
+         *
+         * @param query The query to be expanded into a fully qualified URL if appropriate.
+         * @return The fully qualified URL or null.
+         */
+        String qualifyPartialURLQuery(String query);
 
-    /**
-     * Given a search query, this will attempt to see if the query appears to be portion of a
-     * properly formed URL.  If it appears to be a URL, this will return the fully qualified
-     * version (i.e. including the scheme, etc...).  If the query does not appear to be a URL,
-     * this will return null.
-     *
-     * @param query The query to be expanded into a fully qualified URL if appropriate.
-     * @return The fully qualified URL or null.
-     */
-    static native String nativeQualifyPartialURLQuery(String query);
-
-    /**
-     * Sends a zero suggest request to the server in order to pre-populate the result cache.
-     */
-    static native void nativePrefetchZeroSuggestResults();
+        /**
+         * Sends a zero suggest request to the server in order to pre-populate the result cache.
+         */
+        void prefetchZeroSuggestResults();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorFactory.java
index bae7657..21ef03ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorFactory.java
@@ -32,7 +32,7 @@
 
     /**
      * Temporary shortcut for {@link org.chromium.chrome.browser.IntentHandler} to access
-     * {@link AutocompleteController#nativeQualifyPartialURLQuery(String)} without having an
+     * {@link AutocompleteControllerJni.get().qualifyPartialURLQuery(String)} without having an
      * instance of {@link AutocompleteCoordinator}.
      *
      * TODO(crbug.com/966424): Fix the dependency issue and remove this method.
@@ -42,6 +42,6 @@
      */
     @Deprecated
     public static String qualifyPartialURLQuery(String query) {
-        return AutocompleteController.nativeQualifyPartialURLQuery(query);
+        return AutocompleteControllerJni.get().qualifyPartialURLQuery(query);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
index e32b714..4687001 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinatorImpl.java
@@ -265,12 +265,12 @@
 
     @Override
     public String qualifyPartialURLQuery(String query) {
-        return AutocompleteController.nativeQualifyPartialURLQuery(query);
+        return AutocompleteControllerJni.get().qualifyPartialURLQuery(query);
     }
 
     @Override
     public void prefetchZeroSuggestResults() {
-        AutocompleteController.nativePrefetchZeroSuggestResults();
+        AutocompleteControllerJni.get().prefetchZeroSuggestResults();
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 979daa9..dd27c7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -381,7 +381,7 @@
                 private void maybeTriggerCacheRefresh(String url) {
                     if (url == null) return;
                     if (!UrlConstants.NTP_URL.equals(url)) return;
-                    AutocompleteController.nativePrefetchZeroSuggestResults();
+                    AutocompleteControllerJni.get().prefetchZeroSuggestResults();
                 }
             };
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
index 81c7b7d..66788993 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
@@ -20,9 +20,11 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.task.PostTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.modaldialog.TabModalPresenter;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -120,7 +122,9 @@
         int oldHeight = oldBottom - oldTop;
         int newHeight = bottom - top;
         if (newHeight == oldHeight) return;
-        mModel.set(ILLUSTRATION_VISIBLE, hasSufficientSpaceForIllustration(newHeight));
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+            mModel.set(ILLUSTRATION_VISIBLE, hasSufficientSpaceForIllustration(newHeight));
+        });
     }
 
     void showDialog(@ModalDialogManager.ModalDialogType int type) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogView.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogView.java
index e55c78c..4295cff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogView.java
@@ -45,8 +45,6 @@
             mIllustrationView.setVisibility(VISIBLE);
         } else {
             mIllustrationView.setVisibility(GONE);
-            requestLayout(); // Sometimes the visibility isn't propagated correctly and the image
-                             // appears as INVISIBLE.
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index c745bd56..3881c70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -459,6 +459,9 @@
         // This method should be called at most once per sign-in flow.
         assert mSignInState != null && mSignInState.mCoreAccountInfo != null;
 
+        // The user should not be already signed in
+        assert !mIdentityManager.hasPrimaryAccount();
+
         if (!mIdentityMutator.setPrimaryAccount(mSignInState.mCoreAccountInfo.getId())) {
             Log.w(TAG, "Failed to set the PrimaryAccount in IdentityManager, aborting signin");
             abortSignIn();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java
index b2cf64c..5b4816e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java
@@ -8,12 +8,10 @@
 import static android.support.test.espresso.Espresso.pressBack;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 
-import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.mockito.Mockito.verify;
 
@@ -37,6 +35,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -135,7 +134,7 @@
             mMediator.onLayoutChange(null, 0, 0, (int) (testWidthDipLandscape * dipScale),
                     (int) (testHeightDipLandscape * dipScale), 0, 0, 0, 0);
         });
-        assertThat(mModel.get(ILLUSTRATION_VISIBLE), is(false));
+        CriteriaHelper.pollUiThread(() -> !mModel.get(ILLUSTRATION_VISIBLE));
 
         // Dimensions resembling portrait orientation.
         final int testHeightDipPortrait = 500;
@@ -144,7 +143,7 @@
             mMediator.onLayoutChange(null, 0, 0, (int) (testWidthDipPortrait * dipScale),
                     (int) (testHeightDipPortrait * dipScale), 0, 0, 0, 0);
         });
-        assertThat(mModel.get(ILLUSTRATION_VISIBLE), is(true));
+        CriteriaHelper.pollUiThread(() -> mModel.get(ILLUSTRATION_VISIBLE));
 
         // Dimensions resembling multi-window mode.
         final int testHeightDipMultiWindow = 250;
@@ -153,6 +152,6 @@
             mMediator.onLayoutChange(null, 0, 0, (int) (testWidthDipMultiWindow * dipScale),
                     (int) (testHeightDipMultiWindow * dipScale), 0, 0, 0, 0);
         });
-        assertThat(mModel.get(ILLUSTRATION_VISIBLE), is(false));
+        CriteriaHelper.pollUiThread(() -> !mModel.get(ILLUSTRATION_VISIBLE));
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java
index f038b4d..b02dc89 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcherTest.java
@@ -290,7 +290,7 @@
         // If the CONTENT_SETTINGS_NUM_TYPES value changes *and* a new value has been exposed on
         // Android, then please update this code block to include a test for your new type.
         // Otherwise, just update count in the assert.
-        Assert.assertEquals(53, ContentSettingsType.CONTENT_SETTINGS_NUM_TYPES);
+        Assert.assertEquals(54, ContentSettingsType.CONTENT_SETTINGS_NUM_TYPES);
         websitePreferenceBridge.addContentSettingException(
                 new ContentSettingException(ContentSettingsType.CONTENT_SETTINGS_TYPE_COOKIES,
                         googleOrigin, ContentSettingValues.DEFAULT, preferenceSource));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
index 1bf85d80..bd6e39de4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
@@ -222,6 +222,7 @@
         doReturn(account)
                 .when(mIdentityManager)
                 .findExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress(any());
+        doReturn(false).when(mIdentityManager).hasPrimaryAccount();
         doReturn(true).when(mIdentityMutator).setPrimaryAccount(any());
         doNothing().when(mIdentityMutator).reloadAllAccountsFromSystemWithPrimaryAccount(any());
 
@@ -237,4 +238,30 @@
         assertFalse(mSigninManager.isOperationInProgress());
         assertEquals(1, callCount.get());
     }
+
+    @Test(expected = AssertionError.class)
+    public void failIfAlreadySignedin() {
+        CoreAccountInfo account = new CoreAccountInfo(new CoreAccountId("test_at_gmail.com"),
+                new Account("test@gmail.com", AccountManagerFacade.GOOGLE_ACCOUNT_TYPE),
+                "test_at_gmail.com");
+
+        // No need to seed accounts to the native code.
+        doReturn(true).when(mAccountTrackerService).checkAndSeedSystemAccounts();
+        // Request that policy is loaded. It will pause sign-in until onPolicyCheckedBeforeSignIn is
+        // invoked.
+        doNothing().when(mNativeMock).fetchAndApplyCloudPolicy(anyLong(), any(), any());
+
+        doReturn(account)
+                .when(mIdentityManager)
+                .findExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress(any());
+        doReturn(true).when(mIdentityManager).hasPrimaryAccount();
+
+        mSigninManager.onFirstRunCheckDone(); // Allow sign-in.
+
+        mSigninManager.signIn(account.getAccount(), null);
+        assertTrue(mSigninManager.isOperationInProgress());
+
+        // The following should throw an assertion error
+        mSigninManager.finishSignInAfterPolicyEnforced();
+    }
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 34d93246..42b899b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5389,6 +5389,9 @@
   sources = [
     "flag-metadata.json",
   ]
+  inputs = [
+    "//chrome/VERSION",
+  ]
   outputs = [
     "$root_gen_dir/chrome/browser/expired_flags_list.cc",
   ]
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 4eca632..70bab30 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -787,6 +787,10 @@
 
 #if !defined(OS_ANDROID)
     host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
+        CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA, delete_begin_,
+        delete_end_, website_settings_filter);
+
+    host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
         CONTENT_SETTINGS_TYPE_INTENT_PICKER_DISPLAY, delete_begin_, delete_end_,
         website_settings_filter);
 #endif
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 9c66da9d..9b85c178e 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -94,6 +94,7 @@
 #include "content/public/test/mock_download_manager.h"
 #include "content/public/test/test_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/network_isolation_key.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_auth_cache.h"
@@ -2461,20 +2462,23 @@
 
   net::HttpAuthCache* http_auth_cache = http_session->http_auth_cache();
   http_auth_cache->Add(kOrigin1, net::HttpAuth::AUTH_SERVER, kTestRealm,
-                       net::HttpAuth::AUTH_SCHEME_BASIC, "test challenge",
+                       net::HttpAuth::AUTH_SCHEME_BASIC,
+                       net::NetworkIsolationKey(), "test challenge",
                        net::AuthCredentials(base::ASCIIToUTF16("foo"),
                                             base::ASCIIToUTF16("bar")),
                        "/");
   CHECK(http_auth_cache->Lookup(kOrigin1, net::HttpAuth::AUTH_SERVER,
-                                kTestRealm, net::HttpAuth::AUTH_SCHEME_BASIC));
+                                kTestRealm, net::HttpAuth::AUTH_SCHEME_BASIC,
+                                net::NetworkIsolationKey()));
 
   BlockUntilBrowsingDataRemoved(
       base::Time(), base::Time::Max(),
       ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS, false);
 
-  EXPECT_EQ(nullptr, http_auth_cache->Lookup(
-                         kOrigin1, net::HttpAuth::AUTH_SERVER, kTestRealm,
-                         net::HttpAuth::AUTH_SCHEME_BASIC));
+  EXPECT_EQ(nullptr,
+            http_auth_cache->Lookup(
+                kOrigin1, net::HttpAuth::AUTH_SERVER, kTestRealm,
+                net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey()));
 }
 
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, ClearPermissionPromptCounts) {
diff --git a/chrome/browser/browsing_data/counters/site_data_counting_helper.cc b/chrome/browser/browsing_data/counters/site_data_counting_helper.cc
index ec30f25..95c1604 100644
--- a/chrome/browser/browsing_data/counters/site_data_counting_helper.cc
+++ b/chrome/browser/browsing_data/counters/site_data_counting_helper.cc
@@ -127,7 +127,12 @@
   // Counting site usage data and durable permissions.
   auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile_);
   const ContentSettingsType content_settings[] = {
-      CONTENT_SETTINGS_TYPE_DURABLE_STORAGE, CONTENT_SETTINGS_TYPE_APP_BANNER};
+    CONTENT_SETTINGS_TYPE_DURABLE_STORAGE,
+    CONTENT_SETTINGS_TYPE_APP_BANNER,
+#if !defined(OS_ANDROID)
+    CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA,
+#endif
+  };
   for (auto type : content_settings) {
     tasks_ += 1;
     GetOriginsFromHostContentSettignsMap(hcsm, type);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index de048af..339ac92 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2183,10 +2183,14 @@
     "usb/cros_usb_detector.h",
     "virtual_machines/virtual_machines_util.cc",
     "virtual_machines/virtual_machines_util.h",
+    "wilco_dtc_supportd/fake_wilco_dtc_supportd_client.cc",
+    "wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h",
     "wilco_dtc_supportd/mojo_utils.cc",
     "wilco_dtc_supportd/mojo_utils.h",
     "wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc",
     "wilco_dtc_supportd/wilco_dtc_supportd_bridge.h",
+    "wilco_dtc_supportd/wilco_dtc_supportd_client.cc",
+    "wilco_dtc_supportd/wilco_dtc_supportd_client.h",
     "wilco_dtc_supportd/wilco_dtc_supportd_manager.cc",
     "wilco_dtc_supportd/wilco_dtc_supportd_manager.h",
     "wilco_dtc_supportd/wilco_dtc_supportd_messaging.cc",
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager_browsertest.cc b/chrome/browser/chromeos/accessibility/accessibility_manager_browsertest.cc
index e12108f..2520ebc 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager_browsertest.cc
@@ -525,14 +525,16 @@
   ~AccessibilityManagerLoginTest() override = default;
 
   void SetUpOnMainThread() override {
-    OobeBaseTest::SetUpOnMainThread();
+    // BrailleController has to be set before SetUpOnMainThread call as
+    // observers subscribe to the controller during SetUpOnMainThread.
     AccessibilityManager::SetBrailleControllerForTest(&braille_controller_);
     default_autoclick_delay_ = GetAutoclickDelay();
+    OobeBaseTest::SetUpOnMainThread();
   }
 
   void TearDownOnMainThread() override {
-    AccessibilityManager::SetBrailleControllerForTest(nullptr);
     OobeBaseTest::TearDownOnMainThread();
+    AccessibilityManager::SetBrailleControllerForTest(nullptr);
   }
 
   void CreateSession(const AccountId& account_id) {
diff --git a/chrome/browser/chromeos/customization/customization_document.cc b/chrome/browser/chromeos/customization/customization_document.cc
index 6ab83d7f..cb8c970 100644
--- a/chrome/browser/chromeos/customization/customization_document.cc
+++ b/chrome/browser/chromeos/customization/customization_document.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/constants/chromeos_paths.h"
 #include "chromeos/system/statistics_provider.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -73,10 +74,6 @@
 
 const char kAcceptedManifestVersion[] = "1.0";
 
-// Path to OEM partner startup customization manifest.
-const char kStartupCustomizationManifestPath[] =
-    "/opt/oem/etc/startup_manifest.json";
-
 // This is subdirectory relative to PathService(DIR_CHROMEOS_CUSTOM_WALLPAPERS),
 // where downloaded (and resized) wallpaper is stored.
 const char kCustomizationDefaultWallpaperDir[] = "customization";
@@ -287,7 +284,10 @@
     // Loading manifest causes us to do blocking IO on UI thread.
     // Temporarily allow it until we fix http://crosbug.com/11103
     base::ThreadRestrictions::ScopedAllowIO allow_io;
-    LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
+    base::FilePath startup_customization_manifest;
+    base::PathService::Get(chromeos::FILE_STARTUP_CUSTOMIZATION_MANIFEST,
+                           &startup_customization_manifest);
+    LoadManifestFromFile(startup_customization_manifest);
   }
   Init(system::StatisticsProvider::GetInstance());
 }
diff --git a/chrome/browser/chromeos/dbus/dbus_helper.cc b/chrome/browser/chromeos/dbus/dbus_helper.cc
index 674fbba0..0a5fb39 100644
--- a/chrome/browser/chromeos/dbus/dbus_helper.cc
+++ b/chrome/browser/chromeos/dbus/dbus_helper.cc
@@ -7,6 +7,7 @@
 #include "base/path_service.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/constants/chromeos_paths.h"
 #include "chromeos/cryptohome/system_salt_getter.h"
@@ -84,11 +85,13 @@
 void InitializeFeatureListDependentDBus() {
   dbus::Bus* bus = DBusThreadManager::Get()->GetSystemBus();
   InitializeDBusClient<bluez::BluezDBusManager>(bus);
+  InitializeDBusClient<WilcoDtcSupportdClient>(bus);
 }
 
 void ShutdownDBus() {
-  // Feature list-dependent D-Bus clients are shut down first because we try to.
+  // Feature list-dependent D-Bus clients are shut down first because we try to
   // shut down in reverse order of initialization (in case of dependencies).
+  WilcoDtcSupportdClient::Shutdown();
   bluez::BluezDBusManager::Shutdown();
 
   // Other D-Bus clients are shut down, also in reverse order of initialization.
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 7a71ce6..24bc3bc7 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -33,6 +33,7 @@
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/statistics_recorder.h"
+#include "base/run_loop.h"
 #include "base/scoped_observer.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -88,6 +89,7 @@
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
+#include "components/policy/core/common/policy_service.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/histogram_fetcher.h"
 #include "extensions/browser/event_router.h"
@@ -668,6 +670,26 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateRefreshEnterprisePoliciesFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateRefreshEnterprisePoliciesFunction::
+    ~AutotestPrivateRefreshEnterprisePoliciesFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateRefreshEnterprisePoliciesFunction::Run() {
+  DVLOG(1) << "AutotestPrivateRefreshEnterprisePoliciesFunction";
+
+  g_browser_process->policy_service()->RefreshPolicies(base::Bind(
+      &AutotestPrivateRefreshEnterprisePoliciesFunction::RefreshDone, this));
+  return RespondLater();
+}
+
+void AutotestPrivateRefreshEnterprisePoliciesFunction::RefreshDone() {
+  Respond(NoArguments());
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateGetExtensionsInfoFunction
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index b4bd816f..785425d3 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -502,6 +502,20 @@
   ResponseAction Run() override;
 };
 
+class AutotestPrivateRefreshEnterprisePoliciesFunction
+    : public ExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("autotestPrivate.refreshEnterprisePolicies",
+                             AUTOTESTPRIVATE_REFRESHENTERPRISEPOLICIES)
+
+ private:
+  ~AutotestPrivateRefreshEnterprisePoliciesFunction() override;
+  ResponseAction Run() override;
+
+  // Called once all the policies have been refreshed.
+  void RefreshDone();
+};
+
 class AutotestPrivateBootstrapMachineLearningServiceFunction
     : public ExtensionFunction {
  public:
diff --git a/chrome/browser/chromeos/login/accessibility_browsertest.cc b/chrome/browser/chromeos/login/accessibility_browsertest.cc
new file mode 100644
index 0000000..1c46820
--- /dev/null
+++ b/chrome/browser/chromeos/login/accessibility_browsertest.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 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 "ash/public/cpp/docked_magnifier_controller.h"
+#include "ash/public/cpp/keyboard/keyboard_controller.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/accessibility/magnification_manager.h"
+#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
+#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/view_observer.h"
+
+namespace chromeos {
+
+class DockedMagnifierVirtualKeyboardTest
+    : public OobeBaseTest,
+      public views::ViewObserver,
+      public ChromeKeyboardControllerClient::Observer {
+ public:
+  DockedMagnifierVirtualKeyboardTest() = default;
+  ~DockedMagnifierVirtualKeyboardTest() override = default;
+
+ protected:
+  ChromeKeyboardControllerClient* keyboard_controller() {
+    return ChromeKeyboardControllerClient::Get();
+  }
+
+  MagnificationManager* magnification_manager() {
+    return MagnificationManager::Get();
+  }
+
+  WebUILoginView* webui_login_view() {
+    return LoginDisplayHost::default_host()->GetWebUILoginView();
+  }
+
+  gfx::Rect GetOobeBounds() { return webui_login_view()->GetBoundsInScreen(); }
+
+  void ShowDockedMagnifier() {
+    magnification_manager()->SetDockedMagnifierEnabled(true);
+    ASSERT_TRUE(magnification_manager()->IsDockedMagnifierEnabled());
+    ASSERT_GT(GetMagnifierHeight(), 0);
+  }
+
+  void HideDockedMagnifier() {
+    magnification_manager()->SetDockedMagnifierEnabled(false);
+    ASSERT_FALSE(magnification_manager()->IsDockedMagnifierEnabled());
+    ASSERT_EQ(GetMagnifierHeight(), 0);
+  }
+
+  int GetMagnifierHeight() {
+    return ash::DockedMagnifierController::Get()
+        ->GetMagnifierHeightForTesting();
+  }
+
+  void ShowKeyboard() {
+    AccessibilityManager::Get()->EnableVirtualKeyboard(true);
+    keyboard_controller()->ShowKeyboard();
+    keyboard_controller()->SetKeyboardLocked(true);
+    WaitForBoundsToChange();
+    ASSERT_GT(GetKeyboardHeight(), 0);
+  }
+
+  void HideKeyboard() {
+    keyboard_controller()->HideKeyboard(ash::HideReason::kUser);
+    ASSERT_EQ(GetKeyboardHeight(), 0);
+  }
+
+  int GetKeyboardHeight() { return keyboard_bounds_.height(); }
+
+  void WaitForBoundsToChange() {
+    ASSERT_FALSE(run_loop_);
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+ private:
+  // views::ViewObserver:
+  void OnViewBoundsChanged(views::View* observed_view) override {
+    ASSERT_EQ(observed_view, webui_login_view());
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  // ChromeKeyboardControllerClient::Observer:
+  void OnKeyboardOccludedBoundsChanged(
+      const gfx::Rect& screen_bounds) override {
+    keyboard_bounds_ = screen_bounds;
+  }
+
+  // OobeBaseTest:
+  void SetUpOnMainThread() override {
+    OobeBaseTest::SetUpOnMainThread();
+    webui_login_view()->views::View::AddObserver(this);
+    keyboard_controller()->AddObserver(this);
+  }
+
+  void TearDownOnMainThread() override {
+    webui_login_view()->views::View::RemoveObserver(this);
+    keyboard_controller()->RemoveObserver(this);
+    OobeBaseTest::TearDownOnMainThread();
+  }
+
+  std::unique_ptr<base::RunLoop> run_loop_;
+  gfx::Rect keyboard_bounds_;
+};
+
+IN_PROC_BROWSER_TEST_F(DockedMagnifierVirtualKeyboardTest, WelcomeScreen) {
+  const gfx::Rect original_bounds = GetOobeBounds();
+  gfx::Rect expected_bounds(original_bounds);
+
+  ShowDockedMagnifier();
+  expected_bounds.Inset(0, GetMagnifierHeight(), 0, 0);
+  EXPECT_EQ(expected_bounds, GetOobeBounds());
+
+  ShowKeyboard();
+  expected_bounds.Inset(0, 0, 0, GetKeyboardHeight());
+  EXPECT_EQ(expected_bounds, GetOobeBounds());
+
+  expected_bounds.Inset(0, -GetMagnifierHeight(), 0, 0);
+  HideDockedMagnifier();
+  EXPECT_EQ(expected_bounds, GetOobeBounds());
+
+  HideKeyboard();
+  EXPECT_EQ(original_bounds, GetOobeBounds());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/error_screen_browsertest.cc b/chrome/browser/chromeos/login/error_screen_browsertest.cc
index ff86bd14..1a0f7b6 100644
--- a/chrome/browser/chromeos/login/error_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/error_screen_browsertest.cc
@@ -184,7 +184,7 @@
 }
 
 // Test HideCallback is called after screen hides.
-IN_PROC_BROWSER_TEST_F(NetworkErrorScreenTest, DISABLED_HideCallback) {
+IN_PROC_BROWSER_TEST_F(NetworkErrorScreenTest, HideCallback) {
   bool callback_called = false;
   GetScreen()->SetHideCallback(
       base::BindLambdaForTesting([&]() { callback_called = true; }));
diff --git a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
index f65e74d..a984c13 100644
--- a/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
+++ b/chrome/browser/chromeos/login/profile_auth_data_unittest.cc
@@ -23,6 +23,7 @@
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/network_isolation_key.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/http/http_auth.h"
@@ -170,7 +171,8 @@
   net::HttpAuthCache::Entry* entry =
       GetProxyAuth(user_browser_context_.network_context())
           ->Lookup(GURL(kProxyAuthURL), net::HttpAuth::AUTH_SERVER,
-                   kProxyAuthRealm, net::HttpAuth::AUTH_SCHEME_BASIC);
+                   kProxyAuthRealm, net::HttpAuth::AUTH_SCHEME_BASIC,
+                   net::NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(base::ASCIIToUTF16(kProxyAuthPassword1),
             entry->credentials().password());
@@ -205,7 +207,8 @@
     const std::string& cookie_value) {
   GetProxyAuth(browser_context->network_context())
       ->Add(GURL(kProxyAuthURL), net::HttpAuth::AUTH_SERVER, kProxyAuthRealm,
-            net::HttpAuth::AUTH_SCHEME_BASIC, kProxyAuthChallenge,
+            net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+            kProxyAuthChallenge,
             net::AuthCredentials(base::string16(),
                                  base::ASCIIToUTF16(proxy_auth_password)),
             std::string());
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
index 67fbeca..423bd9af 100644
--- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc
+++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -77,7 +77,6 @@
 #include "components/account_id/account_id.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/guest_view/browser/test_guest_view_manager.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_map.h"
@@ -95,7 +94,6 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
-#include "extensions/browser/guest_view/web_view/web_view_guest.h"
 #include "extensions/common/features/feature_channel.h"
 #include "google_apis/gaia/fake_gaia.h"
 #include "google_apis/gaia/gaia_constants.h"
@@ -831,22 +829,15 @@
   void SetUpOnMainThread() override;
   void StartSamlAndWaitForIdpPageLoad(const std::string& gaia_email) override;
 
-  guest_view::TestGuestViewManager* GetGuestViewManager();
-  content::WebContents* GetEnrollmentContents();
-
  protected:
   LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
   test::EnrollmentUIMixin enrollment_ui_{&mixin_host_};
 
-  guest_view::TestGuestViewManagerFactory guest_view_manager_factory_;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(SAMLEnrollmentTest);
 };
 
 SAMLEnrollmentTest::SAMLEnrollmentTest() {
-  guest_view::GuestViewManager::set_factory_for_testing(
-      &guest_view_manager_factory_);
   gaia_frame_parent_ = "oauth-enroll-auth-view";
   authenticator_id_ = "$('enterprise-enrollment').authenticator_";
 }
@@ -867,32 +858,14 @@
 
 void SAMLEnrollmentTest::StartSamlAndWaitForIdpPageLoad(
     const std::string& gaia_email) {
-  WaitForSigninScreen();
-  ExistingUserController::current_controller()->OnStartEnterpriseEnrollment();
-  while (!GetEnrollmentContents()) {
-    GetGuestViewManager()->WaitForNextGuestCreated();
-  }
-  // Wait for Gaia is ready.
-  OobeBaseTest::WaitForGaiaPageEvent("backButton");
+  LoginDisplayHost::default_host()->StartWizard(
+      EnrollmentScreenView::kScreenId);
+  WaitForGaiaPageBackButtonUpdate();
   SigninFrameJS().TypeIntoPath(gaia_email, {"identifier"});
   SigninFrameJS().TapOn("nextButton");
   OobeBaseTest::WaitForGaiaPageEvent("authFlowChange");
 }
 
-guest_view::TestGuestViewManager* SAMLEnrollmentTest::GetGuestViewManager() {
-  return static_cast<guest_view::TestGuestViewManager*>(
-      guest_view::TestGuestViewManager::FromBrowserContext(
-          ProfileHelper::GetSigninProfile()));
-}
-
-content::WebContents* SAMLEnrollmentTest::GetEnrollmentContents() {
-  content::RenderFrameHost* frame_host =
-      signin::GetAuthFrame(GetLoginUI()->GetWebContents(), gaia_frame_parent_);
-  if (!frame_host)
-    return nullptr;
-  return content::WebContents::FromRenderFrameHost(frame_host);
-}
-
 IN_PROC_BROWSER_TEST_F(SAMLEnrollmentTest, WithoutCredentialsPassingAPI) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   StartSamlAndWaitForIdpPageLoad(kFirstSAMLUserEmail);
@@ -1106,7 +1079,6 @@
 }
 
 void SAMLPolicyTest::ShowGAIALoginForm() {
-  login_screen_load_observer_->Wait();
   content::DOMMessageQueue message_queue;
   ASSERT_TRUE(content::ExecuteScript(
       GetLoginUI()->GetWebContents(),
@@ -1121,7 +1093,6 @@
 }
 
 void SAMLPolicyTest::ShowSAMLInterstitial() {
-  login_screen_load_observer_->Wait();
   WaitForOobeUI();
 
   std::string js =
@@ -1151,8 +1122,6 @@
 }
 
 void SAMLPolicyTest::ClickNextOnSAMLInterstitialPage() {
-  login_screen_load_observer_->Wait();
-
   content::DOMMessageQueue message_queue;
   SetupAuthFlowChangeListener();
 
@@ -1168,8 +1137,6 @@
 }
 
 void SAMLPolicyTest::ClickChangeAccountOnSAMLInterstitialPage() {
-  login_screen_load_observer_->Wait();
-
   std::string js =
       "$('gaia-signin').authenticator_.addEventListener('ready', function() {"
       "  window.domAutomationController.send('ready');"
@@ -1253,7 +1220,6 @@
 // Verifies that the offline login time limit does not affect a user who
 // authenticated without SAML.
 IN_PROC_BROWSER_TEST_F(SAMLPolicyTestWebUILogin, NoSAML) {
-  login_screen_load_observer_->Wait();
   // Verify that offline login is allowed.
   test::OobeJS().ExpectTrue(
       "window.getComputedStyle(document.querySelector("
@@ -1270,7 +1236,6 @@
 // Verifies that when no offline login time limit is set, a user who
 // authenticated with SAML is allowed to log in offline.
 IN_PROC_BROWSER_TEST_F(SAMLPolicyTestWebUILogin, SAMLNoLimit) {
-  login_screen_load_observer_->Wait();
   // Verify that offline login is allowed.
   test::OobeJS().ExpectTrue(
       "window.getComputedStyle(document.querySelector("
@@ -1287,7 +1252,6 @@
 // Verifies that when the offline login time limit is exceeded for a user who
 // authenticated via SAML, that user is forced to log in online the next time.
 IN_PROC_BROWSER_TEST_F(SAMLPolicyTestWebUILogin, SAMLZeroLimit) {
-  login_screen_load_observer_->Wait();
   // Verify that offline login is not allowed.
   test::OobeJS().ExpectTrue(
       "window.getComputedStyle(document.querySelector("
diff --git a/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc b/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
index 7142308..641a1be 100644
--- a/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
@@ -147,8 +147,7 @@
   WaitForScreenExit();
 }
 
-// TODO(https://crbug.com/1009916): Fix flakes and re-enable.
-IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, DISABLED_FingerprintDisabled) {
+IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintDisabled) {
   quick_unlock::EnabledForTesting(false);
   fingerprint_setup_screen_->Show();
 
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
index c1feaaf1..320c478 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen_browsertest.cc
@@ -161,9 +161,7 @@
 
 // Test that if there is no Bluetooth device connected on HID screen, the
 // Bluetooth adapter should be disabled after advancing to the next screen.
-// Flaky. https://crbug.com/1010866
-IN_PROC_BROWSER_TEST_F(HIDDetectionScreenTest,
-                       DISABLED_NoBluetoothDeviceConnected) {
+IN_PROC_BROWSER_TEST_F(HIDDetectionScreenTest, NoBluetoothDeviceConnected) {
   OobeScreenWaiter(HIDDetectionView::kScreenId).Wait();
   EXPECT_TRUE(adapter()->IsPowered());
 
diff --git a/chrome/browser/chromeos/login/screens/welcome_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/welcome_screen_browsertest.cc
index ccd0d909..1742e58 100644
--- a/chrome/browser/chromeos/login/screens/welcome_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/welcome_screen_browsertest.cc
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+
+#include "base/test/scoped_path_override.h"
+#include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
@@ -11,11 +15,17 @@
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
+#include "chrome/browser/chromeos/login/test/test_predicate_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.h"
+#include "chromeos/constants/chromeos_paths.h"
 #include "chromeos/dbus/constants/dbus_switches.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 
@@ -23,6 +33,14 @@
 
 namespace {
 
+const char kStartupManifest[] =
+    R"({
+      "version": "1.0",
+      "initial_locale" : "en-US",
+      "initial_timezone" : "US/Pacific",
+      "keyboard_layout" : "xkb:us::eng",
+    })";
+
 chromeos::OobeUI* GetOobeUI() {
   auto* host = chromeos::LoginDisplayHost::default_host();
   return host ? host->GetOobeUI() : nullptr;
@@ -45,6 +63,27 @@
   js.CreateWaiter(feature_toggle)->Wait();
 }
 
+class LanguageReloadObserver : public WelcomeScreen::Observer {
+ public:
+  explicit LanguageReloadObserver(WelcomeScreen* welcome_screen)
+      : welcome_screen_(welcome_screen) {
+    welcome_screen_->AddObserver(this);
+  }
+
+  // WelcomeScreen::Observer:
+  void OnLanguageListReloaded() override { run_loop_.Quit(); }
+
+  void Wait() { run_loop_.Run(); }
+
+  ~LanguageReloadObserver() override { welcome_screen_->RemoveObserver(this); }
+
+ private:
+  WelcomeScreen* const welcome_screen_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(LanguageReloadObserver);
+};
+
 }  // namespace
 
 class WelcomeScreenBrowserTest : public InProcessBrowserTest {
@@ -55,8 +94,18 @@
   // InProcessBrowserTest:
 
   void SetUpOnMainThread() override {
-    ShowLoginWizard(OobeScreen::SCREEN_TEST_NO_WINDOW);
+    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+    base::FilePath startup_manifest =
+        data_dir_.GetPath().AppendASCII("startup_manifest.json");
+    base::WriteFile(startup_manifest, kStartupManifest,
+                    strlen(kStartupManifest));
+    path_override_ = std::make_unique<base::ScopedPathOverride>(
+        chromeos::FILE_STARTUP_CUSTOMIZATION_MANIFEST, startup_manifest);
 
+    ShowLoginWizard(OobeScreen::SCREEN_TEST_NO_WINDOW);
+    test::TestPredicateWaiter(base::BindRepeating([]() {
+      return WizardController::default_controller() != nullptr;
+    })).Wait();
     WizardController::default_controller()
         ->screen_manager()
         ->DeleteScreenForTesting(WelcomeView::kScreenId);
@@ -65,12 +114,18 @@
         base::BindRepeating(&WelcomeScreenBrowserTest::OnWelcomeScreenExit,
                             base::Unretained(this)));
     welcome_screen_ = welcome_screen.get();
+    observer_ = std::make_unique<LanguageReloadObserver>(welcome_screen_);
     WizardController::default_controller()
         ->screen_manager()
         ->SetScreenForTesting(std::move(welcome_screen));
     InProcessBrowserTest::SetUpOnMainThread();
   }
 
+  void TearDownOnMainThread() override {
+    observer_.reset();
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
   void WaitForScreenExit() {
     if (screen_exit_)
       return;
@@ -87,8 +142,11 @@
   }
 
   WelcomeScreen* welcome_screen_ = nullptr;
+  std::unique_ptr<LanguageReloadObserver> observer_;
 
  private:
+  std::unique_ptr<base::ScopedPathOverride> path_override_;
+  base::ScopedTempDir data_dir_;
   bool screen_exit_ = false;
 
   base::OnceClosure screen_exit_callback_;
@@ -314,14 +372,35 @@
   ASSERT_FALSE(MagnificationManager::Get()->IsMagnifierEnabled());
 }
 
-// Flaky. http://crbug.com/1010676
-IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest,
-                       DISABLED_A11yDockedMagnifierDisabled) {
+IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, A11yDockedMagnifierDisabled) {
   welcome_screen_->Show();
   OobeScreenWaiter(WelcomeView::kScreenId).Wait();
   test::OobeJS().ExpectHiddenPath({"connect", "dockedMagnifierOobeOption"});
 }
 
+IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, PRE_SelectedLanguage) {
+  EXPECT_EQ(
+      StartupCustomizationDocument::GetInstance()->initial_locale_default(),
+      "en-US");
+  welcome_screen_->Show();
+  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
+  const std::string locale = "ru";
+  welcome_screen_->SetApplicationLocale(locale);
+  test::OobeJS().TapOnPath({"connect", "welcomeScreen", "welcomeNextButton"});
+  WaitForScreenExit();
+  EXPECT_EQ(g_browser_process->local_state()->GetString(
+                language::prefs::kApplicationLocale),
+            locale);
+}
+
+IN_PROC_BROWSER_TEST_F(WelcomeScreenBrowserTest, SelectedLanguage) {
+  observer_->Wait();
+  const std::string locale = "ru";
+  EXPECT_EQ(g_browser_process->local_state()->GetString(
+                language::prefs::kApplicationLocale),
+            locale);
+}
+
 IN_PROC_BROWSER_TEST_F(WelcomeScreenWithExperimentalAccessibilityFeaturesTest,
                        A11yDockedMagnifierEnabled) {
   welcome_screen_->Show();
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index 14243e0..083d99a 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -304,11 +304,6 @@
   }
 
   void LoginAsExistingUser() {
-    content::WindowedNotificationObserver(
-        chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-        content::NotificationService::AllSources())
-        .Wait();
-
     test::OobeJS().ExpectTrue("!!document.querySelector('#account-picker')");
     test::OobeJS().ExpectTrue("!!document.querySelector('#pod-row')");
 
@@ -572,11 +567,6 @@
 IN_PROC_BROWSER_TEST_F(OAuth2Test, DISABLED_MergeSession) {
   SimulateNetworkOnline();
 
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources())
-      .Wait();
-
   test::OobeJS().ExpectTrue("!!document.querySelector('#account-picker')");
   test::OobeJS().ExpectTrue("!!document.querySelector('#pod-row')");
 
@@ -610,12 +600,6 @@
   SetupGaiaServerForUnexpiredAccount();
   SimulateNetworkOnline();
 
-  // Waits for login screen to be ready.
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources())
-      .Wait();
-
   // Blocks database thread to control TokenService::LoadCredentials timing.
   // TODO(achuith): Fix this. crbug.com/753615.
   auto thread_blocker = std::make_unique<ThreadBlocker>(nullptr);
@@ -730,12 +714,6 @@
   SetupGaiaServerForUnexpiredAccount();
   SimulateNetworkOnline();
 
-  // Waits for login screen to be ready.
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources())
-      .Wait();
-
   // Signs in as the existing user created in pre test.
   ExistingUserController* const controller =
       ExistingUserController::current_controller();
diff --git a/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc b/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc
index d05ca1754..3d190431 100644
--- a/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc
+++ b/chrome/browser/chromeos/login/signin_partition_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/test_utils.h"
 #include "content/public/test/web_contents_tester.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/network_isolation_key.h"
 #include "net/cookies/cookie_store.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_transaction_factory.h"
@@ -52,8 +53,9 @@
                                             ->GetSession()
                                             ->http_auth_cache();
   http_auth_cache->Add(GURL(kEmbedderUrl), net::HttpAuth::AUTH_SERVER, "",
-                       net::HttpAuth::AUTH_SCHEME_BASIC, "",
-                       net::AuthCredentials(), "");
+                       net::HttpAuth::AUTH_SCHEME_BASIC,
+                       net::NetworkIsolationKey(), "", net::AuthCredentials(),
+                       "");
 }
 
 void IsEntryInHttpAuthCache(network::NetworkContext* network_context,
@@ -64,7 +66,8 @@
                                             ->http_auth_cache();
   *out_entry_found =
       http_auth_cache->Lookup(GURL(kEmbedderUrl), net::HttpAuth::AUTH_SERVER,
-                              "", net::HttpAuth::AUTH_SCHEME_BASIC) != nullptr;
+                              "", net::HttpAuth::AUTH_SCHEME_BASIC,
+                              net::NetworkIsolationKey()) != nullptr;
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/login/sync_consent_interactive_ui_test.cc b/chrome/browser/chromeos/login/sync_consent_interactive_ui_test.cc
index b65b787..7fc4e60 100644
--- a/chrome/browser/chromeos/login/sync_consent_interactive_ui_test.cc
+++ b/chrome/browser/chromeos/login/sync_consent_interactive_ui_test.cc
@@ -114,13 +114,6 @@
     OobeBaseTest::TearDownOnMainThread();
   }
 
-  void WaitForUIToLoad() {
-    content::WindowedNotificationObserver observer(
-        chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-        content::NotificationService::AllSources());
-    observer.Wait();
-  }
-
   void SwitchLanguage(const std::string& language) {
     const char get_num_reloads[] = "Oobe.getInstance().reloadContentNumEvents_";
     const int prev_reloads = test::OobeJS().GetInt(get_num_reloads);
@@ -212,7 +205,6 @@
 };
 
 IN_PROC_BROWSER_TEST_F(SyncConsentTest, SyncConsentRecorder) {
-  WaitForUIToLoad();
   EXPECT_EQ(g_browser_process->GetApplicationLocale(), "en-US");
   LoginToSyncConsentScreen();
   // For En-US we hardcode strings here to catch string issues too.
@@ -245,7 +237,6 @@
 IN_PROC_BROWSER_TEST_P(SyncConsentTestWithParams, SyncConsentTestWithLocale) {
   LOG(INFO) << "SyncConsentTestWithParams() started with param='" << GetParam()
             << "'";
-  WaitForUIToLoad();
   EXPECT_EQ(g_browser_process->GetApplicationLocale(), "en-US");
   SwitchLanguage(GetParam());
   LoginToSyncConsentScreen();
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.cc b/chrome/browser/chromeos/login/test/oobe_base_test.cc
index 0a00e46..096aa6e 100644
--- a/chrome/browser/chromeos/login/test/oobe_base_test.cc
+++ b/chrome/browser/chromeos/login/test/oobe_base_test.cc
@@ -103,6 +103,7 @@
           run_loop.QuitClosure())) {
     run_loop.Run();
   }
+  MaybeWaitForLoginScreenLoad();
 }
 
 void OobeBaseTest::WaitForGaiaPageLoad() {
@@ -159,7 +160,7 @@
 
   WizardController::SkipPostLoginScreensForTesting();
 
-  login_screen_load_observer_->Wait();
+  MaybeWaitForLoginScreenLoad();
 }
 
 test::JSChecker OobeBaseTest::SigninFrameJS() {
@@ -172,4 +173,11 @@
   return result;
 }
 
+void OobeBaseTest::MaybeWaitForLoginScreenLoad() {
+  if (!login_screen_load_observer_)
+    return;
+  login_screen_load_observer_->Wait();
+  login_screen_load_observer_.reset();
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.h b/chrome/browser/chromeos/login/test/oobe_base_test.h
index 71deb2b..9dff976 100644
--- a/chrome/browser/chromeos/login/test/oobe_base_test.h
+++ b/chrome/browser/chromeos/login/test/oobe_base_test.h
@@ -58,13 +58,18 @@
   // is set before SetUpCommandLine is invoked.
   bool needs_background_networking_ = false;
 
-  std::unique_ptr<content::WindowedNotificationObserver>
-      login_screen_load_observer_;
   std::string gaia_frame_parent_ = "signin-frame";
   std::string authenticator_id_ = "$('gaia-signin').authenticator_";
   EmbeddedTestServerSetupMixin embedded_test_server_{&mixin_host_,
                                                      embedded_test_server()};
 
+ private:
+  // Waits for login_screen_load_observer_ and resets it afterwards.
+  void MaybeWaitForLoginScreenLoad();
+
+  std::unique_ptr<content::WindowedNotificationObserver>
+      login_screen_load_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(OobeBaseTest);
 };
 
diff --git a/chrome/browser/chromeos/login/test/oobe_screens_utils.cc b/chrome/browser/chromeos/login/test/oobe_screens_utils.cc
index 47dae1d..53f78580 100644
--- a/chrome/browser/chromeos/login/test/oobe_screens_utils.cc
+++ b/chrome/browser/chromeos/login/test/oobe_screens_utils.cc
@@ -42,10 +42,6 @@
 }  // namespace
 
 void WaitForWelcomeScreen() {
-  content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources());
-  observer.Wait();
   WaitFor(WelcomeView::kScreenId);
 }
 
diff --git a/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
index 5edcf48..176b25e9 100644
--- a/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
@@ -84,9 +84,6 @@
 
 IN_PROC_BROWSER_TEST_F(ForceMaximizeOnFirstRunTest, TwoRuns) {
   SetUpResolution();
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources()).Wait();
   LogIn(kAccountId, kAccountPassword, kEmptyServices);
 
   const Browser* const browser = OpenNewBrowserWindow();
diff --git a/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc b/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
index 1b290c7..1073577 100644
--- a/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
+++ b/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
@@ -85,9 +85,6 @@
 
 // Verify that the policies are honored on an existing user's login.
 IN_PROC_BROWSER_TEST_F(RestoreOnStartupTestChromeOS, LogInAndVerify) {
-  content::WindowedNotificationObserver(
-      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
-      content::NotificationService::AllSources()).Wait();
   LogInAndVerifyStartUpURLs();
 }
 
diff --git a/chromeos/dbus/fake_wilco_dtc_supportd_client.cc b/chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.cc
similarity index 96%
rename from chromeos/dbus/fake_wilco_dtc_supportd_client.cc
rename to chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.cc
index 78ca233..2d0f94c 100644
--- a/chromeos/dbus/fake_wilco_dtc_supportd_client.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.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 "chromeos/dbus/fake_wilco_dtc_supportd_client.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h"
 
 #include <utility>
 
diff --git a/chromeos/dbus/fake_wilco_dtc_supportd_client.h b/chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h
similarity index 82%
rename from chromeos/dbus/fake_wilco_dtc_supportd_client.h
rename to chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h
index 94d71d8e..a56d26a 100644
--- a/chromeos/dbus/fake_wilco_dtc_supportd_client.h
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h
@@ -2,21 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_DBUS_FAKE_WILCO_DTC_SUPPORTD_CLIENT_H_
-#define CHROMEOS_DBUS_FAKE_WILCO_DTC_SUPPORTD_CLIENT_H_
+#ifndef CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_FAKE_WILCO_DTC_SUPPORTD_CLIENT_H_
+#define CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_FAKE_WILCO_DTC_SUPPORTD_CLIENT_H_
 
 #include <vector>
 
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
-#include "chromeos/dbus/wilco_dtc_supportd_client.h"
 
 namespace chromeos {
 
-class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeWilcoDtcSupportdClient final
-    : public WilcoDtcSupportdClient {
+class FakeWilcoDtcSupportdClient final : public WilcoDtcSupportdClient {
  public:
   FakeWilcoDtcSupportdClient();
   ~FakeWilcoDtcSupportdClient() override;
@@ -60,4 +59,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROMEOS_DBUS_FAKE_WILCO_DTC_SUPPORTD_CLIENT_H_
+#endif  // CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_FAKE_WILCO_DTC_SUPPORTD_CLIENT_H_
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc b/chrome/browser/chromeos/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc
index 1261b3e..937d8a3 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc
@@ -15,11 +15,10 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/fake_wilco_dtc_supportd_client.h"
-#include "chromeos/dbus/wilco_dtc_supportd_client.h"
 
 namespace chromeos {
 
@@ -130,9 +129,8 @@
 };
 
 FakeWilcoDtcSupportdClient* GetFakeDbusWilcoDtcSupportdClient() {
-  DCHECK(DBusThreadManager::Get()->IsUsingFakes());
   WilcoDtcSupportdClient* const wilco_dtc_supportd_client =
-      DBusThreadManager::Get()->GetWilcoDtcSupportdClient();
+      WilcoDtcSupportdClient::Get();
   DCHECK(wilco_dtc_supportd_client);
   return static_cast<FakeWilcoDtcSupportdClient*>(wilco_dtc_supportd_client);
 }
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
index bc50074..f3454816 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.cc
@@ -15,11 +15,11 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/mojo_utils.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/wilco_dtc_supportd_client.h"
 #include "mojo/public/cpp/bindings/interface_ptr_info.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
@@ -160,11 +160,9 @@
   // ScheduleWaitingForDBusService().
   dbus_waiting_weak_ptr_factory_.InvalidateWeakPtrs();
 
-  DBusThreadManager::Get()
-      ->GetWilcoDtcSupportdClient()
-      ->WaitForServiceToBeAvailable(
-          base::BindOnce(&WilcoDtcSupportdBridge::OnWaitedForDBusService,
-                         dbus_waiting_weak_ptr_factory_.GetWeakPtr()));
+  chromeos::WilcoDtcSupportdClient::Get()->WaitForServiceToBeAvailable(
+      base::BindOnce(&WilcoDtcSupportdBridge::OnWaitedForDBusService,
+                     dbus_waiting_weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WilcoDtcSupportdBridge::ScheduleWaitingForDBusService() {
@@ -221,12 +219,10 @@
 
   // Send the file descriptor with the Mojo message pipe's remote endpoint to
   // the wilco_dtc_supportd daemon via the D-Bus.
-  DBusThreadManager::Get()
-      ->GetWilcoDtcSupportdClient()
-      ->BootstrapMojoConnection(
-          std::move(remote_endpoint_fd),
-          base::BindOnce(&WilcoDtcSupportdBridge::OnBootstrappedMojoConnection,
-                         weak_ptr_factory_.GetWeakPtr()));
+  chromeos::WilcoDtcSupportdClient::Get()->BootstrapMojoConnection(
+      std::move(remote_endpoint_fd),
+      base::BindOnce(&WilcoDtcSupportdBridge::OnBootstrappedMojoConnection,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WilcoDtcSupportdBridge::OnBootstrappedMojoConnection(bool success) {
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge_unittest.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge_unittest.cc
index b186ae2..f91771a 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge_unittest.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge_unittest.cc
@@ -12,12 +12,11 @@
 #include "base/optional.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_bridge.h"
 #include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/fake_wilco_dtc_supportd_client.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -173,8 +172,7 @@
 class WilcoDtcSupportdBridgeTest : public testing::Test {
  protected:
   WilcoDtcSupportdBridgeTest() {
-    DBusThreadManager::Initialize();
-    CHECK(DBusThreadManager::Get()->IsUsingFakes());
+    WilcoDtcSupportdClient::InitializeFake();
 
     auto profile_manager = std::make_unique<TestingProfileManager>(
         TestingBrowserProcess::GetGlobal());
@@ -198,7 +196,7 @@
 
   ~WilcoDtcSupportdBridgeTest() override {
     wilco_dtc_supportd_bridge_.reset();
-    DBusThreadManager::Shutdown();
+    WilcoDtcSupportdClient::Shutdown();
   }
 
   StrictMock<MockWilcoDtcSupportdNotificationController>*
@@ -212,7 +210,7 @@
 
   FakeWilcoDtcSupportdClient* wilco_dtc_supportd_dbus_client() {
     WilcoDtcSupportdClient* const wilco_dtc_supportd_client =
-        DBusThreadManager::Get()->GetWilcoDtcSupportdClient();
+        WilcoDtcSupportdClient::Get();
     DCHECK(wilco_dtc_supportd_client);
     return static_cast<FakeWilcoDtcSupportdClient*>(wilco_dtc_supportd_client);
   }
diff --git a/chromeos/dbus/wilco_dtc_supportd_client.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.cc
similarity index 68%
rename from chromeos/dbus/wilco_dtc_supportd_client.cc
rename to chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.cc
index a20ded3..6e726518 100644
--- a/chromeos/dbus/wilco_dtc_supportd_client.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.cc
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/dbus/wilco_dtc_supportd_client.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 
 #include <utility>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/fake_wilco_dtc_supportd_client.h"
+#include "chrome/common/chrome_features.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -16,6 +19,8 @@
 
 namespace {
 
+WilcoDtcSupportdClient* g_instance = nullptr;
+
 void OnVoidDBusMethod(VoidDBusMethodCallback callback,
                       dbus::Response* response) {
   std::move(callback).Run(response != nullptr);
@@ -32,9 +37,6 @@
       WaitForServiceToBeAvailableCallback callback) override;
   void BootstrapMojoConnection(base::ScopedFD fd,
                                VoidDBusMethodCallback callback) override;
-
- protected:
-  // WilcoDtcSupportdClient overrides:
   void Init(dbus::Bus* bus) override;
 
  private:
@@ -75,11 +77,46 @@
 
 }  // namespace
 
-// static
-std::unique_ptr<WilcoDtcSupportdClient> WilcoDtcSupportdClient::Create() {
-  return std::make_unique<WilcoDtcSupportdClientImpl>();
+WilcoDtcSupportdClient::WilcoDtcSupportdClient() {
+  DCHECK(!g_instance);
+  g_instance = this;
 }
 
-WilcoDtcSupportdClient::WilcoDtcSupportdClient() = default;
+WilcoDtcSupportdClient::~WilcoDtcSupportdClient() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
+
+// static
+void WilcoDtcSupportdClient::Initialize(dbus::Bus* bus) {
+  DCHECK(bus);
+#if defined(OS_CHROMEOS)
+  if (base::FeatureList::IsEnabled(::features::kWilcoDtc)) {
+    (new WilcoDtcSupportdClientImpl())->Init(bus);
+  }
+#endif
+}
+
+// static
+void WilcoDtcSupportdClient::InitializeFake() {
+  new FakeWilcoDtcSupportdClient();
+}
+
+// static
+void WilcoDtcSupportdClient::Shutdown() {
+  if (g_instance)
+    delete g_instance;
+}
+
+// static
+bool WilcoDtcSupportdClient::IsInitialized() {
+  return g_instance;
+}
+
+// static
+WilcoDtcSupportdClient* WilcoDtcSupportdClient::Get() {
+  CHECK(IsInitialized());
+  return g_instance;
+}
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h
new file mode 100644
index 0000000..98b0816
--- /dev/null
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_WILCO_DTC_SUPPORTD_CLIENT_H_
+#define CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_WILCO_DTC_SUPPORTD_CLIENT_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "chromeos/dbus/dbus_client.h"
+#include "chromeos/dbus/dbus_method_call_status.h"
+#include "dbus/object_proxy.h"
+
+namespace chromeos {
+
+class WilcoDtcSupportdClient : public DBusClient {
+ public:
+  // Creates and initializes the global instance. |bus| must not be null.
+  static void Initialize(dbus::Bus* bus);
+  // Creates and initializes a fake global instance if not already created.
+  static void InitializeFake();
+  // Destroys the global instance which must have been initialized.
+  static void Shutdown();
+  // Checks if initialization was performed
+  static bool IsInitialized();
+  // Returns the global instance if initialized.
+  static WilcoDtcSupportdClient* Get();
+
+  // Registers |callback| to run when the wilco_dtc_supportd service becomes
+  // available.
+  virtual void WaitForServiceToBeAvailable(
+      WaitForServiceToBeAvailableCallback callback) = 0;
+
+  // Bootstrap the Mojo connection between Chrome and the wilco_dtc_supportd
+  // daemon. |fd| is the file descriptor with the child end of the Mojo pipe.
+  virtual void BootstrapMojoConnection(base::ScopedFD fd,
+                                       VoidDBusMethodCallback callback) = 0;
+
+ protected:
+  // Create() should be used instead.
+  WilcoDtcSupportdClient();
+  ~WilcoDtcSupportdClient() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WilcoDtcSupportdClient);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_WILCO_DTC_SUPPORTD_WILCO_DTC_SUPPORTD_CLIENT_H_
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc
index 890e4d2e..b146978 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_manager_unittest.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/upstart/fake_upstart_client.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
@@ -96,11 +96,13 @@
 class WilcoDtcSupportdManagerTest : public testing::Test {
  protected:
   WilcoDtcSupportdManagerTest() {
-    DBusThreadManager::Initialize();
+    WilcoDtcSupportdClient::InitializeFake();
     upstart_client_ = std::make_unique<TestUpstartClient>();
   }
 
-  ~WilcoDtcSupportdManagerTest() override { DBusThreadManager::Shutdown(); }
+  ~WilcoDtcSupportdManagerTest() override {
+    WilcoDtcSupportdClient::Shutdown();
+  }
 
   std::unique_ptr<WilcoDtcSupportdManager::Delegate> CreateDelegate() {
     return std::make_unique<FakeWilcoDtcSupportdManagerDelegate>(
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging_unittest.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging_unittest.cc
index bba8a1d..19e2693 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging_unittest.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging_unittest.cc
@@ -15,9 +15,9 @@
 #include "base/test/task_environment.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/mojo_utils.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h"
 #include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "extensions/browser/api/messaging/native_message_host.h"
 #include "mojo/public/cpp/system/handle.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -126,7 +126,7 @@
 class WilcoDtcSupportdMessagingOpenedByExtensionTest : public testing::Test {
  protected:
   WilcoDtcSupportdMessagingOpenedByExtensionTest() {
-    DBusThreadManager::Initialize();
+    WilcoDtcSupportdClient::InitializeFake();
     testing_wilco_dtc_supportd_bridge_wrapper_ =
         TestingWilcoDtcSupportdBridgeWrapper::Create(
             &mojo_wilco_dtc_supportd_service_,
@@ -140,7 +140,7 @@
     // DBusThreadManager is shut down, since the WilcoDtcSupportdBridge class
     // uses the latter.
     wilco_dtc_supportd_bridge_.reset();
-    DBusThreadManager::Shutdown();
+    WilcoDtcSupportdClient::Shutdown();
   }
 
   MockMojoWilcoDtcSupportdService* mojo_wilco_dtc_supportd_service() {
diff --git a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
index 66198b0..fbd01ce 100644
--- a/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
+++ b/chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_notification_controller_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_client.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e15cf918..ead7d2a 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -303,12 +303,12 @@
   {
     "name": "autofill-profile-client-validation",
     "owners": [ "parastoog" ],
-    "expiry_milestone": 77
+    "expiry_milestone": 79
   },
   {
     "name": "autofill-profile-server-validation",
     "owners": [ "parastoog" ],
-    "expiry_milestone": 77
+    "expiry_milestone": 79
   },
   {
     "name": "autofill-prune-suggestions",
@@ -332,13 +332,13 @@
   },
   {
     "name": "autofill-use-improved-label-disambiguation",
-    "owners": [ "ftirelo", "tmartino" ],
-    "expiry_milestone": 77
+    "owners": [ "fhorschig" ],
+    "expiry_milestone": 81
   },
   {
     "name": "autofill-use-mobile-label-disambiguation",
-    "owners": [ "ftirelo", "tmartino" ],
-    "expiry_milestone": 79
+    "owners": [ "fhorschig" ],
+    "expiry_milestone": 81
   },
   {
     "name": "back-forward-cache",
@@ -1741,8 +1741,8 @@
   },
   {
     "name": "enable-suggestions-with-substring-match",
-    "owners": [ "tmartino" ],
-    "expiry_milestone": 77
+    "owners": [ "fhorschig" ],
+    "expiry_milestone": 81
   },
   {
     "name": "enable-surfacecontrol",
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index f9cc8aff..afec830 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -573,6 +573,9 @@
   network_service->SetUpHttpAuth(CreateHttpAuthStaticParams(local_state_));
   network_service->ConfigureHttpAuthPrefs(
       CreateHttpAuthDynamicParams(local_state_));
+  network_service->SetSplitAuthCacheByNetworkIsolationKey(
+      base::FeatureList::IsEnabled(
+          features::kSplitAuthCacheByNetworkIsolationKey));
 
   // TODO(lukasza): https://crbug.com/944162: Once
   // kMimeHandlerViewInCrossProcessFrame feature ships, unconditionally include
diff --git a/chrome/browser/offline_pages/offline_page_utils_unittest.cc b/chrome/browser/offline_pages/offline_page_utils_unittest.cc
index 1e42d6e..7daf029 100644
--- a/chrome/browser/offline_pages/offline_page_utils_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_utils_unittest.cc
@@ -13,8 +13,11 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/string16.h"
+#include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -45,7 +48,6 @@
 #include "url/gurl.h"
 
 #if defined(OS_ANDROID)
-#include "base/test/bind_test_util.h"
 #include "base/test/test_timeouts.h"
 #include "chrome/browser/download/android/mock_download_controller.h"
 #include "components/gcm_driver/instance_id/instance_id_android.h"
@@ -65,21 +67,12 @@
 const char* kTestPage3ClientId = "7890";
 const char* kTestPage4ClientId = "42";
 
-void CheckDuplicateDownloadsCallback(
-    OfflinePageUtils::DuplicateCheckResult* out_result,
-    OfflinePageUtils::DuplicateCheckResult result) {
-  DCHECK(out_result);
-  *out_result = result;
+void RunTasksForDuration(base::TimeDelta delta) {
+  base::RunLoop run_loop;
+  base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), delta);
+  run_loop.Run();
 }
 
-void GetAllRequestsCallback(
-    std::vector<std::unique_ptr<SavePageRequest>>* out_requests,
-    std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  *out_requests = std::move(requests);
-}
-
-void SavePageLaterCallback(AddRequestResult ignored) {}
-
 }  // namespace
 
 class OfflinePageUtilsTest
@@ -92,7 +85,6 @@
 
   void SetUp() override;
   void TearDown() override;
-  void RunUntilIdle();
 
   void SavePage(const GURL& url,
                 const ClientId& client_id,
@@ -102,24 +94,78 @@
   int FindRequestByNamespaceAndURL(const std::string& name_space,
                                    const GURL& url);
 
-  // Necessary callbacks for the offline page model.
-  void OnSavePageDone(SavePageResult result, int64_t offlineId);
-  void OnClearAllDone();
-  void OnExpirePageDone(bool success);
-  void OnGetURLDone(const GURL& url);
-  void OnSizeInBytesCalculated(int64_t size);
+  size_t GetRequestCount() { return GetAllRequests().size(); }
+
+  // Wait until there are at least |min_request_count| requests.
+  void WaitForRequestMinCount(size_t min_request_count) {
+    for (;;) {
+      if (min_request_count <= GetRequestCount()) {
+        break;
+      }
+      RunTasksForDuration(base::TimeDelta::FromMilliseconds(100));
+    }
+  }
+
+  RequestCoordinator* GetRequestCoordinator() {
+    return RequestCoordinatorFactory::GetForBrowserContext(profile());
+  }
+
+  OfflinePageUtils::DuplicateCheckResult CheckDuplicateDownloads(GURL url) {
+    OfflinePageUtils::DuplicateCheckResult result;
+    base::RunLoop run_loop;
+    auto quit = run_loop.QuitClosure();
+    auto on_done = [&](OfflinePageUtils::DuplicateCheckResult check_result) {
+      result = check_result;
+      quit.Run();
+    };
+    OfflinePageUtils::CheckDuplicateDownloads(
+        profile(), url, base::BindLambdaForTesting(on_done));
+
+    run_loop.Run();
+    return result;
+  }
+
+  base::Optional<int64_t> GetCachedOfflinePageSizeBetween(
+      const base::Time& begin_time,
+      const base::Time& end_time) {
+    int64_t result;
+    base::RunLoop run_loop;
+    auto quit = run_loop.QuitClosure();
+    auto on_done = [&](int64_t size) {
+      result = size;
+      quit.Run();
+    };
+    if (!OfflinePageUtils::GetCachedOfflinePageSizeBetween(
+            profile(), base::BindLambdaForTesting(on_done), begin_time,
+            end_time)) {
+      return base::nullopt;
+    }
+    run_loop.Run();
+    return result;
+  }
 
   // OfflinePageTestArchiver::Observer implementation:
-  void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override;
+  void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override {}
 
   TestScopedOfflineClock* clock() { return &clock_; }
   TestingProfile* profile() { return &profile_; }
   content::WebContents* web_contents() const { return web_contents_.get(); }
 
-  int64_t offline_id() const { return offline_id_; }
-  int64_t last_cache_size() { return last_cache_size_; }
-
   void CreateCachedOfflinePages();
+  std::vector<std::unique_ptr<SavePageRequest>> GetAllRequests() {
+    base::RunLoop run_loop;
+    auto quit = run_loop.QuitClosure();
+    std::vector<std::unique_ptr<SavePageRequest>> result;
+    auto on_done = [&](std::vector<std::unique_ptr<SavePageRequest>> requests) {
+      result = std::move(requests);
+      quit.Run();
+    };
+
+    GetRequestCoordinator()->GetAllRequests(
+        base::BindLambdaForTesting(on_done));
+    run_loop.Run();
+    return result;
+  }
 
  private:
   void CreateOfflinePages();
@@ -130,12 +176,9 @@
 
   TestScopedOfflineClock clock_;
   content::BrowserTaskEnvironment task_environment_;
-  int64_t offline_id_;
-  GURL url_;
   TestingProfile profile_;
   std::unique_ptr<content::WebContents> web_contents_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  int64_t last_cache_size_;
 #if defined(OS_ANDROID)
   chrome::android::MockDownloadController download_controller_;
   // OfflinePageTabHelper instantiates PrefetchService which in turn requests a
@@ -159,7 +202,6 @@
   web_contents_ = content::WebContents::Create(
       content::WebContents::CreateParams(profile()));
   OfflinePageTabHelper::CreateForWebContents(web_contents_.get());
-
   // Reset the value of the test clock.
   clock_.SetNow(base::Time::Now());
 
@@ -167,14 +209,14 @@
   OfflinePageModelFactory::GetInstance()->SetTestingFactoryAndUse(
       profile_.GetProfileKey(),
       base::BindRepeating(&BuildTestOfflinePageModel));
-  RunUntilIdle();
 
   RequestCoordinatorFactory::GetInstance()->SetTestingFactoryAndUse(
       &profile_, base::BindRepeating(&BuildTestRequestCoordinator));
-  RunUntilIdle();
 
   // Make sure to create offline pages and requests.
   CreateOfflinePages();
+  // TODO(harringtond): I was surprised this test creates requests in Setup(),
+  // we should avoid this to be less surprising.
   CreateRequests();
 
 // This is needed in order to skip the logic to request storage permission.
@@ -189,10 +231,6 @@
 #endif
 }
 
-void OfflinePageUtilsTest::RunUntilIdle() {
-  base::RunLoop().RunUntilIdle();
-}
-
 void OfflinePageUtilsTest::SavePage(
     const GURL& url,
     const ClientId& client_id,
@@ -200,36 +238,16 @@
   OfflinePageModel::SavePageParams save_page_params;
   save_page_params.url = url;
   save_page_params.client_id = client_id;
+  base::RunLoop run_loop;
+  auto save_page_done = [&](SavePageResult result, int64_t offline_id) {
+    run_loop.QuitClosure().Run();
+  };
   OfflinePageModelFactory::GetForBrowserContext(profile())->SavePage(
       save_page_params, std::move(archiver), web_contents_.get(),
-      base::Bind(&OfflinePageUtilsTest::OnSavePageDone, AsWeakPtr()));
-  RunUntilIdle();
+      base::BindLambdaForTesting(save_page_done));
+  run_loop.Run();
 }
 
-void OfflinePageUtilsTest::OnSavePageDone(SavePageResult result,
-                                          int64_t offline_id) {
-  offline_id_ = offline_id;
-}
-
-void OfflinePageUtilsTest::OnExpirePageDone(bool success) {
-  // Result ignored here.
-}
-
-void OfflinePageUtilsTest::OnClearAllDone() {
-  // Result ignored here.
-}
-
-void OfflinePageUtilsTest::OnGetURLDone(const GURL& url) {
-  url_ = url;
-}
-
-void OfflinePageUtilsTest::OnSizeInBytesCalculated(int64_t size) {
-  last_cache_size_ = size;
-}
-
-void OfflinePageUtilsTest::SetLastPathCreatedByArchiver(
-    const base::FilePath& file_path) {}
-
 void OfflinePageUtilsTest::CreateOfflinePages() {
   // Create page 1.
   std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver(
@@ -247,16 +265,16 @@
 }
 
 void OfflinePageUtilsTest::CreateRequests() {
-  RequestCoordinator* request_coordinator =
-      RequestCoordinatorFactory::GetForBrowserContext(profile());
-
   RequestCoordinator::SavePageLaterParams params;
   params.url = kTestPage3Url;
   params.client_id =
       offline_pages::ClientId(kDownloadNamespace, kTestPage3ClientId);
-  request_coordinator->SavePageLater(params,
-                                     base::Bind(&SavePageLaterCallback));
-  RunUntilIdle();
+  base::RunLoop run_loop;
+  auto quit = run_loop.QuitClosure();
+  auto page_saved = [&](AddRequestResult ignored) { quit.Run(); };
+  GetRequestCoordinator()->SavePageLater(
+      params, base::BindLambdaForTesting(page_saved));
+  run_loop.Run();
 }
 
 void OfflinePageUtilsTest::CreateCachedOfflinePages() {
@@ -308,12 +326,7 @@
 int OfflinePageUtilsTest::FindRequestByNamespaceAndURL(
     const std::string& name_space,
     const GURL& url) {
-  RequestCoordinator* request_coordinator =
-      RequestCoordinatorFactory::GetForBrowserContext(profile());
-  std::vector<std::unique_ptr<SavePageRequest>> requests;
-  request_coordinator->GetAllRequests(
-      base::Bind(&GetAllRequestsCallback, base::Unretained(&requests)));
-  RunUntilIdle();
+  std::vector<std::unique_ptr<SavePageRequest>> requests = GetAllRequests();
 
   int matches = 0;
   for (auto& request : requests) {
@@ -326,31 +339,17 @@
 }
 
 TEST_F(OfflinePageUtilsTest, CheckDuplicateDownloads) {
-  OfflinePageUtils::DuplicateCheckResult result =
-      OfflinePageUtils::DuplicateCheckResult::NOT_FOUND;
-
   // The duplicate page should be found for this.
-  OfflinePageUtils::CheckDuplicateDownloads(
-      profile(), kTestPage1Url,
-      base::Bind(&CheckDuplicateDownloadsCallback, base::Unretained(&result)));
-  RunUntilIdle();
   EXPECT_EQ(OfflinePageUtils::DuplicateCheckResult::DUPLICATE_PAGE_FOUND,
-            result);
+            CheckDuplicateDownloads(kTestPage1Url));
 
   // The duplicate request should be found for this.
-  OfflinePageUtils::CheckDuplicateDownloads(
-      profile(), kTestPage3Url,
-      base::Bind(&CheckDuplicateDownloadsCallback, base::Unretained(&result)));
-  RunUntilIdle();
   EXPECT_EQ(OfflinePageUtils::DuplicateCheckResult::DUPLICATE_REQUEST_FOUND,
-            result);
+            CheckDuplicateDownloads(kTestPage3Url));
 
   // No duplicate should be found for this.
-  OfflinePageUtils::CheckDuplicateDownloads(
-      profile(), kTestPage4Url,
-      base::Bind(&CheckDuplicateDownloadsCallback, base::Unretained(&result)));
-  RunUntilIdle();
-  EXPECT_EQ(OfflinePageUtils::DuplicateCheckResult::NOT_FOUND, result);
+  EXPECT_EQ(OfflinePageUtils::DuplicateCheckResult::NOT_FOUND,
+            CheckDuplicateDownloads(kTestPage4Url));
 }
 
 TEST_F(OfflinePageUtilsTest, ScheduleDownload) {
@@ -359,25 +358,27 @@
   ASSERT_EQ(1, FindRequestByNamespaceAndURL(kDownloadNamespace, kTestPage3Url));
   ASSERT_EQ(0, FindRequestByNamespaceAndURL(kDownloadNamespace, kTestPage4Url));
 
+  // TODO(harringtond): Remove request creation in Setup().
+  size_t request_count_wait = 1;
   // Re-downloading a page with duplicate page found.
   OfflinePageUtils::ScheduleDownload(
       web_contents(), kDownloadNamespace, kTestPage1Url,
       OfflinePageUtils::DownloadUIActionFlags::NONE);
-  RunUntilIdle();
+  WaitForRequestMinCount(++request_count_wait);
   EXPECT_EQ(1, FindRequestByNamespaceAndURL(kDownloadNamespace, kTestPage1Url));
 
   // Re-downloading a page with duplicate request found.
   OfflinePageUtils::ScheduleDownload(
       web_contents(), kDownloadNamespace, kTestPage3Url,
       OfflinePageUtils::DownloadUIActionFlags::NONE);
-  RunUntilIdle();
+  WaitForRequestMinCount(++request_count_wait);
   EXPECT_EQ(2, FindRequestByNamespaceAndURL(kDownloadNamespace, kTestPage3Url));
 
   // Downloading a page with no duplicate found.
   OfflinePageUtils::ScheduleDownload(
       web_contents(), kDownloadNamespace, kTestPage4Url,
       OfflinePageUtils::DownloadUIActionFlags::NONE);
-  RunUntilIdle();
+  WaitForRequestMinCount(++request_count_wait);
   EXPECT_EQ(1, FindRequestByNamespaceAndURL(kDownloadNamespace, kTestPage4Url));
 }
 
@@ -387,7 +388,12 @@
   OfflinePageUtils::ScheduleDownload(
       web_contents(), kDownloadNamespace, kTestPage4Url,
       OfflinePageUtils::DownloadUIActionFlags::NONE);
-  RunUntilIdle();
+
+  // Here, we're waiting to make sure a request is not created. We can't use
+  // QuitClosure, since there's no callback threaded through ScheduleDownload.
+  // Instead, just wait a bit and assume ScheduleDownload is complete.
+  RunTasksForDuration(base::TimeDelta::FromSeconds(1));
+
   EXPECT_EQ(0, FindRequestByNamespaceAndURL(kDownloadNamespace, kTestPage4Url));
 }
 #endif
@@ -400,13 +406,10 @@
   clock()->Advance(base::TimeDelta::FromMinutes(5));
 
   // Get the size of cached offline pages between 01:05:00 and 03:05:00.
-  bool ret = OfflinePageUtils::GetCachedOfflinePageSizeBetween(
-      profile(),
-      base::Bind(&OfflinePageUtilsTest::OnSizeInBytesCalculated, AsWeakPtr()),
-      clock()->Now() - base::TimeDelta::FromHours(2), clock()->Now());
-  RunUntilIdle();
-  EXPECT_TRUE(ret);
-  EXPECT_EQ(kTestFileSize * 2, last_cache_size());
+  EXPECT_EQ(
+      kTestFileSize * 2,
+      GetCachedOfflinePageSizeBetween(
+          clock()->Now() - base::TimeDelta::FromHours(2), clock()->Now()));
 }
 
 TEST_F(OfflinePageUtilsTest, TestGetCachedOfflinePageSizeNoPageInModel) {
@@ -423,13 +426,9 @@
   // Get the size of cached offline pages between 01:00:00 and 03:00:00.
   // Since no temporary pages were added to the model, the cache size should be
   // 0.
-  bool ret = OfflinePageUtils::GetCachedOfflinePageSizeBetween(
-      profile(),
-      base::Bind(&OfflinePageUtilsTest::OnSizeInBytesCalculated, AsWeakPtr()),
-      clock()->Now() - base::TimeDelta::FromHours(2), clock()->Now());
-  RunUntilIdle();
-  EXPECT_TRUE(ret);
-  EXPECT_EQ(0, last_cache_size());
+  EXPECT_EQ(
+      0, GetCachedOfflinePageSizeBetween(
+             clock()->Now() - base::TimeDelta::FromHours(2), clock()->Now()));
 }
 
 TEST_F(OfflinePageUtilsTest, TestGetCachedOfflinePageSizeNoPageInRange) {
@@ -440,13 +439,9 @@
   clock()->Advance(base::TimeDelta::FromMinutes(5));
 
   // Get the size of cached offline pages between 03:04:00 and 03:05:00.
-  bool ret = OfflinePageUtils::GetCachedOfflinePageSizeBetween(
-      profile(),
-      base::Bind(&OfflinePageUtilsTest::OnSizeInBytesCalculated, AsWeakPtr()),
-      clock()->Now() - base::TimeDelta::FromMinutes(1), clock()->Now());
-  RunUntilIdle();
-  EXPECT_TRUE(ret);
-  EXPECT_EQ(0, last_cache_size());
+  EXPECT_EQ(
+      0, GetCachedOfflinePageSizeBetween(
+             clock()->Now() - base::TimeDelta::FromMinutes(1), clock()->Now()));
 }
 
 TEST_F(OfflinePageUtilsTest, TestGetCachedOfflinePageSizeAllPagesInRange) {
@@ -457,13 +452,10 @@
   clock()->Advance(base::TimeDelta::FromHours(20));
 
   // Get the size of cached offline pages between -01:00:00 and 23:00:00.
-  bool ret = OfflinePageUtils::GetCachedOfflinePageSizeBetween(
-      profile(),
-      base::Bind(&OfflinePageUtilsTest::OnSizeInBytesCalculated, AsWeakPtr()),
-      clock()->Now() - base::TimeDelta::FromHours(24), clock()->Now());
-  RunUntilIdle();
-  EXPECT_TRUE(ret);
-  EXPECT_EQ(kTestFileSize * 4, last_cache_size());
+  EXPECT_EQ(
+      kTestFileSize * 4,
+      GetCachedOfflinePageSizeBetween(
+          clock()->Now() - base::TimeDelta::FromHours(24), clock()->Now()));
 }
 
 TEST_F(OfflinePageUtilsTest, TestGetCachedOfflinePageSizeAllPagesInvalidRange) {
@@ -476,12 +468,8 @@
   // Get the size of cached offline pages between 23:00:00 and -01:00:00, which
   // is an invalid range, the return value will be false and there will be no
   // callback.
-  bool ret = OfflinePageUtils::GetCachedOfflinePageSizeBetween(
-      profile(),
-      base::Bind(&OfflinePageUtilsTest::OnSizeInBytesCalculated, AsWeakPtr()),
-      clock()->Now(), clock()->Now() - base::TimeDelta::FromHours(24));
-  RunUntilIdle();
-  EXPECT_FALSE(ret);
+  EXPECT_FALSE(GetCachedOfflinePageSizeBetween(
+      clock()->Now(), clock()->Now() - base::TimeDelta::FromHours(24)));
 }
 
 TEST_F(OfflinePageUtilsTest, TestGetCachedOfflinePageSizeEdgeCase) {
@@ -491,13 +479,10 @@
   // Get the size of cached offline pages between 02:00:00 and 03:00:00, since
   // we are using a [begin_time, end_time) range so there will be only 1 page
   // when query for this time range.
-  bool ret = OfflinePageUtils::GetCachedOfflinePageSizeBetween(
-      profile(),
-      base::Bind(&OfflinePageUtilsTest::OnSizeInBytesCalculated, AsWeakPtr()),
-      clock()->Now() - base::TimeDelta::FromHours(1), clock()->Now());
-  RunUntilIdle();
-  EXPECT_TRUE(ret);
-  EXPECT_EQ(kTestFileSize * 1, last_cache_size());
+  EXPECT_EQ(
+      kTestFileSize * 1,
+      GetCachedOfflinePageSizeBetween(
+          clock()->Now() - base::TimeDelta::FromHours(1), clock()->Now()));
 }
 
 // Timeout on Android.  http://crbug.com/981972
diff --git a/chrome/browser/password_manager/account_storage/account_password_store_factory.cc b/chrome/browser/password_manager/account_storage/account_password_store_factory.cc
index 56832b7..2f8df20 100644
--- a/chrome/browser/password_manager/account_storage/account_password_store_factory.cc
+++ b/chrome/browser/password_manager/account_storage/account_password_store_factory.cc
@@ -28,8 +28,50 @@
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 
+#if !defined(OS_ANDROID)
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "chrome/browser/password_manager/chrome_password_manager_client.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "content/public/browser/browser_task_traits.h"
+#endif  // !defined(OS_ANDROID)
+
 using password_manager::PasswordStore;
 
+#if !defined(OS_ANDROID)
+
+namespace {
+
+void UpdateAllFormManagers(Profile* profile) {
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (browser->profile() != profile)
+      continue;
+    TabStripModel* tabs = browser->tab_strip_model();
+    for (int index = 0; index < tabs->count(); index++) {
+      content::WebContents* web_contents = tabs->GetWebContentsAt(index);
+      ChromePasswordManagerClient* client =
+          ChromePasswordManagerClient::FromWebContents(web_contents);
+      if (client)
+        client->UpdateFormManagers();
+    }
+  }
+}
+
+}  // namespace
+
+#endif  // !defined(OS_ANDROID)
+
+void SyncEnabledOrDisabled(Profile* profile) {
+#if defined(OS_ANDROID)
+  NOTREACHED();
+#else
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&UpdateAllFormManagers, profile));
+#endif  // defined(OS_ANDROID)
+}
+
 // static
 scoped_refptr<PasswordStore> AccountPasswordStoreFactory::GetForProfile(
     Profile* profile,
@@ -77,7 +119,8 @@
 
   scoped_refptr<PasswordStore> ps =
       new password_manager::PasswordStoreDefault(std::move(login_db));
-  if (!ps->Init(/*flare=*/base::DoNothing(), profile->GetPrefs())) {
+  if (!ps->Init(/*flare=*/base::DoNothing(), profile->GetPrefs(),
+                base::BindRepeating(&SyncEnabledOrDisabled, profile))) {
     // TODO(crbug.com/479725): Remove the LOG once this error is visible in the
     // UI.
     LOG(WARNING) << "Could not initialize password store.";
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index e2fd2e7..835535e 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/password_manager/account_storage/account_password_store_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
-#include "chrome/browser/password_manager/touch_to_fill_controller.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
@@ -865,8 +864,9 @@
           password_generation_driver_bindings_.GetCurrentTargetFrame(),
           bounds));
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      bounds, 0 /*max_length*/, base::string16() /*generation_element*/,
-      field_renderer_id, base::i18n::TextDirection(), form);
+      bounds, /*max_length=*/0, /*generation_element=*/base::string16(),
+      field_renderer_id, /*is_generation_element_password_type=*/true,
+      base::i18n::TextDirection(), form);
   popup_controller_ = PasswordGenerationPopupControllerImpl::GetOrCreate(
       popup_controller_, element_bounds_in_screen_space, ui_data,
       driver->AsWeakPtr(), observer_, web_contents(),
@@ -1171,10 +1171,13 @@
     bool is_manually_triggered) {
   gfx::RectF element_bounds_in_top_frame_space =
       TransformToRootCoordinates(driver->render_frame_host(), ui_data.bounds);
+  // Only show password suggestions iff the field is of password type.
+  bool show_password_suggestions = ui_data.is_generation_element_password_type;
   if (!is_manually_triggered &&
       driver->GetPasswordAutofillManager()
           ->MaybeShowPasswordSuggestionsWithGeneration(
-              element_bounds_in_top_frame_space, ui_data.text_direction)) {
+              element_bounds_in_top_frame_space, ui_data.text_direction,
+              show_password_suggestions)) {
     return;
   }
 
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 db8bbb34..30de8bb4 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
@@ -152,11 +152,13 @@
     const content::NotificationDetails& details) {
   switch (type) {
     case chrome::NOTIFICATION_PROFILE_CREATED: {
-      // TODO(1013168): Create the worker watchers here.
+      Profile* profile = content::Source<Profile>(source).ptr();
+      CreateSharedWorkerWatcher(profile);
       break;
     }
     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
-      // TODO(1013168): Delete the worker watchers here.
+      Profile* profile = content::Source<Profile>(source).ptr();
+      DeleteSharedWorkerWatcher(profile);
       break;
     }
     default:
diff --git a/chrome/browser/prefs/synced_pref_change_registrar_browsertest.cc b/chrome/browser/prefs/synced_pref_change_registrar_browsertest.cc
deleted file mode 100644
index a0624c1..0000000
--- a/chrome/browser/prefs/synced_pref_change_registrar_browsertest.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-
-#include "base/bind.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/run_loop.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "chrome/browser/prefs/pref_service_syncable_util.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/policy/core/common/mock_configuration_policy_provider.h"
-#include "components/policy/core/common/policy_map.h"
-#include "components/policy/core/common/policy_types.h"
-#include "components/policy/policy_constants.h"
-#include "components/sync/model/fake_sync_change_processor.h"
-#include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_error_factory.h"
-#include "components/sync/model/sync_error_factory_mock.h"
-#include "components/sync/model/syncable_service.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync_preferences/synced_pref_change_registrar.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/test_utils.h"
-
-namespace {
-
-using testing::Return;
-using testing::_;
-
-class SyncedPrefChangeRegistrarTest : public InProcessBrowserTest {
- public:
-  SyncedPrefChangeRegistrarTest() : next_sync_data_id_(0) {}
-  ~SyncedPrefChangeRegistrarTest() override {}
-
-  void UpdateChromePolicy(const policy::PolicyMap& policies) {
-    policy_provider_.UpdateChromePolicy(policies);
-    DCHECK(base::MessageLoopCurrent::Get());
-    base::RunLoop loop;
-    loop.RunUntilIdle();
-  }
-
-  void SetBooleanPrefValueFromSync(const std::string& name, bool value) {
-    std::string serialized_value;
-    JSONStringValueSerializer json(&serialized_value);
-    json.Serialize(base::Value(value));
-
-    sync_pb::EntitySpecifics specifics;
-    sync_pb::PreferenceSpecifics* pref_specifics =
-        specifics.mutable_preference();
-    pref_specifics->set_name(name);
-    pref_specifics->set_value(serialized_value);
-
-    syncer::SyncData change_data =
-        syncer::SyncData::CreateRemoteData(++next_sync_data_id_, specifics);
-    syncer::SyncChange change(
-        FROM_HERE, syncer::SyncChange::ACTION_UPDATE, change_data);
-
-    syncer::SyncChangeList change_list;
-    change_list.push_back(change);
-
-    syncer_->ProcessSyncChanges(FROM_HERE, change_list);
-  }
-
-  void SetBooleanPrefValueFromLocal(const std::string& name, bool value) {
-    prefs_->SetBoolean(name.c_str(), value);
-  }
-
-  bool GetBooleanPrefValue(const std::string& name) {
-    return prefs_->GetBoolean(name.c_str());
-  }
-
-  sync_preferences::PrefServiceSyncable* prefs() const { return prefs_; }
-
-  sync_preferences::SyncedPrefChangeRegistrar* registrar() const {
-    return registrar_.get();
-  }
-
- private:
-  void SetUpInProcessBrowserTestFixture() override {
-    EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
-        .WillRepeatedly(Return(true));
-    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
-        &policy_provider_);
-  }
-
-  void SetUpOnMainThread() override {
-    prefs_ = PrefServiceSyncableFromProfile(browser()->profile());
-    syncer_ = prefs_->GetSyncableService(syncer::PREFERENCES);
-    syncer_->MergeDataAndStartSyncing(
-        syncer::PREFERENCES, syncer::SyncDataList(),
-        std::unique_ptr<syncer::SyncChangeProcessor>(
-            new syncer::FakeSyncChangeProcessor),
-        std::unique_ptr<syncer::SyncErrorFactory>(
-            new syncer::SyncErrorFactoryMock));
-    registrar_.reset(new sync_preferences::SyncedPrefChangeRegistrar(prefs_));
-  }
-
-  void TearDownOnMainThread() override { registrar_.reset(); }
-
-  sync_preferences::PrefServiceSyncable* prefs_;
-  syncer::SyncableService* syncer_;
-  int next_sync_data_id_;
-
-  std::unique_ptr<sync_preferences::SyncedPrefChangeRegistrar> registrar_;
-  policy::MockConfigurationPolicyProvider policy_provider_;
-};
-
-struct TestSyncedPrefObserver {
-  bool last_seen_value;
-  bool last_update_is_from_sync;
-  bool has_been_notified;
-};
-
-void TestPrefChangeCallback(PrefService* prefs,
-                            TestSyncedPrefObserver* observer,
-                            const std::string& path,
-                            bool from_sync) {
-  observer->last_seen_value = prefs->GetBoolean(path.c_str());
-  observer->last_update_is_from_sync = from_sync;
-  observer->has_been_notified = true;
-}
-
-}  // namespace
-
-IN_PROC_BROWSER_TEST_F(SyncedPrefChangeRegistrarTest,
-                       DifferentiateRemoteAndLocalChanges) {
-  TestSyncedPrefObserver observer = {};
-  registrar()->Add(prefs::kShowHomeButton,
-      base::Bind(&TestPrefChangeCallback, prefs(), &observer));
-
-  EXPECT_FALSE(observer.has_been_notified);
-
-  SetBooleanPrefValueFromSync(prefs::kShowHomeButton, true);
-  EXPECT_TRUE(observer.has_been_notified);
-  EXPECT_TRUE(GetBooleanPrefValue(prefs::kShowHomeButton));
-  EXPECT_TRUE(observer.last_update_is_from_sync);
-  EXPECT_TRUE(observer.last_seen_value);
-
-  observer.has_been_notified = false;
-  SetBooleanPrefValueFromLocal(prefs::kShowHomeButton, false);
-  EXPECT_TRUE(observer.has_been_notified);
-  EXPECT_FALSE(GetBooleanPrefValue(prefs::kShowHomeButton));
-  EXPECT_FALSE(observer.last_update_is_from_sync);
-  EXPECT_FALSE(observer.last_seen_value);
-
-  observer.has_been_notified = false;
-  SetBooleanPrefValueFromLocal(prefs::kShowHomeButton, true);
-  EXPECT_TRUE(observer.has_been_notified);
-  EXPECT_TRUE(GetBooleanPrefValue(prefs::kShowHomeButton));
-  EXPECT_FALSE(observer.last_update_is_from_sync);
-  EXPECT_TRUE(observer.last_seen_value);
-
-  observer.has_been_notified = false;
-  SetBooleanPrefValueFromSync(prefs::kShowHomeButton, false);
-  EXPECT_TRUE(observer.has_been_notified);
-  EXPECT_FALSE(GetBooleanPrefValue(prefs::kShowHomeButton));
-  EXPECT_TRUE(observer.last_update_is_from_sync);
-  EXPECT_FALSE(observer.last_seen_value);
-}
-
-IN_PROC_BROWSER_TEST_F(SyncedPrefChangeRegistrarTest,
-                       IgnoreLocalChangesToManagedPrefs) {
-  TestSyncedPrefObserver observer = {};
-  registrar()->Add(prefs::kShowHomeButton,
-      base::Bind(&TestPrefChangeCallback, prefs(), &observer));
-
-  policy::PolicyMap policies;
-  policies.Set(policy::key::kShowHomeButton, policy::POLICY_LEVEL_MANDATORY,
-               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(true), nullptr);
-  UpdateChromePolicy(policies);
-
-  EXPECT_TRUE(prefs()->IsManagedPreference(prefs::kShowHomeButton));
-
-  SetBooleanPrefValueFromLocal(prefs::kShowHomeButton, false);
-  EXPECT_FALSE(observer.has_been_notified);
-  EXPECT_TRUE(GetBooleanPrefValue(prefs::kShowHomeButton));
-}
-
-IN_PROC_BROWSER_TEST_F(SyncedPrefChangeRegistrarTest,
-                       IgnoreSyncChangesToManagedPrefs) {
-  TestSyncedPrefObserver observer = {};
-  registrar()->Add(prefs::kShowHomeButton,
-      base::Bind(&TestPrefChangeCallback, prefs(), &observer));
-
-  policy::PolicyMap policies;
-  policies.Set(policy::key::kShowHomeButton, policy::POLICY_LEVEL_MANDATORY,
-               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(true), nullptr);
-  UpdateChromePolicy(policies);
-
-  EXPECT_TRUE(prefs()->IsManagedPreference(prefs::kShowHomeButton));
-  SetBooleanPrefValueFromSync(prefs::kShowHomeButton, false);
-  EXPECT_FALSE(observer.has_been_notified);
-  EXPECT_TRUE(GetBooleanPrefValue(prefs::kShowHomeButton));
-}
diff --git a/chrome/browser/profiles/gaia_info_update_service_unittest.cc b/chrome/browser/profiles/gaia_info_update_service_unittest.cc
index 9156a3d..242e148 100644
--- a/chrome/browser/profiles/gaia_info_update_service_unittest.cc
+++ b/chrome/browser/profiles/gaia_info_update_service_unittest.cc
@@ -294,48 +294,6 @@
       GetString(prefs::kGoogleServicesHostedDomain));
 }
 
-// TODO(anthonyvd) : remove or update test once the refactoring of the internals
-// of ProfileInfoCache is complete.
-TEST_F(GAIAInfoUpdateServiceTest, HandlesProfileReordering) {
-  size_t index = GetCache()->GetIndexOfProfileWithPath(profile()->GetPath());
-  GetCache()->SetLocalProfileNameOfProfileAtIndex(index, FullName16("B"));
-  GetCache()->SetProfileIsUsingDefaultNameAtIndex(index, true);
-
-  CreateProfile(FullName("A"));
-  CreateProfile(FullName("C"));
-  CreateProfile(FullName("E"));
-
-  size_t index_before =
-      GetCache()->GetIndexOfProfileWithPath(profile()->GetPath());
-
-  // Rename our profile.
-  RenameProfile(FullName16("D"), GivenName16("D"));
-  // Profiles should have been reordered in the cache.
-  EXPECT_NE(index_before,
-            GetCache()->GetIndexOfProfileWithPath(profile()->GetPath()));
-  // Rename the profile back to the original name, it should go back to its
-  // original position.
-  RenameProfile(FullName16("B"), GivenName16("B"));
-  EXPECT_EQ(index_before,
-            GetCache()->GetIndexOfProfileWithPath(profile()->GetPath()));
-
-  // Rename only the given name of our profile.
-  RenameProfile(FullName16("B"), GivenName16("D"));
-  // Rename the profile back to the original name, it should go back to its
-  // original position.
-  RenameProfile(FullName16("B"), GivenName16("B"));
-  EXPECT_EQ(index_before,
-            GetCache()->GetIndexOfProfileWithPath(profile()->GetPath()));
-
-  // Rename only the full name of our profile.
-  RenameProfile(FullName16("D"), GivenName16("B"));
-  // Rename the profile back to the original name, it should go back to its
-  // original position.
-  RenameProfile(FullName16("B"), GivenName16("B"));
-  EXPECT_EQ(index_before,
-            GetCache()->GetIndexOfProfileWithPath(profile()->GetPath()));
-}
-
 TEST_F(GAIAInfoUpdateServiceTest, ShouldUseGAIAProfileInfo) {
 #if defined(OS_CHROMEOS)
   // This feature should never be enabled on ChromeOS.
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index 3945acf3..75cd830 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -83,7 +83,7 @@
 #endif
     base::string16 name;
     info->GetString(kNameKey, &name);
-    sorted_keys_.insert(FindPositionForProfile(it.key(), name), it.key());
+    keys_.push_back(it.key());
     profile_attributes_entries_[user_data_dir_.AppendASCII(it.key()).value()] =
         std::unique_ptr<ProfileAttributesEntry>(nullptr);
 
@@ -167,7 +167,7 @@
     old_single_profile_name = entry->GetName();
   }
 
-  sorted_keys_.insert(FindPositionForProfile(key, name), key);
+  keys_.push_back(key);
   profile_attributes_entries_[user_data_dir_.AppendASCII(key).value()] =
       std::unique_ptr<ProfileAttributesEntry>();
 
@@ -219,7 +219,7 @@
   base::DictionaryValue* cache = update.Get();
   std::string key = CacheKeyFromProfilePath(profile_path);
   cache->Remove(key, NULL);
-  sorted_keys_.erase(std::find(sorted_keys_.begin(), sorted_keys_.end(), key));
+  keys_.erase(std::find(keys_.begin(), keys_.end(), key));
   profile_attributes_entries_.erase(profile_path.value());
 
   bool single_profile_name_changed =
@@ -236,7 +236,7 @@
 }
 
 size_t ProfileInfoCache::GetNumberOfProfiles() const {
-  return sorted_keys_.size();
+  return keys_.size();
 }
 
 size_t ProfileInfoCache::GetIndexOfProfileWithPath(
@@ -244,8 +244,8 @@
   if (profile_path.DirName() != user_data_dir_)
     return std::string::npos;
   std::string search_key = CacheKeyFromProfilePath(profile_path);
-  for (size_t i = 0; i < sorted_keys_.size(); ++i) {
-    if (sorted_keys_[i] == search_key)
+  for (size_t i = 0; i < keys_.size(); ++i) {
+    if (keys_[i] == search_key)
       return i;
   }
   return std::string::npos;
@@ -279,7 +279,7 @@
 }
 
 base::FilePath ProfileInfoCache::GetPathOfProfileAtIndex(size_t index) const {
-  return user_data_dir_.AppendASCII(sorted_keys_[index]);
+  return user_data_dir_.AppendASCII(keys_[index]);
 }
 
 base::string16 ProfileInfoCache::GetUserNameOfProfileAtIndex(
@@ -455,7 +455,6 @@
 
   base::string16 new_display_name = GetNameToDisplayOfProfileAtIndex(index);
   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
-  UpdateSortForProfileIndex(index);
 
   if (old_display_name != new_display_name) {
     for (auto& observer : observer_list_)
@@ -572,7 +571,6 @@
   SetInfoForProfileAtIndex(index, std::move(info));
   base::string16 new_display_name = GetNameToDisplayOfProfileAtIndex(index);
   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
-  UpdateSortForProfileIndex(index);
 
   if (old_display_name != new_display_name) {
     for (auto& observer : observer_list_)
@@ -593,7 +591,6 @@
   SetInfoForProfileAtIndex(index, std::move(info));
   base::string16 new_display_name = GetNameToDisplayOfProfileAtIndex(index);
   base::FilePath profile_path = GetPathOfProfileAtIndex(index);
-  UpdateSortForProfileIndex(index);
 
   if (old_display_name != new_display_name) {
     for (auto& observer : observer_list_)
@@ -723,7 +720,7 @@
   const base::DictionaryValue* cache =
       prefs_->GetDictionary(prefs::kProfileInfoCache);
   const base::DictionaryValue* info = NULL;
-  cache->GetDictionaryWithoutPathExpansion(sorted_keys_[index], &info);
+  cache->GetDictionaryWithoutPathExpansion(keys_[index], &info);
   return info;
 }
 
@@ -732,7 +729,7 @@
     std::unique_ptr<base::DictionaryValue> info) {
   DictionaryPrefUpdate update(prefs_, prefs::kProfileInfoCache);
   base::DictionaryValue* cache = update.Get();
-  cache->SetWithoutPathExpansion(sorted_keys_[index], std::move(info));
+  cache->SetWithoutPathExpansion(keys_[index], std::move(info));
 }
 
 std::string ProfileInfoCache::CacheKeyFromProfilePath(
@@ -742,36 +739,6 @@
   return base_name.MaybeAsASCII();
 }
 
-std::vector<std::string>::iterator ProfileInfoCache::FindPositionForProfile(
-    const std::string& search_key,
-    const base::string16& search_name) {
-  base::string16 search_name_l = base::i18n::ToLower(search_name);
-  for (size_t i = 0; i < GetNumberOfProfiles(); ++i) {
-    base::string16 name_l =
-        base::i18n::ToLower(GetNameToDisplayOfProfileAtIndex(i));
-    int name_compare = search_name_l.compare(name_l);
-    if (name_compare < 0)
-      return sorted_keys_.begin() + i;
-    if (name_compare == 0) {
-      int key_compare = search_key.compare(sorted_keys_[i]);
-      if (key_compare < 0)
-        return sorted_keys_.begin() + i;
-    }
-  }
-  return sorted_keys_.end();
-}
-
-void ProfileInfoCache::UpdateSortForProfileIndex(size_t index) {
-  base::string16 name = GetNameToDisplayOfProfileAtIndex(index);
-
-  // Remove and reinsert key in |sorted_keys_| to alphasort.
-  std::string key = CacheKeyFromProfilePath(GetPathOfProfileAtIndex(index));
-  auto key_it = std::find(sorted_keys_.begin(), sorted_keys_.end(), key);
-  DCHECK(key_it != sorted_keys_.end());
-  sorted_keys_.erase(key_it);
-  sorted_keys_.insert(FindPositionForProfile(key, name), key);
-}
-
 const gfx::Image* ProfileInfoCache::GetHighResAvatarOfProfileAtIndex(
     size_t index) const {
   const size_t avatar_index = GetAvatarIconIndexOfProfileAtIndex(index);
@@ -806,7 +773,6 @@
       if (name == entries[j]->GetLocalProfileName()) {
         entries[j]->SetLocalProfileName(
             ChooseNameForNewProfile(entries[j]->GetAvatarIconIndex()));
-        UpdateSortForProfileIndex(entries[j]->profile_index());
       }
     }
   }
diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h
index c7b2632..3e5ed91 100644
--- a/chrome/browser/profiles/profile_info_cache.h
+++ b/chrome/browser/profiles/profile_info_cache.h
@@ -197,7 +197,7 @@
   // recomputed to "Person 1" and "Person 2".
   void RecomputeProfileNamesIfNeeded();
 
-  std::vector<std::string> sorted_keys_;
+  std::vector<std::string> keys_;
   const base::FilePath user_data_dir_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileInfoCache);
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc
index 6d91c2d..7d99272 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.cc
+++ b/chrome/browser/profiles/profile_info_cache_unittest.cc
@@ -281,12 +281,9 @@
   EXPECT_TRUE(GetCache()->GetGAIANameOfProfileAtIndex(index1).empty());
   EXPECT_TRUE(GetCache()->GetGAIANameOfProfileAtIndex(index2).empty());
 
-  // Set GAIA name. This re-sorts the cache.
+  // Set GAIA name.
   base::string16 gaia_name(ASCIIToUTF16("Pat Smith"));
   GetCache()->SetGAIANameOfProfileAtIndex(index2, gaia_name);
-  index1 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_1"));
-  index2 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_2"));
-
   // Since there is a GAIA name, we use that as a display name.
   EXPECT_TRUE(GetCache()->GetGAIANameOfProfileAtIndex(index1).empty());
   EXPECT_EQ(gaia_name, GetCache()->GetGAIANameOfProfileAtIndex(index2));
@@ -294,14 +291,10 @@
                                      concatenate_enabled_, false),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index2));
 
-  // This re-sorts the cache.
   base::string16 custom_name(ASCIIToUTF16("Custom name"));
   GetCache()->SetLocalProfileNameOfProfileAtIndex(index2, custom_name);
   GetCache()->SetProfileIsUsingDefaultNameAtIndex(index2, false);
 
-  index1 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_1"));
-  index2 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_2"));
-
   EXPECT_EQ(GetExpectedNameToDisplay(gaia_name, custom_name, false,
                                      concatenate_enabled_, false),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index2));
@@ -331,7 +324,6 @@
       GetProfilePath("path_2"), ASCIIToUTF16("Person 2"), std::string(),
       base::string16(), false, 0, std::string(), EmptyAccountId());
 
-  index1 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_1"));
   EXPECT_EQ(ASCIIToUTF16("Patt (Person 1)"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index1));
 
@@ -340,16 +332,13 @@
             GetCache()->GetNameToDisplayOfProfileAtIndex(index2));
   // Set Gaia name.
   GetCache()->SetGAIANameOfProfileAtIndex(index2, ASCIIToUTF16("Patti Smith"));
-  index2 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_2"));
   // Profile name is a substring of Gaia name.
   GetCache()->SetLocalProfileNameOfProfileAtIndex(index2,
                                                   ASCIIToUTF16("patti"));
-  index2 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_2"));
   EXPECT_EQ(ASCIIToUTF16("Patti Smith"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index2));
   // Profile name equals Gaia given name.
   GetCache()->SetGAIAGivenNameOfProfileAtIndex(index2, ASCIIToUTF16("Patti"));
-  index2 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_2"));
   EXPECT_EQ(ASCIIToUTF16("Patti"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index2));
   GetCache()->SetLocalProfileNameOfProfileAtIndex(index2, ASCIIToUTF16("Work"));
@@ -362,12 +351,10 @@
                                 std::string(), EmptyAccountId());
   int index3 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_3"));
   GetCache()->SetGAIAGivenNameOfProfileAtIndex(index3, ASCIIToUTF16("Pat"));
-  index3 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_3"));
   EXPECT_EQ(ASCIIToUTF16("Pat"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index3));
   GetCache()->SetGAIANameOfProfileAtIndex(index3, ASCIIToUTF16("Pat Smith"));
   GetCache()->SetGAIAGivenNameOfProfileAtIndex(index3, ASCIIToUTF16(""));
-  index3 = GetCache()->GetIndexOfProfileWithPath(GetProfilePath("path_3"));
   EXPECT_EQ(ASCIIToUTF16("Pat Smith"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index3));
 
@@ -438,49 +425,6 @@
 #endif
 }
 
-TEST_F(ProfileInfoCacheTest, Sort) {
-  base::string16 name_a = ASCIIToUTF16("apple");
-  GetCache()->AddProfileToCache(GetProfilePath("path_a"), name_a, std::string(),
-                                base::string16(), false, 0, std::string(),
-                                EmptyAccountId());
-
-  base::string16 name_c = ASCIIToUTF16("cat");
-  GetCache()->AddProfileToCache(GetProfilePath("path_c"), name_c, std::string(),
-                                base::string16(), false, 0, std::string(),
-                                EmptyAccountId());
-
-  // Sanity check the initial order.
-  EXPECT_EQ(name_a, GetCache()->GetNameToDisplayOfProfileAtIndex(0));
-  EXPECT_EQ(name_c, GetCache()->GetNameToDisplayOfProfileAtIndex(1));
-
-  // Add a new profile (start with a capital to test case insensitive sorting.
-  base::string16 name_b = ASCIIToUTF16("Banana");
-  GetCache()->AddProfileToCache(GetProfilePath("path_b"), name_b, std::string(),
-                                base::string16(), false, 0, std::string(),
-                                EmptyAccountId());
-
-  // Verify the new order.
-  EXPECT_EQ(name_a, GetCache()->GetNameToDisplayOfProfileAtIndex(0));
-  EXPECT_EQ(name_b, GetCache()->GetNameToDisplayOfProfileAtIndex(1));
-  EXPECT_EQ(name_c, GetCache()->GetNameToDisplayOfProfileAtIndex(2));
-
-  // Change the name of an existing profile.
-  name_a = UTF8ToUTF16("dog");
-  GetCache()->SetLocalProfileNameOfProfileAtIndex(0, name_a);
-
-  // Verify the new order.
-  EXPECT_EQ(name_b, GetCache()->GetNameToDisplayOfProfileAtIndex(0));
-  EXPECT_EQ(name_c, GetCache()->GetNameToDisplayOfProfileAtIndex(1));
-  EXPECT_EQ(name_a, GetCache()->GetNameToDisplayOfProfileAtIndex(2));
-
-  // Delete a profile.
-  GetCache()->DeleteProfileFromCache(GetProfilePath("path_c"));
-
-  // Verify the new order.
-  EXPECT_EQ(name_b, GetCache()->GetNameToDisplayOfProfileAtIndex(0));
-  EXPECT_EQ(name_a, GetCache()->GetNameToDisplayOfProfileAtIndex(1));
-}
-
 // Will be removed SOON with ProfileInfoCache tests.
 TEST_F(ProfileInfoCacheTest, BackgroundModeStatus) {
   GetCache()->AddProfileToCache(
diff --git a/chrome/browser/resources/ntp4/incognito_tab.css b/chrome/browser/resources/ntp4/incognito_tab.css
index 23dfea3..a5f3cbb 100644
--- a/chrome/browser/resources/ntp4/incognito_tab.css
+++ b/chrome/browser/resources/ntp4/incognito_tab.css
@@ -124,6 +124,10 @@
   flex: none;
 }
 
+#cookie-controls-toggle:not(:defined) {
+  width: 34px;
+}
+
 /** Layout ------------------------------------------------------------------ */
 
 /* Align the content, icon, and title to to the center. */
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
index 9fb4cfe..2924c05 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_browsertest.cc
@@ -28,7 +28,7 @@
 namespace {
 const char kTelUrl[] = "tel:+9876543210";
 const char kNonTelUrl[] = "https://google.com";
-
+const char kLinkText[] = "Google";
 const char kTextWithPhoneNumber[] = "call 9876543210 now";
 const char kTextWithoutPhoneNumber[] = "abcde";
 
@@ -46,11 +46,6 @@
     return std::string(kTestPageURL);
   }
 
-  void SetUpDevices(int count) {
-    SharingBrowserTest::SetUpDevices(
-        count, sync_pb::SharingSpecificFields::CLICK_TO_CALL);
-  }
-
   void CheckLastSharingMessageSent(
       const std::string& expected_phone_number) const {
     chrome_browser_sharing::SharingMessage sharing_message =
@@ -79,17 +74,14 @@
 // TODO(himanshujaju): Add UI checks.
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest,
                        ContextMenu_TelLink_SingleDeviceAvailable) {
-  Init();
-  SetUpDevices(/*count=*/1);
-
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::UNKNOWN);
   auto devices = sharing_service()->GetDeviceCandidates(
       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
-
   ASSERT_EQ(1u, devices.size());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithoutPhoneNumber));
+      InitContextMenu(GURL(kTelUrl), kLinkText, kTextWithoutPhoneNumber);
 
   // Check click to call items in context menu
   ASSERT_TRUE(menu->IsItemPresent(
@@ -104,12 +96,14 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest, ContextMenu_NoDevicesAvailable) {
-  Init();
-  AwaitQuiescence();
+  Init(sync_pb::SharingSpecificFields::UNKNOWN,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::CLICK_TO_CALL);
+  ASSERT_EQ(0u, devices.size());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithoutPhoneNumber));
+      InitContextMenu(GURL(kTelUrl), kLinkText, kTextWithoutPhoneNumber);
   EXPECT_FALSE(menu->IsItemPresent(
       IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE));
   EXPECT_FALSE(menu->IsItemPresent(
@@ -118,15 +112,18 @@
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest,
                        ContextMenu_DevicesAvailable_SyncTurnedOff) {
-  Init();
-  SetUpDevices(/*count=*/1);
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::CLICK_TO_CALL);
+  ASSERT_EQ(1u, devices.size());
+
   // Disable syncing preferences which is necessary for Sharing.
   GetSyncService(0)->GetUserSettings()->SetSelectedTypes(false, {});
-  AwaitQuiescence();
+  ASSERT_TRUE(AwaitQuiescence());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithoutPhoneNumber));
+      InitContextMenu(GURL(kTelUrl), kLinkText, kTextWithoutPhoneNumber);
   EXPECT_FALSE(menu->IsItemPresent(
       IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE));
   EXPECT_FALSE(menu->IsItemPresent(
@@ -135,17 +132,14 @@
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest,
                        ContextMenu_TelLink_MultipleDevicesAvailable) {
-  Init();
-  SetUpDevices(/*count=*/2);
-
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
   auto devices = sharing_service()->GetDeviceCandidates(
       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
-
   ASSERT_EQ(2u, devices.size());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithoutPhoneNumber));
+      InitContextMenu(GURL(kTelUrl), kLinkText, kTextWithoutPhoneNumber);
   EXPECT_FALSE(menu->IsItemPresent(
       IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE));
   ASSERT_TRUE(menu->IsItemPresent(
@@ -171,17 +165,14 @@
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest,
                        ContextMenu_HighlightedText_MultipleDevicesAvailable) {
-  Init();
-  SetUpDevices(/*count=*/2);
-
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
   auto devices = sharing_service()->GetDeviceCandidates(
       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
-
   ASSERT_EQ(2u, devices.size());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kNonTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithPhoneNumber));
+      InitContextMenu(GURL(kNonTelUrl), kLinkText, kTextWithPhoneNumber);
   EXPECT_FALSE(menu->IsItemPresent(
       IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE));
   ASSERT_TRUE(menu->IsItemPresent(
@@ -220,17 +211,14 @@
 IN_PROC_BROWSER_TEST_F(
     ClickToCallBrowserTestWithContextMenuDisabled,
     ContextMenu_HighlightedText_DevicesAvailable_FeatureFlagOff) {
-  Init();
-  SetUpDevices(/*count=*/2);
-
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
   auto devices = sharing_service()->GetDeviceCandidates(
       sync_pb::SharingSpecificFields::CLICK_TO_CALL);
-
   ASSERT_EQ(2u, devices.size());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kNonTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithPhoneNumber));
+      InitContextMenu(GURL(kNonTelUrl), kLinkText, kTextWithPhoneNumber);
 
   EXPECT_FALSE(menu->IsItemPresent(
       IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE));
@@ -239,8 +227,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest, ContextMenu_UKM) {
-  Init();
-  SetUpDevices(/*count=*/1);
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::CLICK_TO_CALL);
+  ASSERT_EQ(1u, devices.size());
 
   ukm::TestAutoSetUkmRecorder ukm_recorder;
   base::RunLoop run_loop;
@@ -248,8 +239,7 @@
       ukm::builders::Sharing_ClickToCall::kEntryName, run_loop.QuitClosure());
 
   std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitRightClickMenu(GURL(kNonTelUrl), base::ASCIIToUTF16("Google"),
-                         base::ASCIIToUTF16(kTextWithPhoneNumber));
+      InitContextMenu(GURL(kNonTelUrl), kLinkText, kTextWithPhoneNumber);
 
   // Check click to call items in context menu
   ASSERT_TRUE(menu->IsItemPresent(
@@ -298,8 +288,11 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest, CloseTabWithBubble) {
-  Init();
-  SetUpDevices(/*count=*/1);
+  Init(sync_pb::SharingSpecificFields::CLICK_TO_CALL,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::CLICK_TO_CALL);
+  ASSERT_EQ(1u, devices.size());
 
   base::RunLoop run_loop;
   ClickToCallUiController::GetOrCreateFromWebContents(web_contents())
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc
new file mode 100644
index 0000000..57b0418
--- /dev/null
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc
@@ -0,0 +1,169 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
+#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
+#include "chrome/browser/sharing/sharing_browsertest.h"
+#include "chrome/browser/sharing/sharing_constants.h"
+#include "chrome/browser/sharing/sharing_metrics.h"
+#include "chrome/browser/sharing/sharing_sync_preference.h"
+#include "chrome/browser/sync/test/integration/sessions_helper.h"
+#include "components/sync/driver/profile_sync_service.h"
+#include "url/gurl.h"
+
+namespace {
+const char kSelectedText[] = "Lorem ipsum";
+const char kTestPageURL[] = "/sharing/tel.html";
+}  // namespace
+
+// Browser tests for the Shared Clipboard feature.
+class SharedClipboardBrowserTestBase : public SharingBrowserTest {
+ public:
+  SharedClipboardBrowserTestBase() {}
+
+  ~SharedClipboardBrowserTestBase() override = default;
+
+  std::string GetTestPageURL() const override {
+    return std::string(kTestPageURL);
+  }
+
+  void CheckLastSharingMessageSent(const std::string& expected_text) const {
+    chrome_browser_sharing::SharingMessage sharing_message =
+        GetLastSharingMessageSent();
+    ASSERT_TRUE(sharing_message.has_shared_clipboard_message());
+    ASSERT_EQ(expected_text, sharing_message.shared_clipboard_message().text());
+  }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SharedClipboardBrowserTestBase);
+};
+
+class SharedClipboardBrowserTest : public SharedClipboardBrowserTestBase {
+ public:
+  SharedClipboardBrowserTest() {
+    feature_list_.InitWithFeatures({kSharedClipboardUI}, {});
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_SingleDevice) {
+  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  ASSERT_EQ(1u, devices.size());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      InitContextMenu(GURL(), "", kSelectedText);
+  ASSERT_TRUE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
+
+  menu->ExecuteCommand(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, 0);
+  CheckLastReceiver(devices[0]->guid());
+  CheckLastSharingMessageSent(kSelectedText);
+}
+
+IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest,
+                       ContextMenu_MultipleDevices) {
+  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD,
+       sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  ASSERT_EQ(2u, devices.size());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      InitContextMenu(GURL(), "", kSelectedText);
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
+  ASSERT_TRUE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
+
+  ui::MenuModel* sub_menu_model = nullptr;
+  int device_id = -1;
+  ASSERT_TRUE(menu->GetMenuModelAndItemIndex(kSubMenuFirstDeviceCommandId,
+                                             &sub_menu_model, &device_id));
+  EXPECT_EQ(2, sub_menu_model->GetItemCount());
+  EXPECT_EQ(0, device_id);
+
+  for (auto& device : devices) {
+    EXPECT_EQ(kSubMenuFirstDeviceCommandId + device_id,
+              sub_menu_model->GetCommandIdAt(device_id));
+    sub_menu_model->ActivatedAt(device_id);
+
+    CheckLastReceiver(device->guid());
+    CheckLastSharingMessageSent(kSelectedText);
+    device_id++;
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_NoDevices) {
+  Init(sync_pb::SharingSpecificFields::UNKNOWN,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  ASSERT_EQ(0u, devices.size());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      InitContextMenu(GURL(), "", kSelectedText);
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
+}
+
+IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_SyncTurnedOff) {
+  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD,
+       sync_pb::SharingSpecificFields::UNKNOWN);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  ASSERT_EQ(1u, devices.size());
+
+  // Disable syncing preferences which is necessary for Sharing.
+  GetSyncService(0)->GetUserSettings()->SetSelectedTypes(false, {});
+  ASSERT_TRUE(AwaitQuiescence());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      InitContextMenu(GURL(), "", kSelectedText);
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
+}
+
+class SharedClipboardUIFeatureDisabledBrowserTest
+    : public SharedClipboardBrowserTestBase {
+ public:
+  SharedClipboardUIFeatureDisabledBrowserTest() {
+    feature_list_.InitWithFeatures({}, {kSharedClipboardUI});
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SharedClipboardUIFeatureDisabledBrowserTest,
+                       ContextMenu_UIFeatureDisabled) {
+  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD,
+       sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  auto devices = sharing_service()->GetDeviceCandidates(
+      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD);
+  ASSERT_EQ(2u, devices.size());
+
+  std::unique_ptr<TestRenderViewContextMenu> menu =
+      InitContextMenu(GURL(), "", kSelectedText);
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
+  ASSERT_FALSE(menu->IsItemPresent(
+      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
+}
diff --git a/chrome/browser/sharing/sharing_browsertest.cc b/chrome/browser/sharing/sharing_browsertest.cc
index 664ce06..4e05aee 100644
--- a/chrome/browser/sharing/sharing_browsertest.cc
+++ b/chrome/browser/sharing/sharing_browsertest.cc
@@ -7,6 +7,8 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -32,7 +34,9 @@
   host_resolver()->AddRule("mock.http", "127.0.0.1");
 }
 
-void SharingBrowserTest::Init() {
+void SharingBrowserTest::Init(
+    sync_pb::SharingSpecificFields_EnabledFeatures first_device_feature,
+    sync_pb::SharingSpecificFields_EnabledFeatures second_device_feature) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -47,67 +51,86 @@
   gcm_service_->set_collect(true);
 
   sharing_service_ = SharingServiceFactory::GetForBrowserContext(GetProfile(0));
+
+  SetUpDevices(first_device_feature, second_device_feature);
 }
 
 void SharingBrowserTest::SetUpDevices(
-    int count,
-    sync_pb::SharingSpecificFields_EnabledFeatures feature) {
-  for (int i = 0; i < count; i++) {
-    SharingService* service =
-        SharingServiceFactory::GetForBrowserContext(GetProfile(i));
-    service->SetDeviceInfoTrackerForTesting(&fake_device_info_tracker_);
+    sync_pb::SharingSpecificFields_EnabledFeatures first_device_feature,
+    sync_pb::SharingSpecificFields_EnabledFeatures second_device_feature) {
+  ASSERT_EQ(2u, GetSyncClients().size());
 
-    base::RunLoop run_loop;
-    service->RegisterDeviceInTesting(
-        std::set<sync_pb::SharingSpecificFields_EnabledFeatures>{feature},
-        base::BindLambdaForTesting([&](SharingDeviceRegistrationResult r) {
-          ASSERT_EQ(SharingDeviceRegistrationResult::kSuccess, r);
-          run_loop.Quit();
-        }));
-    run_loop.Run();
-    AwaitQuiescence();
-  }
+  RegisterDevice(0, first_device_feature);
+  RegisterDevice(1, second_device_feature);
 
   syncer::DeviceInfoTracker* original_device_info_tracker =
       DeviceInfoSyncServiceFactory::GetForProfile(GetProfile(0))
           ->GetDeviceInfoTracker();
   std::vector<std::unique_ptr<syncer::DeviceInfo>> original_devices =
       original_device_info_tracker->GetAllDeviceInfo();
-  int device_id = 0;
+  ASSERT_EQ(2u, original_devices.size());
 
-  for (auto& device : original_devices) {
-    std::unique_ptr<syncer::DeviceInfo> fake_device =
-        std::make_unique<syncer::DeviceInfo>(
-            device->guid(),
-            base::StrCat({"testing_device_", base::NumberToString(device_id)}),
-            device->chrome_version(), device->sync_user_agent(),
-            device->device_type(), device->signin_scoped_device_id(),
-            base::SysInfo::HardwareInfo{
-                "Google",
-                base::StrCat({"model", base::NumberToString(device_id)}),
-                "serial_number"},
-            device->last_updated_timestamp(),
-            device->send_tab_to_self_receiving_enabled(),
-            device->sharing_info());
-    fake_device_info_tracker_.Add(fake_device.get());
-    device_infos_.push_back(std::move(fake_device));
-    device_id++;
-  }
+  for (size_t i = 0; i < original_devices.size(); i++)
+    AddDeviceInfo(*original_devices[i], i);
+  ASSERT_EQ(2, fake_device_info_tracker_.CountActiveDevices());
 }
 
-// TODO(himanshujaju): try to move to static method in
-// render_view_context_menu_test_util.cc
-std::unique_ptr<TestRenderViewContextMenu>
-SharingBrowserTest::InitRightClickMenu(const GURL& url,
-                                       const base::string16& link_text,
-                                       const base::string16& selection_text) {
+void SharingBrowserTest::RegisterDevice(
+    int profile_index,
+    sync_pb::SharingSpecificFields_EnabledFeatures feature) {
+  SharingService* service =
+      SharingServiceFactory::GetForBrowserContext(GetProfile(profile_index));
+  service->SetDeviceInfoTrackerForTesting(&fake_device_info_tracker_);
+
+  base::RunLoop run_loop;
+  service->RegisterDeviceInTesting(
+      std::set<sync_pb::SharingSpecificFields_EnabledFeatures>{feature},
+      base::BindLambdaForTesting([&](SharingDeviceRegistrationResult r) {
+        ASSERT_EQ(SharingDeviceRegistrationResult::kSuccess, r);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+  ASSERT_TRUE(AwaitQuiescence());
+}
+
+void SharingBrowserTest::AddDeviceInfo(
+    const syncer::DeviceInfo& original_device,
+    int fake_device_id) {
+  // The SharingInfo on the DeviceInfo will be empty. In this test we want the
+  // SharingInfo to be read from SharingSyncPreference instead.
+  base::Optional<syncer::DeviceInfo::SharingInfo> fake_sharing_info =
+      base::nullopt;
+
+  std::unique_ptr<syncer::DeviceInfo> fake_device =
+      std::make_unique<syncer::DeviceInfo>(
+          original_device.guid(),
+          base::StrCat(
+              {"testing_device_", base::NumberToString(fake_device_id)}),
+          original_device.chrome_version(), original_device.sync_user_agent(),
+          original_device.device_type(),
+          original_device.signin_scoped_device_id(),
+          base::SysInfo::HardwareInfo{
+              "Google",
+              base::StrCat({"model", base::NumberToString(fake_device_id)}),
+              "serial_number"},
+          original_device.last_updated_timestamp(),
+          original_device.send_tab_to_self_receiving_enabled(),
+          fake_sharing_info);
+  fake_device_info_tracker_.Add(fake_device.get());
+  device_infos_.push_back(std::move(fake_device));
+}
+
+std::unique_ptr<TestRenderViewContextMenu> SharingBrowserTest::InitContextMenu(
+    const GURL& url,
+    base::StringPiece link_text,
+    base::StringPiece selection_text) {
   content::ContextMenuParams params;
-  params.selection_text = selection_text;
+  params.selection_text = base::ASCIIToUTF16(selection_text);
   params.media_type = blink::WebContextMenuData::MediaType::kMediaTypeNone;
   params.unfiltered_link_url = url;
   params.link_url = url;
   params.src_url = url;
-  params.link_text = link_text;
+  params.link_text = base::ASCIIToUTF16(link_text);
   params.page_url = web_contents_->GetVisibleURL();
   params.source_type = ui::MenuSourceType::MENU_SOURCE_MOUSE;
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/sharing/sharing_browsertest.h b/chrome/browser/sharing/sharing_browsertest.h
index 0f1d3b7a..1148070 100644
--- a/chrome/browser/sharing/sharing_browsertest.h
+++ b/chrome/browser/sharing/sharing_browsertest.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/strings/string_piece_forward.h"
 #include "chrome/browser/gcm/gcm_profile_service_factory.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/sharing/sharing_service.h"
@@ -27,17 +28,16 @@
 
   void SetUpOnMainThread() override;
 
-  void Init();
+  void Init(
+      sync_pb::SharingSpecificFields_EnabledFeatures first_device_feature,
+      sync_pb::SharingSpecificFields_EnabledFeatures second_device_feature);
 
   virtual std::string GetTestPageURL() const = 0;
 
-  void SetUpDevices(int count,
-                    sync_pb::SharingSpecificFields_EnabledFeatures feature);
-
-  std::unique_ptr<TestRenderViewContextMenu> InitRightClickMenu(
+  std::unique_ptr<TestRenderViewContextMenu> InitContextMenu(
       const GURL& url,
-      const base::string16& link_text,
-      const base::string16& selection_text);
+      base::StringPiece link_text,
+      base::StringPiece selection_text);
 
   void CheckLastReceiver(const std::string& device_guid) const;
 
@@ -48,6 +48,15 @@
   content::WebContents* web_contents() const;
 
  private:
+  void SetUpDevices(
+      sync_pb::SharingSpecificFields_EnabledFeatures first_device_feature,
+      sync_pb::SharingSpecificFields_EnabledFeatures second_device_feature);
+
+  void RegisterDevice(int profile_index,
+                      sync_pb::SharingSpecificFields_EnabledFeatures feature);
+  void AddDeviceInfo(const syncer::DeviceInfo& original_device,
+                     int fake_device_id);
+
   gcm::GCMProfileServiceFactory::ScopedTestingFactoryInstaller
       scoped_testing_factory_installer_;
   gcm::FakeGCMProfileService* gcm_service_;
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc
index c0bfb13d..e546d11 100644
--- a/chrome/browser/sharing/sharing_device_registration.cc
+++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -224,6 +224,6 @@
 }
 
 void SharingDeviceRegistration::SetEnabledFeaturesForTesting(
-    std::set<SharingSpecificFields::EnabledFeatures> enabled_feautres) {
-  enabled_features_testing_value_ = std::move(enabled_feautres);
+    std::set<SharingSpecificFields::EnabledFeatures> enabled_features) {
+  enabled_features_testing_value_ = std::move(enabled_features);
 }
diff --git a/chrome/browser/sharing/sharing_device_registration.h b/chrome/browser/sharing/sharing_device_registration.h
index 285b416..5e071e0 100644
--- a/chrome/browser/sharing/sharing_device_registration.h
+++ b/chrome/browser/sharing/sharing_device_registration.h
@@ -54,7 +54,7 @@
   // For testing
   void SetEnabledFeaturesForTesting(
       std::set<sync_pb::SharingSpecificFields_EnabledFeatures>
-          enabled_feautres);
+          enabled_features);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(SharingDeviceRegistrationTest,
diff --git a/chrome/browser/sharing/sharing_service.cc b/chrome/browser/sharing/sharing_service.cc
index 2b72ca3..008becb 100644
--- a/chrome/browser/sharing/sharing_service.cc
+++ b/chrome/browser/sharing/sharing_service.cc
@@ -446,10 +446,10 @@
 }
 
 void SharingService::RegisterDeviceInTesting(
-    std::set<sync_pb::SharingSpecificFields_EnabledFeatures> enabled_feautres,
+    std::set<sync_pb::SharingSpecificFields_EnabledFeatures> enabled_features,
     SharingDeviceRegistration::RegistrationCallback callback) {
   sharing_device_registration_->SetEnabledFeaturesForTesting(
-      std::move(enabled_feautres));
+      std::move(enabled_features));
   sharing_device_registration_->RegisterDevice(std::move(callback));
 }
 
diff --git a/chrome/browser/sharing/sharing_service.h b/chrome/browser/sharing/sharing_service.h
index 5bb5b0e..a7323035 100644
--- a/chrome/browser/sharing/sharing_service.h
+++ b/chrome/browser/sharing/sharing_service.h
@@ -119,7 +119,7 @@
 
   // Used to register devices with required capabilities in tests.
   void RegisterDeviceInTesting(
-      std::set<sync_pb::SharingSpecificFields_EnabledFeatures> enabled_feautres,
+      std::set<sync_pb::SharingSpecificFields_EnabledFeatures> enabled_features,
       SharingDeviceRegistration::RegistrationCallback callback);
 
   SharingSyncPreference* GetSyncPreferences() const;
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
index 20a668d..098a510 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_credential_item.xml
@@ -24,7 +24,10 @@
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_margin="12dp"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginTop="6dp"
+        android:layout_marginBottom="6dp"
         android:layout_weight="1"
         android:orientation="vertical">
         <TextView
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
index 0cff910..217ec91 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_header_item.xml
@@ -9,32 +9,24 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:layout_marginBottom="16dp"
-    android:orientation="vertical"
-    android:paddingStart="16dp"
-    android:paddingEnd="16dp">
+    android:orientation="vertical">
 
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
-        android:layout_marginEnd="@dimen/touch_to_fill_sheet_margin"
-        android:layout_marginStart="@dimen/touch_to_fill_sheet_margin"
         android:importantForAccessibility="no"
         app:srcCompat="@drawable/touch_to_fill_header_image" />
 
     <org.chromium.ui.widget.TextViewWithLeading
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/touch_to_fill_sheet_margin"
-        android:layout_marginStart="@dimen/touch_to_fill_sheet_margin"
         android:layout_gravity="center_horizontal"
         android:text="@string/touch_to_fill_sheet_title"
         android:textAppearance="@style/TextAppearance.BlackHeadline" />
 
     <org.chromium.ui.widget.TextViewWithLeading
         android:id="@+id/touch_to_fill_sheet_subtitle"
-        android:layout_marginEnd="@dimen/touch_to_fill_sheet_margin"
-        android:layout_marginStart="@dimen/touch_to_fill_sheet_margin"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
index 67aba53..aa2743ce 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
@@ -9,7 +9,6 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
-    android:minHeight="340dp"
     android:orientation="vertical">
 
     <ImageView
@@ -29,11 +28,10 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
-        android:layout_marginTop="16dp"
         android:layout_marginEnd="@dimen/touch_to_fill_sheet_margin"
         android:layout_marginStart="@dimen/touch_to_fill_sheet_margin"
         android:clipToPadding="false"
-        android:paddingBottom="16dp"
+        android:paddingBottom="8dp"
         android:divider="@null"
         tools:listitem="@layout/touch_to_fill_credential_item"/>
 
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/values/dimens.xml b/chrome/browser/touch_to_fill/android/internal/java/res/values/dimens.xml
index 249a6ea..918ec23 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/values/dimens.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/values/dimens.xml
@@ -6,4 +6,6 @@
 <resources>
     <dimen name="touch_to_fill_favicon_size">24dp</dimen>
     <dimen name="touch_to_fill_sheet_margin">16dp</dimen>
+    <dimen name="touch_to_fill_sheet_height_multiple_credentials">326dp</dimen>
+    <dimen name="touch_to_fill_sheet_height_single_credential">298dp</dimen>
 </resources>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
index 3752bed..f7b7c12 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.touch_to_fill;
 
 import android.content.Context;
+import android.support.annotation.DimenRes;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -128,7 +129,7 @@
 
     @Override
     public int getPeekHeight() {
-        return Math.min(mContentView.getMinimumHeight(),
+        return Math.min(mContext.getResources().getDimensionPixelSize(getDesiredSheetHeight()),
                 (int) mBottomSheetController.getBottomSheet().getSheetContainerHeight());
     }
 
@@ -161,4 +162,13 @@
     public int getSheetClosedAccessibilityStringId() {
         return R.string.touch_to_fill_sheet_closed;
     }
+
+    // TODO(crbug.com/1009331): This should add up the height of all items up to the 2nd credential.
+    private @DimenRes int getDesiredSheetHeight() {
+        if (mSheetItemListView.getAdapter() != null
+                && mSheetItemListView.getAdapter().getItemCount() > 2) {
+            return R.dimen.touch_to_fill_sheet_height_multiple_credentials;
+        }
+        return R.dimen.touch_to_fill_sheet_height_single_credential;
+    }
 }
diff --git a/chrome/browser/ui/login/login_handler_browsertest.cc b/chrome/browser/ui/login/login_handler_browsertest.cc
index 3a48d76..27c6950a 100644
--- a/chrome/browser/ui/login/login_handler_browsertest.cc
+++ b/chrome/browser/ui/login/login_handler_browsertest.cc
@@ -166,7 +166,14 @@
   }
 }
 
-class LoginPromptBrowserTest : public InProcessBrowserTest {
+enum class SplitAuthCacheByNetworkIsolationKey {
+  kFalse,
+  kTrue,
+};
+
+class LoginPromptBrowserTest
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface<SplitAuthCacheByNetworkIsolationKey> {
  public:
   LoginPromptBrowserTest()
       : bad_password_("incorrect"),
@@ -178,8 +185,20 @@
     auth_map_["bar"] = AuthInfo("testuser", "barpassword");
     auth_map_["testrealm"] = AuthInfo(username_basic_, password_);
 
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kHTTPAuthCommittedInterstitials);
+    if (GetParam() == SplitAuthCacheByNetworkIsolationKey::kFalse) {
+      scoped_feature_list_.InitWithFeatures(
+          // enabled_features
+          {features::kHTTPAuthCommittedInterstitials},
+          // disabled_features
+          {features::kSplitAuthCacheByNetworkIsolationKey});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          // enabled_features
+          {features::kHTTPAuthCommittedInterstitials,
+           features::kSplitAuthCacheByNetworkIsolationKey},
+          // disabled_features
+          {});
+    }
   }
 
   void SetUpOnMainThread() override {
@@ -213,6 +232,12 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    LoginPromptBrowserTest,
+    ::testing::Values(SplitAuthCacheByNetworkIsolationKey::kFalse,
+                      SplitAuthCacheByNetworkIsolationKey::kTrue));
+
 void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
   const net::AuthChallengeInfo& challenge = handler->auth_info();
 
@@ -255,7 +280,7 @@
 // correctness.  Instead, it relies on the auth dialog blocking the
 // browser, and triggering a timeout to cause failure when the
 // prefetch resource requires authorization.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PrefetchAuthCancels) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, PrefetchAuthCancels) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_page = embedded_test_server()->GetURL(kPrefetchAuthPage);
 
@@ -295,7 +320,7 @@
 }
 
 // Test that "Basic" HTTP authentication works.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuth) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestBasicAuth) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_page = embedded_test_server()->GetURL(kAuthBasicPage);
 
@@ -359,7 +384,7 @@
 }
 
 // Test that "Digest" HTTP authentication works.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestDigestAuth) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestDigestAuth) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_page = embedded_test_server()->GetURL(kAuthDigestPage);
 
@@ -409,7 +434,7 @@
   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestTwoAuths) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestTwoAuths) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   content::WebContents* contents1 =
@@ -468,7 +493,7 @@
 }
 
 // Test manual login prompt cancellation.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth_Manual) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestCancelAuth_Manual) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL kAuthURL = embedded_test_server()->GetURL(kAuthBasicPage);
 
@@ -495,7 +520,7 @@
 }
 
 // Test login prompt cancellation on navigation to a new page.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth_OnNavigation) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestCancelAuth_OnNavigation) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL kAuthURL = embedded_test_server()->GetURL(kAuthBasicPage);
   const GURL kNoAuthURL = embedded_test_server()->GetURL(kNoAuthPage1);
@@ -521,7 +546,7 @@
 }
 
 // Test login prompt cancellation on navigation to back.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth_OnBack) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestCancelAuth_OnBack) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL kAuthURL = embedded_test_server()->GetURL(kAuthBasicPage);
   const GURL kNoAuthURL = embedded_test_server()->GetURL(kNoAuthPage1);
@@ -552,7 +577,7 @@
 }
 
 // Test login prompt cancellation on navigation to forward.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth_OnForward) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestCancelAuth_OnForward) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL kAuthURL = embedded_test_server()->GetURL(kAuthBasicPage);
   const GURL kNoAuthURL1 = embedded_test_server()->GetURL(kNoAuthPage1);
@@ -611,6 +636,12 @@
   LoginPromptBrowserTestObserver login_prompt_observer_;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    MultiRealmLoginPromptBrowserTest,
+    ::testing::Values(SplitAuthCacheByNetworkIsolationKey::kFalse,
+                      SplitAuthCacheByNetworkIsolationKey::kTrue));
+
 template <class F>
 void MultiRealmLoginPromptBrowserTest::RunTest(const F& for_each_realm_func) {
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -652,7 +683,7 @@
 }
 
 // Checks that cancelling works as expected.
-IN_PROC_BROWSER_TEST_F(MultiRealmLoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(MultiRealmLoginPromptBrowserTest,
                        MultipleRealmCancellation) {
   RunTest([this](LoginHandler* handler) {
     WindowedAuthCancelledObserver waiter(GetNavigationController());
@@ -666,7 +697,7 @@
 }
 
 // Checks that supplying credentials works as expected.
-IN_PROC_BROWSER_TEST_F(MultiRealmLoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(MultiRealmLoginPromptBrowserTest,
                        MultipleRealmConfirmation) {
   RunTest([this](LoginHandler* handler) {
     WindowedAuthSuppliedObserver waiter(GetNavigationController());
@@ -681,7 +712,7 @@
 
 // Testing for recovery from an incorrect password for the case where
 // there are multiple authenticated resources.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, IncorrectConfirmation) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, IncorrectConfirmation) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_page = embedded_test_server()->GetURL(kSingleRealmTestPage);
 
@@ -748,7 +779,7 @@
 // If the favicon is an authenticated resource, we shouldn't prompt
 // for credentials.  The same URL, if requested elsewhere should
 // prompt for credentials.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, NoLoginPromptForFavicon) {
   const char kFaviconTestPage[] = "/login/has_favicon.html";
   const char kFaviconResource[] = "/auth-basic/favicon.gif";
 
@@ -802,7 +833,7 @@
 }
 
 // Block crossdomain image login prompting as a phishing defense.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        BlockCrossdomainPromptForSubresources) {
   const char kTestPage[] = "/login/load_img_from_b.html";
 
@@ -870,7 +901,7 @@
 // Block same domain image resource if the top level frame is HTTPS and the
 // image resource is HTTP.
 // E.g. Top level: https://example.com, Image resource: http://example.com/image
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        BlockCrossdomainPromptForSubresourcesMixedContent) {
   ASSERT_TRUE(embedded_test_server()->Start());
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
@@ -902,7 +933,7 @@
 }
 
 // Allow crossdomain iframe login prompting despite the above.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        AllowCrossdomainPromptForSubframes) {
   const char kTestPage[] = "/login/load_iframe_from_b.html";
 
@@ -954,7 +985,7 @@
   EXPECT_EQ(1, observer.auth_needed_count());
 }
 
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, SupplyRedundantAuths) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, SupplyRedundantAuths) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Get NavigationController for tab 1.
@@ -1010,7 +1041,7 @@
   EXPECT_EQ(0, observer.auth_cancelled_count());
 }
 
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, CancelRedundantAuths) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, CancelRedundantAuths) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Get NavigationController for tab 1.
@@ -1066,7 +1097,7 @@
   EXPECT_EQ(2, observer.auth_cancelled_count());
 }
 
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        SupplyRedundantAuthsMultiProfile) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1134,7 +1165,7 @@
 
 // If an XMLHttpRequest is made with incorrect credentials, there should be no
 // login prompt; instead the 401 status should be returned to the script.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        NoLoginPromptForXHRWithBadCredentials) {
   const char kXHRTestPage[] = "/login/xhr_with_credentials.html#incorrect";
 
@@ -1168,7 +1199,7 @@
 
 // If an XMLHttpRequest is made with correct credentials, there should be no
 // login prompt either.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        NoLoginPromptForXHRWithGoodCredentials) {
   const char kXHRTestPage[] = "/login/xhr_with_credentials.html#secret";
 
@@ -1202,7 +1233,7 @@
 
 // If an XMLHttpRequest is made without credentials, there should be a login
 // prompt.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        LoginPromptForXHRWithoutCredentials) {
   const char kXHRTestPage[] = "/login/xhr_without_credentials.html";
 
@@ -1265,7 +1296,7 @@
 
 // If an XMLHttpRequest is made without credentials, there should be a login
 // prompt.  If it's cancelled, the script should get a 401 status.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        LoginPromptForXHRWithoutCredentialsCancelled) {
   const char kXHRTestPage[] = "/login/xhr_without_credentials.html";
 
@@ -1307,9 +1338,88 @@
   EXPECT_EQ(1, observer.auth_cancelled_count());
 }
 
+// Test that the auth cache respects NetworkIsolationKeys when splitting the
+// cache based on the key is enabled.
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
+                       AuthCacheAcrossNetworkIsolationKeys) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL test_page = embedded_test_server()->GetURL(kAuthBasicPage);
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  NavigationController* controller = &contents->GetController();
+
+  LoginPromptBrowserTestObserver observer;
+  observer.Register(content::Source<NavigationController>(controller));
+  WindowedAuthNeededObserver auth_needed_waiter(controller);
+  browser()->OpenURL(OpenURLParams(test_page, Referrer(),
+                                   WindowOpenDisposition::CURRENT_TAB,
+                                   ui::PAGE_TRANSITION_TYPED, false));
+  auth_needed_waiter.Wait();
+
+  ASSERT_EQ(1u, observer.handlers().size());
+  WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
+  LoginHandler* handler = *observer.handlers().begin();
+  SetAuthFor(handler);
+  auth_supplied_waiter.Wait();
+
+  base::string16 expected_title = ExpectedTitleFromAuth(
+      base::ASCIIToUTF16("basicuser"), base::ASCIIToUTF16("secret"));
+  content::TitleWatcher title_watcher(contents, expected_title);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  EXPECT_EQ(1, observer.auth_needed_count());
+
+  base::RunLoop run_loop;
+  content::BrowserContext::GetDefaultStoragePartition(browser()->profile())
+      ->GetNetworkContext()
+      ->ClearHttpCache(base::Time(), base::Time(), nullptr,
+                       run_loop.QuitClosure());
+  run_loop.Run();
+
+  // Navigate to a URL on a different origin that iframes the URL with the
+  // challenge.
+  GURL cross_origin_page = embedded_test_server()->GetURL(
+      "localhost", "/iframe?" + test_page.spec());
+  if (GetParam() == SplitAuthCacheByNetworkIsolationKey::kFalse) {
+    // When allowing credentials to be used across NetworkIsolationKeys, the
+    // auth credentials should be reused and there should be no new auth dialog.
+    ui_test_utils::NavigateToURL(browser(), cross_origin_page);
+    EXPECT_EQ(0u, observer.handlers().size());
+    EXPECT_EQ(1, observer.auth_needed_count());
+  } else {
+    // When not allowing credentials to be used across NetworkIsolationKeys,
+    // there should be another auth challenge.
+    content::TestNavigationObserver navigation_observer(contents);
+    WindowedAuthNeededObserver auth_needed_waiter2(controller);
+    browser()->OpenURL(OpenURLParams(cross_origin_page, Referrer(),
+                                     WindowOpenDisposition::CURRENT_TAB,
+                                     ui::PAGE_TRANSITION_TYPED, false));
+    auth_needed_waiter2.Wait();
+    ASSERT_EQ(1u, observer.handlers().size());
+    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
+    LoginHandler* handler = *observer.handlers().begin();
+    SetAuthFor(handler);
+    auth_supplied_waiter.Wait();
+    navigation_observer.Wait();
+    EXPECT_EQ(2, observer.auth_needed_count());
+  }
+
+  std::vector<content::RenderFrameHost*> frames = contents->GetAllFrames();
+  ASSERT_EQ(2u, frames.size());
+  ASSERT_TRUE(frames[1]->IsDescendantOf(frames[0]));
+  ASSERT_EQ(test_page, frames[1]->GetLastCommittedURL());
+
+  // Make sure the iframe is displaying the base64-encoded credentials that
+  // should have been set, which the EmbeddedTestServer echos back in response
+  // bodies when /basic-auth is requested.
+  EXPECT_EQ(true, content::EvalJs(frames[1],
+                                  "document.documentElement.innerText.search("
+                                  "'YmFzaWN1c2VyOnNlY3JldA==') >= 0"));
+}
+
 // If a cross origin direct navigation triggers a login prompt, the login
 // interstitial should be shown.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        ShowCorrectUrlForCrossOriginMainFrameRequests) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1321,7 +1431,7 @@
 
 // Same as ShowCorrectUrlForCrossOriginMainFrameRequests, but happening in a
 // popup window.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        ShowCorrectUrlForCrossOriginMainFrameRequests_Popup) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1334,7 +1444,7 @@
 
 // If a cross origin redirect triggers a login prompt, the destination URL
 // should be shown in the omnibox when the auth dialog is displayed.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        ShowCorrectUrlForCrossOriginMainFrameRedirects) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1359,7 +1469,7 @@
 #define MAYBE_CancelLoginInterstitialOnRedirect \
   CancelLoginInterstitialOnRedirect
 #endif
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        MAYBE_CancelLoginInterstitialOnRedirect) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1415,7 +1525,7 @@
 // that ends up with an auth URL works fine. This can happen if a URL that
 // triggers the auth dialog can also trigger an SSL interstitial (or any other
 // type of interstitial).
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     LoginPromptBrowserTest,
     DISABLED_LoginInterstitialShouldReplaceExistingInterstitial) {
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
@@ -1485,7 +1595,7 @@
 // is any other interstitial being displayed. With committed SSL interstitials,
 // the navigation is actually cross domain since the interstitial is actually
 // a committed navigation, but we still expect the same behavior.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        ShouldReplaceExistingInterstitialWhenNavigated) {
   ASSERT_TRUE(embedded_test_server()->Start());
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
@@ -1555,7 +1665,7 @@
 // interstitial. If this test becomes flaky, it's likely that the logic that
 // prevents the tested scenario from happening got broken, rather than the test
 // itself.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        ShouldNotProceedExistingInterstitial) {
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
@@ -1600,13 +1710,13 @@
 }
 
 // Test where Basic HTTP authentication is disabled.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PRE_TestBasicAuthDisabled) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, PRE_TestBasicAuthDisabled) {
   // Disable all auth schemes. The modified list isn't respected until the
   // browser is restarted, however.
   g_browser_process->local_state()->SetString(prefs::kAuthSchemes, "");
 }
 
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuthDisabled) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, TestBasicAuthDisabled) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL test_page = embedded_test_server()->GetURL(kAuthBasicPage);
 
@@ -1641,7 +1751,7 @@
 
 // Tests that when HTTP Auth committed interstitials are enabled, a cross-origin
 // main-frame auth challenge cancels the auth request.
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     LoginPromptBrowserTest,
     TestAuthChallengeCancelsNavigationWithCommittedInterstitials) {
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -1664,7 +1774,7 @@
 // Tests that when HTTP Auth committed interstitials are enabled, the login
 // prompt is shown on top of a committed error page when there is a cross-origin
 // main-frame auth challenge.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest,
                        PromptWithCommittedInterstitials) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -1700,7 +1810,7 @@
 // Tests that when HTTP Auth committed interstitials are enabled, showing a
 // login prompt in a new window opened from window.open() does not
 // crash. Regression test for https://crbug.com/1005096.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PromptWithNoVisibleEntry) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, PromptWithNoVisibleEntry) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   content::WebContents* contents =
@@ -1744,7 +1854,7 @@
 }
 
 // Tests that FTP auth challenges appear over a blank committed interstitial.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FtpAuth) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, FtpAuth) {
   net::SpawnedTestServer ftp_server(
       net::SpawnedTestServer::TYPE_FTP,
       base::FilePath(FILE_PATH_LITERAL("chrome/test/data/ftp")));
@@ -1777,7 +1887,7 @@
 
 // Tests that FTP auth prompts do not appear when credentials have been
 // previously entered and cached.
-IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FtpAuthWithCache) {
+IN_PROC_BROWSER_TEST_P(LoginPromptBrowserTest, FtpAuthWithCache) {
   net::SpawnedTestServer ftp_server(
       net::SpawnedTestServer::TYPE_FTP,
       base::FilePath(FILE_PATH_LITERAL("chrome/test/data/ftp")));
diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc
index 2d18d03..2f78653 100644
--- a/chrome/browser/ui/login/login_tab_helper.cc
+++ b/chrome/browser/ui/login/login_tab_helper.cc
@@ -121,6 +121,7 @@
   }
 
   challenge_ = navigation_handle->GetAuthChallengeInfo().value();
+  network_isolation_key_ = navigation_handle->GetNetworkIsolationKey();
 
   url_for_delegate_ = navigation_handle->GetURL();
   delegate_ = CreateLoginPrompt(
@@ -202,7 +203,8 @@
     content::BrowserContext::GetDefaultStoragePartition(
         web_contents()->GetBrowserContext())
         ->GetNetworkContext()
-        ->AddAuthCacheEntry(challenge_, credentials.value(),
+        ->AddAuthCacheEntry(challenge_, network_isolation_key_,
+                            credentials.value(),
                             base::BindOnce(&LoginTabHelper::Reload,
                                            weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/chrome/browser/ui/login/login_tab_helper.h b/chrome/browser/ui/login/login_tab_helper.h
index a841aba..dd059ed 100644
--- a/chrome/browser/ui/login/login_tab_helper.h
+++ b/chrome/browser/ui/login/login_tab_helper.h
@@ -9,6 +9,9 @@
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "net/base/auth.h"
+#include "net/base/network_isolation_key.h"
+#include "url/gurl.h"
 
 namespace content {
 class LoginDelegate;
@@ -71,6 +74,7 @@
   GURL url_for_delegate_;
 
   net::AuthChallengeInfo challenge_;
+  net::NetworkIsolationKey network_isolation_key_;
 
   // Stores the navigation entry ID for a pending refresh due to a user
   // cancelling a login prompt. This is set to the visible navigation entry ID
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
index 9494912..57356aed 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/passwords/password_generation_popup_controller_impl.h"
 
+#include <memory>
+
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/autofill/core/common/password_form.h"
@@ -41,8 +43,9 @@
 
 TEST_F(PasswordGenerationPopupControllerImplTest, GetOrCreateTheSame) {
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      gfx::RectF(100, 20), 20 /*max_length*/, base::ASCIIToUTF16("element"),
-      100 /*generation_element_id*/, base::i18n::TextDirection(),
+      gfx::RectF(100, 20), /*max_length=*/20, base::ASCIIToUTF16("element"),
+      /*generation_element_id=*/100,
+      /*is_generation_element_password_type=*/true, base::i18n::TextDirection(),
       autofill::PasswordForm());
   ui_data.password_form.username_value = base::ASCIIToUTF16("Name");
   ui_data.password_form.password_value = base::ASCIIToUTF16("12345");
@@ -64,8 +67,9 @@
 TEST_F(PasswordGenerationPopupControllerImplTest, GetOrCreateDifferentBounds) {
   gfx::RectF rect(100, 20);
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      rect, 20 /*max_length*/, base::ASCIIToUTF16("element"),
-      100 /*generation_element_id*/, base::i18n::TextDirection(),
+      rect, /*max_length=*/20, base::ASCIIToUTF16("element"),
+      /*generation_element_id=*/100,
+      /*is_generation_element_password_type=*/true, base::i18n::TextDirection(),
       autofill::PasswordForm());
   ui_data.password_form.username_value = base::ASCIIToUTF16("Name");
   ui_data.password_form.password_value = base::ASCIIToUTF16("12345");
@@ -88,8 +92,9 @@
 
 TEST_F(PasswordGenerationPopupControllerImplTest, GetOrCreateDifferentTabs) {
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      gfx::RectF(100, 20), 20 /*max_length*/, base::ASCIIToUTF16("element"),
-      100 /*generation_element_id*/, base::i18n::TextDirection(),
+      gfx::RectF(100, 20), /*max_length=*/20, base::ASCIIToUTF16("element"),
+      /*generation_element_id=*/100,
+      /*is_generation_element_password_type=*/true, base::i18n::TextDirection(),
       autofill::PasswordForm());
   ui_data.password_form.username_value = base::ASCIIToUTF16("Name");
   ui_data.password_form.password_value = base::ASCIIToUTF16("12345");
@@ -112,8 +117,9 @@
 
 TEST_F(PasswordGenerationPopupControllerImplTest, GetOrCreateDifferentDrivers) {
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      gfx::RectF(100, 20), 20 /*max_length*/, base::ASCIIToUTF16("element"),
-      100 /*generation_element_id*/, base::i18n::TextDirection(),
+      gfx::RectF(100, 20), /*max_length=*/20, base::ASCIIToUTF16("element"),
+      /*generation_element_id=*/100,
+      /*is_generation_element_password_type=*/true, base::i18n::TextDirection(),
       autofill::PasswordForm());
   ui_data.password_form.username_value = base::ASCIIToUTF16("Name");
   ui_data.password_form.password_value = base::ASCIIToUTF16("12345");
@@ -137,8 +143,9 @@
 TEST_F(PasswordGenerationPopupControllerImplTest,
        GetOrCreateDifferentElements) {
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      gfx::RectF(100, 20), 20 /*max_length*/, base::ASCIIToUTF16("element"),
-      100 /*generation_element_id*/, base::i18n::TextDirection(),
+      gfx::RectF(100, 20), /*max_length=*/20, base::ASCIIToUTF16("element"),
+      /*generation_element_id=*/100,
+      /*is_generation_element_password_type=*/true, base::i18n::TextDirection(),
       autofill::PasswordForm());
   auto driver = CreateDriver();
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
@@ -159,8 +166,9 @@
 
 TEST_F(PasswordGenerationPopupControllerImplTest, DestroyInPasswordAccepted) {
   autofill::password_generation::PasswordGenerationUIData ui_data(
-      gfx::RectF(100, 20), 20 /*max_length*/, base::ASCIIToUTF16("element"),
-      100 /*generation_element_id*/, base::i18n::TextDirection(),
+      gfx::RectF(100, 20), /*max_length=*/20, base::ASCIIToUTF16("element"),
+      /*generation_element_id=*/100,
+      /*is_generation_element_password_type=*/true, base::i18n::TextDirection(),
       autofill::PasswordForm());
   auto driver = CreateDriver();
   std::unique_ptr<content::WebContents> web_contents = CreateTestWebContents();
diff --git a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
index 83c6799..4ec380c 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
@@ -26,11 +26,12 @@
       : PasswordGenerationPopupControllerImpl(
             gfx::RectF(0, 0, 10, 10),
             autofill::password_generation::PasswordGenerationUIData(
-                gfx::RectF(0, 0, 10, 10),
-                10,
-                base::string16(),
-                100,
-                base::i18n::TextDirection(),
+                /*bounds=*/gfx::RectF(0, 0, 10, 10),
+                /*max_length=*/10,
+                /*generation_element=*/base::string16(),
+                /*generation_element_renderer_id=*/100,
+                /*is_generation_element_password_type=*/true,
+                /*text_direction=*/base::i18n::TextDirection(),
                 PasswordForm()),
             password_manager::ContentPasswordManagerDriverFactory::
                 FromWebContents(web_contents)
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
new file mode 100644
index 0000000..d00f28c
--- /dev/null
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
@@ -0,0 +1,453 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h"
+
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/infobars/core/infobar.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/result_codes.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+content::WebContents* GetWebContents(Browser* browser, int tab) {
+  return browser->tab_strip_model()->GetWebContentsAt(tab);
+}
+
+InfoBarService* GetInfobarService(Browser* browser, int tab) {
+  return InfoBarService::FromWebContents(GetWebContents(browser, tab));
+}
+
+base::string16 GetInfobarMessageText(Browser* browser, int tab) {
+  return static_cast<ConfirmInfoBarDelegate*>(
+             GetInfobarService(browser, tab)->infobar_at(0)->delegate())
+      ->GetMessageText();
+}
+
+content::DesktopMediaID GetDesktopMediaID(Browser* browser, int tab) {
+  content::RenderFrameHost* main_frame =
+      GetWebContents(browser, tab)->GetMainFrame();
+  return content::DesktopMediaID(
+      content::DesktopMediaID::TYPE_WEB_CONTENTS,
+      content::DesktopMediaID::kNullId,
+      content::WebContentsMediaCaptureId(main_frame->GetProcess()->GetID(),
+                                         main_frame->GetRoutingID()));
+}
+
+views::Widget* GetContentsBorder(Browser* browser) {
+  return BrowserView::GetBrowserViewForBrowser(browser)
+      ->contents_border_widget();
+}
+
+scoped_refptr<MediaStreamCaptureIndicator> GetCaptureIndicator() {
+  return MediaCaptureDevicesDispatcher::GetInstance()
+      ->GetMediaStreamCaptureIndicator();
+}
+
+void ActivateTab(Browser* browser, int tab) {
+  browser->tab_strip_model()->ActivateTabAt(
+      tab, {TabStripModel::GestureType::kMouse});
+}
+
+constexpr int kNoSharedTabIndex = -1;
+}  // namespace
+
+class TabSharingUIViewsBrowserTest : public InProcessBrowserTest {
+ public:
+  TabSharingUIViewsBrowserTest() {}
+
+  void CreateUiAndStartSharing(Browser* browser, int tab) {
+    // Explicitly activate the shared tab in testing.
+    ActivateTab(browser, tab);
+
+    tab_sharing_ui_ =
+        TabSharingUI::Create(GetDesktopMediaID(browser, tab),
+                             base::UTF8ToUTF16("example-sharing.com"));
+    tab_sharing_ui_->OnStarted(
+        base::OnceClosure(),
+        base::BindRepeating(&TabSharingUIViewsBrowserTest::OnStartSharing,
+                            base::Unretained(this)));
+  }
+
+  // Verify that tab sharing infobars are displayed on all tabs, and content
+  // border and tab capture indicator are only visible on the shared tab. Pass
+  // |kNoSharedTabIndex| for |shared_tab_index| to indicate the shared tab is
+  // not in |browser|.
+  void VerifyUi(Browser* browser,
+                int shared_tab_index,
+                size_t infobar_count = 1,
+                bool has_border = true) {
+    views::Widget* contents_border = GetContentsBorder(browser);
+    EXPECT_EQ(has_border, contents_border != nullptr);
+    auto capture_indicator = GetCaptureIndicator();
+    for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
+      // All tabs have |infobar_count| tab sharing infobars.
+      InfoBarService* infobar_service = GetInfobarService(browser, i);
+      EXPECT_EQ(infobar_count, infobar_service->infobar_count());
+      for (size_t j = 0; j < infobar_count; ++j) {
+        EXPECT_EQ(infobars::InfoBarDelegate::TAB_SHARING_INFOBAR_DELEGATE,
+                  infobar_service->infobar_at(j)->delegate()->GetIdentifier());
+      }
+
+      // Content border is only visible on the shared tab.
+      if (has_border) {
+        ActivateTab(browser, i);
+        EXPECT_EQ(i == shared_tab_index, contents_border->IsVisible());
+      }
+
+      // Tab capture indicator is only displayed on the shared tab.
+      EXPECT_EQ(i == shared_tab_index,
+                capture_indicator->IsBeingMirrored(GetWebContents(browser, i)));
+    }
+  }
+
+  void AddTabs(Browser* browser, int tab_count = 1) {
+    for (int i = 0; i < tab_count; ++i) {
+      AddTabAtIndexToBrowser(browser, 0, GURL(chrome::kChromeUINewTabURL),
+                             ui::PAGE_TRANSITION_LINK, true);
+    }
+  }
+
+  TabSharingUIViews* tab_sharing_ui_views() {
+    return static_cast<TabSharingUIViews*>(tab_sharing_ui_.get());
+  }
+
+ private:
+  void OnStartSharing(const content::DesktopMediaID& media_id) {
+    tab_sharing_ui_->OnStarted(
+        base::OnceClosure(),
+        base::BindRepeating(&TabSharingUIViewsBrowserTest::OnStartSharing,
+                            base::Unretained(this)));
+  }
+
+  std::unique_ptr<TabSharingUI> tab_sharing_ui_;
+};
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest, StartSharing) {
+  AddTabs(browser(), 2);
+
+  // Test that before sharing there are no infobars, content border or tab
+  // capture indicator.
+  VerifyUi(browser(), kNoSharedTabIndex, 0 /*infobar_count*/,
+           false /*has_border*/);
+
+  // Create UI and start sharing the tab at index 1.
+  CreateUiAndStartSharing(browser(), 1);
+
+  // Test that infobars were created, and contents border and tab capture
+  // indicator are displayed on the shared tab.
+  VerifyUi(browser(), 1);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest, SwitchSharedTab) {
+  AddTabs(browser(), 2);
+  CreateUiAndStartSharing(browser(), 1);
+
+  // Share a different tab.
+  ActivateTab(browser(), 2);
+  tab_sharing_ui_views()->StartSharing(
+      GetInfobarService(browser(), 2)->infobar_at(0));
+
+  // Test that the UI has been updated.
+  VerifyUi(browser(), 2);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest, StopSharing) {
+  AddTabs(browser());
+  CreateUiAndStartSharing(browser(), 1);
+
+  tab_sharing_ui_views()->StopSharing();
+
+  // Test that the infobars have been removed, and the contents border and tab
+  // capture indicator are no longer visible.
+  VerifyUi(browser(), kNoSharedTabIndex, 0 /*infobar_count*/);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest, CloseTab) {
+  AddTabs(browser(), 2);
+  CreateUiAndStartSharing(browser(), 1);
+
+  // Close a tab different than the shared one and test that the UI has not
+  // changed.
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  tab_strip_model->CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
+  VerifyUi(browser(), 1);
+
+  // Close the shared tab and verify that sharing is stopped, i.e. the UI is
+  // removed.
+  tab_strip_model->CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
+  VerifyUi(browser(), kNoSharedTabIndex, 0 /*infobar_count*/);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest,
+                       CloseTabInIncognitoBrowser) {
+  AddTabs(browser(), 2);
+
+  // Start sharing a tab in an incognito browser.
+  Browser* incognito_browser = CreateIncognitoBrowser();
+  AddTabs(incognito_browser, 2);
+  CreateUiAndStartSharing(incognito_browser, 1);
+  VerifyUi(incognito_browser, 1);
+  VerifyUi(browser(), kNoSharedTabIndex, 1 /*infobar_count*/,
+           false /*has_border*/);
+
+  // Close a tab different than the shared one and test that the UI has not
+  // changed.
+  TabStripModel* tab_strip_model = incognito_browser->tab_strip_model();
+  tab_strip_model->CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
+  VerifyUi(incognito_browser, 1);
+  VerifyUi(browser(), kNoSharedTabIndex, 1, false);
+
+  // Close the shared tab in the incognito browser and test that the UI is
+  // removed.
+  incognito_browser->tab_strip_model()->CloseWebContentsAt(
+      1, TabStripModel::CLOSE_NONE);
+  VerifyUi(incognito_browser, kNoSharedTabIndex, 0 /*infobar_count*/);
+  VerifyUi(browser(), kNoSharedTabIndex, 0, false);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest, KillTab) {
+  AddTabs(browser(), 2);
+  CreateUiAndStartSharing(browser(), 1);
+
+  // Kill a tab different than the shared one.
+  content::WebContents* web_contents = GetWebContents(browser(), 0);
+  content::RenderProcessHost* process =
+      web_contents->GetMainFrame()->GetProcess();
+  content::RenderProcessHostWatcher crash_observer(
+      process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  process->Shutdown(content::RESULT_CODE_KILLED);
+  crash_observer.Wait();
+
+  // Verify that the sad tab does not have an infobar.
+  InfoBarService* infobar_service = GetInfobarService(browser(), 0);
+  EXPECT_EQ(0u, infobar_service->infobar_count());
+
+  // Stop sharing should not result in a crash.
+  tab_sharing_ui_views()->StopSharing();
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest, KillSharedTab) {
+  AddTabs(browser(), 2);
+  CreateUiAndStartSharing(browser(), 1);
+
+  // Kill the shared tab.
+  content::WebContents* shared_tab_web_contents = GetWebContents(browser(), 1);
+  content::RenderProcessHost* shared_tab_process =
+      shared_tab_web_contents->GetMainFrame()->GetProcess();
+  content::RenderProcessHostWatcher shared_tab_crash_observer(
+      shared_tab_process,
+      content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  shared_tab_process->Shutdown(content::RESULT_CODE_KILLED);
+  shared_tab_crash_observer.Wait();
+
+  // Verify that killing the shared tab stopped sharing.
+  VerifyUi(browser(), kNoSharedTabIndex, 0);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSharingUIViewsBrowserTest,
+                       InfobarLabelUpdatedOnNavigation) {
+  AddTabs(browser());
+  CreateUiAndStartSharing(browser(), 0);
+  ASSERT_THAT(base::UTF16ToUTF8(GetInfobarMessageText(browser(), 1)),
+              ::testing::HasSubstr(chrome::kChromeUINewTabHost));
+
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL));
+  EXPECT_THAT(base::UTF16ToUTF8(GetInfobarMessageText(browser(), 1)),
+              ::testing::HasSubstr(chrome::kChromeUIVersionHost));
+
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+  EXPECT_THAT(base::UTF16ToUTF8(GetInfobarMessageText(browser(), 1)),
+              ::testing::HasSubstr("about:blank"));
+}
+
+namespace {
+
+class DragObserver : public content::NotificationObserver {
+ public:
+  DragObserver() {
+    registrar_.Add(this, chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
+                   content::NotificationService::AllSources());
+  }
+
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override {
+    run_loop_.QuitWhenIdle();
+  }
+  void Wait() & { run_loop_.Run(); }
+
+ private:
+  content::NotificationRegistrar registrar_;
+  base::RunLoop run_loop_;
+};
+}  // namespace
+
+class TabDragTabSharingUIViewsBrowserTest
+    : public TabSharingUIViewsBrowserTest {
+ public:
+  TabDragTabSharingUIViewsBrowserTest() {}
+
+  void DragTab(Browser* browser, int tab) {
+    TabStrip* tab_strip =
+        BrowserView::GetBrowserViewForBrowser(browser)->tabstrip();
+    DragObserver observer;
+    size_t browser_count = BrowserList::GetInstance()->size();
+    const gfx::Point tab_center =
+        ui_test_utils::GetCenterInScreenCoordinates(tab_strip->tab_at(tab));
+    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_center) &&
+                ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
+                                                   ui_controls::DOWN));
+    // Drag the tab enough so that it detaches.
+    const gfx::Point drag_location =
+        tab_center + gfx::Vector2d(0, tab_strip->height() * 2);
+    ASSERT_TRUE(ui_controls::SendMouseMoveNotifyWhenDone(
+        drag_location.x(), drag_location.y(),
+        base::BindOnce(
+            &TabDragTabSharingUIViewsBrowserTest::ReleaseInputAfterDrag,
+            base::Unretained(this), browser_count + 1)));
+    observer.Wait();
+  }
+
+  void ReleaseInputAfterDrag(size_t browser_count) {
+    if (BrowserList::GetInstance()->size() != browser_count) {
+      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(
+              &TabDragTabSharingUIViewsBrowserTest::ReleaseInputAfterDrag,
+              base::Unretained(this), browser_count),
+          base::TimeDelta::FromMilliseconds(1));
+      return;
+    }
+
+    ASSERT_TRUE(
+        ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP));
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(TabDragTabSharingUIViewsBrowserTest, DragTab) {
+  AddTabs(browser());
+  CreateUiAndStartSharing(browser(), 1);
+
+  DragTab(browser(), 0);
+
+  BrowserList* browser_list = BrowserList::GetInstance();
+  ASSERT_EQ(browser_list->size(), 2u);
+  ASSERT_EQ(browser_list->get(0), browser());
+  VerifyUi(browser(), 0);
+  VerifyUi(browser_list->get(1), kNoSharedTabIndex, 1 /*infobar_count*/,
+           false /*has_border*/);
+}
+
+IN_PROC_BROWSER_TEST_F(TabDragTabSharingUIViewsBrowserTest, DragSharedTab) {
+  AddTabs(browser());
+  CreateUiAndStartSharing(browser(), 1);
+
+  DragTab(browser(), 1);
+
+  BrowserList* browser_list = BrowserList::GetInstance();
+  ASSERT_EQ(browser_list->size(), 2u);
+  ASSERT_EQ(browser_list->get(0), browser());
+  VerifyUi(browser(), kNoSharedTabIndex);
+  VerifyUi(browser_list->get(1), 0);
+}
+
+class MultipleTabSharingUIViewsBrowserTest : public InProcessBrowserTest {
+ public:
+  MultipleTabSharingUIViewsBrowserTest() {}
+
+  void CreateUIsAndStartSharing(Browser* browser,
+                                int tab_index,
+                                int tab_count) {
+    for (int i = 0; i < tab_count; ++i) {
+      int tab = tab_index + i;
+      ActivateTab(browser, tab);
+      tab_sharing_ui_views_.push_back(
+          TabSharingUI::Create(GetDesktopMediaID(browser, tab),
+                               base::UTF8ToUTF16("example-sharing.com")));
+      tab_sharing_ui_views_[tab_sharing_ui_views_.size() - 1]->OnStarted(
+          base::OnceClosure(), content::MediaStreamUI::SourceCallback());
+    }
+  }
+
+  TabSharingUIViews* tab_sharing_ui_views(int i) {
+    return static_cast<TabSharingUIViews*>(tab_sharing_ui_views_[i].get());
+  }
+
+  void AddTabs(Browser* browser, int tab_count) {
+    for (int i = 0; i < tab_count; ++i)
+      AddBlankTabAndShow(browser);
+  }
+
+ private:
+  std::vector<std::unique_ptr<TabSharingUI>> tab_sharing_ui_views_;
+};
+
+IN_PROC_BROWSER_TEST_F(MultipleTabSharingUIViewsBrowserTest, VerifyUi) {
+  AddTabs(browser(), 3);
+  CreateUIsAndStartSharing(browser(), 1, 3);
+
+  // Check that all tabs have 3 infobars corresponding to the 3 sharing
+  // sessions.
+  int tab_count = browser()->tab_strip_model()->count();
+  for (int i = 0; i < tab_count; ++i)
+    EXPECT_EQ(3u, GetInfobarService(browser(), i)->infobar_count());
+
+  // Check that all shared tabs display a tab capture indicator.
+  auto capture_indicator = GetCaptureIndicator();
+  for (int i = 1; i < tab_count; ++i)
+    EXPECT_TRUE(
+        capture_indicator->IsBeingMirrored(GetWebContents(browser(), i)));
+
+  // Check that the border is only displayed on the last shared tab (known
+  // limitation https://crbug.com/996631).
+  views::Widget* contents_border = GetContentsBorder(browser());
+  for (int i = 0; i < tab_count; ++i) {
+    ActivateTab(browser(), i);
+    EXPECT_EQ(i == 3, contents_border->IsVisible());
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(MultipleTabSharingUIViewsBrowserTest, StopSharing) {
+  AddTabs(browser(), 3);
+  CreateUIsAndStartSharing(browser(), 1, 3);
+
+  // Stop sharing tabs one by one and check that infobars are removed as well.
+  size_t shared_tab_count = 3;
+  while (shared_tab_count) {
+    tab_sharing_ui_views(--shared_tab_count)->StopSharing();
+    for (int j = 0; j < browser()->tab_strip_model()->count(); ++j)
+      EXPECT_EQ(shared_tab_count,
+                GetInfobarService(browser(), j)->infobar_count());
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(MultipleTabSharingUIViewsBrowserTest, CloseTabs) {
+  AddTabs(browser(), 3);
+  CreateUIsAndStartSharing(browser(), 1, 3);
+
+  // Close shared tabs one by one and check that infobars are removed as well.
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  while (tab_strip_model->count() > 1) {
+    tab_strip_model->CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
+    for (int i = 0; i < tab_strip_model->count(); ++i)
+      EXPECT_EQ(tab_strip_model->count() - 1u,
+                GetInfobarService(browser(), i)->infobar_count());
+  }
+}
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index f7de5a6f..0654a84 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -18,6 +18,7 @@
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
@@ -27,7 +28,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/installable/installable_metrics.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -42,6 +42,7 @@
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/window_finder.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_install_utils.h"
@@ -627,29 +628,25 @@
 
   Browser* GetTerminalAppBrowser() {
     // Install the app (but only once per session).
-    if (!terminal_app_extension_) {
+    if (!terminal_app_id_) {
       GURL app_url = embedded_test_server()->GetURL("app.com", "/simple.html");
       auto web_app_info = std::make_unique<WebApplicationInfo>();
       web_app_info->app_url = app_url;
       web_app_info->scope = app_url.GetWithoutFilename();
       web_app_info->open_as_window = true;
-      web_app::AppId app_id = InstallWebApp(std::move(web_app_info));
+      terminal_app_id_ = InstallWebApp(std::move(web_app_info));
 
       auto* provider = web_app::WebAppProvider::Get(browser()->profile());
       provider->system_web_app_manager().SetSystemAppsForTesting(
           {{web_app::SystemAppType::TERMINAL,
             web_app::SystemAppInfo(app_url)}});
       web_app::ExternallyInstalledWebAppPrefs(browser()->profile()->GetPrefs())
-          .Insert(app_url, app_id,
+          .Insert(app_url, *terminal_app_id_,
                   web_app::ExternalInstallSource::kInternalDefault);
-
-      terminal_app_extension_ =
-          extensions::ExtensionRegistry::Get(browser()->profile())
-              ->GetInstalledExtension(app_id);
     }
 
-    return extensions::browsertest_util::LaunchAppBrowser(
-        browser()->profile(), terminal_app_extension_);
+    return web_app::LaunchWebAppBrowser(browser()->profile(),
+                                        *terminal_app_id_);
   }
 
   Browser* browser() const { return InProcessBrowserTest::browser(); }
@@ -659,7 +656,7 @@
   // The root window for the event generator.
   aura::Window* root_ = nullptr;
 #endif
-  const extensions::Extension* terminal_app_extension_ = nullptr;
+  base::Optional<web_app::AppId> terminal_app_id_;
 
   DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
 };
diff --git a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
index 8ec89e8..7392dc3 100644
--- a/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
+++ b/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
@@ -354,8 +354,12 @@
 
   std::string selected_language;
   if (!language_switch_result) {
-    selected_language =
-        StartupCustomizationDocument::GetInstance()->initial_locale_default();
+    if (!g_browser_process->GetApplicationLocale().empty()) {
+      selected_language = g_browser_process->GetApplicationLocale();
+    } else {
+      selected_language =
+          StartupCustomizationDocument::GetInstance()->initial_locale_default();
+    }
   } else {
     if (language_switch_result->success) {
       if (language_switch_result->requested_locale ==
diff --git a/chrome/browser/ui/webui/site_settings_helper.cc b/chrome/browser/ui/webui/site_settings_helper.cc
index 0cacb0b..d7a5a6f 100644
--- a/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/site_settings_helper.cc
@@ -121,6 +121,7 @@
     {CONTENT_SETTINGS_TYPE_WAKE_LOCK_SCREEN, nullptr},
     {CONTENT_SETTINGS_TYPE_WAKE_LOCK_SYSTEM, nullptr},
     {CONTENT_SETTINGS_TYPE_LEGACY_COOKIE_ACCESS, nullptr},
+    {CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA, nullptr},
 };
 static_assert(base::size(kContentSettingsTypeGroupNames) ==
                   // ContentSettingsType starts at -1, so add 1 here.
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index 821b78f7..f91bf3ca 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -90,10 +90,12 @@
   }
 
   deps = [
+    "//base/util/values:values_util",
     "//chrome/app/resources:platform_locale_settings",
     "//chrome/app/theme:chrome_unscaled_resources",
     "//chrome/browser/web_applications:web_app_group",
     "//chrome/common",
+    "//components/content_settings/core/browser",
     "//components/crx_file",
     "//components/favicon/content",
     "//components/keyed_service/content",
diff --git a/chrome/browser/web_applications/components/manifest_update_manager.cc b/chrome/browser/web_applications/components/manifest_update_manager.cc
index 63936805..48fb08c 100644
--- a/chrome/browser/web_applications/components/manifest_update_manager.cc
+++ b/chrome/browser/web_applications/components/manifest_update_manager.cc
@@ -5,12 +5,69 @@
 #include "chrome/browser/web_applications/components/manifest_update_manager.h"
 
 #include "base/metrics/histogram_macros.h"
+#include "base/util/values/values_util.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/common/chrome_features.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 
 namespace web_app {
 
-ManifestUpdateManager::ManifestUpdateManager() = default;
+namespace {
+
+const char kLastUpdateCheckKey[] = "last_update_check";
+
+class AppPrefs {
+ public:
+  AppPrefs(Profile* profile, const GURL& origin) {
+    settings_ = HostContentSettingsMapFactory::GetForProfile(profile);
+    if (!settings_)
+      return;
+    origin_data_ = settings_->GetWebsiteSetting(
+        origin, GURL(), CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA,
+        std::string(), nullptr);
+  }
+
+  bool IsAvailable() const { return settings_; }
+
+  const base::Value* GetAppData(const AppId& app_id) const {
+    if (!origin_data_)
+      return nullptr;
+    return origin_data_->FindKey(app_id);
+  }
+
+  base::Value& GetAppDataMutable(const AppId& app_id) {
+    DCHECK(IsAvailable());
+    if (!origin_data_)
+      origin_data_ =
+          std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+    base::Value* app_data = origin_data_->FindKey(app_id);
+    if (!app_data) {
+      app_data = origin_data_->SetKey(
+          app_id, base::Value(base::Value::Type::DICTIONARY));
+    }
+    return *app_data;
+  }
+
+  void Save(const GURL& origin) {
+    DCHECK(IsAvailable());
+    settings_->SetWebsiteSettingDefaultScope(
+        origin, GURL(), CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA,
+        std::string(), std::move(origin_data_));
+  }
+
+ private:
+  HostContentSettingsMap* settings_ = nullptr;
+  std::unique_ptr<base::Value> origin_data_;
+};
+
+}  // namespace
+
+ManifestUpdateManager::ManifestUpdateManager(Profile* profile)
+    : profile_(profile) {}
 
 ManifestUpdateManager::~ManifestUpdateManager() = default;
 
@@ -49,7 +106,7 @@
   if (base::Contains(tasks_, app_id))
     return;
 
-  if (!MaybeConsumeUpdateCheck(app_id)) {
+  if (!MaybeConsumeUpdateCheck(url.GetOrigin(), app_id)) {
     NotifyResult(url, ManifestUpdateResult::kThrottled);
     return;
   }
@@ -73,20 +130,48 @@
   DCHECK(!tasks_.contains(app_id));
 }
 
-bool ManifestUpdateManager::MaybeConsumeUpdateCheck(const AppId& app_id) {
-  base::Time now = time_override_for_testing_.value_or(base::Time::Now());
-  base::Time& last_check_time = last_check_times_[app_id];
+bool ManifestUpdateManager::MaybeConsumeUpdateCheck(const GURL& origin,
+                                                    const AppId& app_id) {
+  base::Optional<base::Time> last_check_time =
+      GetLastUpdateCheckTime(origin, app_id);
+  if (!last_check_time)
+    return false;
 
+  base::Time now = time_override_for_testing_.value_or(base::Time::Now());
   // Throttling updates to at most once per day is consistent with Android.
   // See |UPDATE_INTERVAL| in WebappDataStorage.java.
   constexpr base::TimeDelta kDelayBetweenChecks = base::TimeDelta::FromDays(1);
-  if (now < last_check_time + kDelayBetweenChecks)
+  if (now < *last_check_time + kDelayBetweenChecks)
     return false;
 
-  last_check_time = now;
+  SetLastUpdateCheckTime(origin, app_id, now);
   return true;
 }
 
+base::Optional<base::Time> ManifestUpdateManager::GetLastUpdateCheckTime(
+    const GURL& origin,
+    const AppId& app_id) const {
+  AppPrefs app_prefs(profile_, origin);
+  if (!app_prefs.IsAvailable())
+    return base::nullopt;
+  const base::Value* app_data = app_prefs.GetAppData(app_id);
+  if (!app_data)
+    return base::Time();
+  return util::ValueToTime(app_data->FindKey(kLastUpdateCheckKey))
+      .value_or(base::Time());
+}
+
+void ManifestUpdateManager::SetLastUpdateCheckTime(const GURL& origin,
+                                                   const AppId& app_id,
+                                                   base::Time time) {
+  AppPrefs app_prefs(profile_, origin);
+  if (!app_prefs.IsAvailable())
+    return;
+  base::Value& app_data = app_prefs.GetAppDataMutable(app_id);
+  app_data.SetKey(kLastUpdateCheckKey, util::TimeToValue(time));
+  app_prefs.Save(origin);
+}
+
 void ManifestUpdateManager::OnUpdateStopped(const ManifestUpdateTask& task,
                                             ManifestUpdateResult result) {
   DCHECK_EQ(&task, tasks_[task.app_id()].get());
diff --git a/chrome/browser/web_applications/components/manifest_update_manager.h b/chrome/browser/web_applications/components/manifest_update_manager.h
index de7c0de..c6430a6 100644
--- a/chrome/browser/web_applications/components/manifest_update_manager.h
+++ b/chrome/browser/web_applications/components/manifest_update_manager.h
@@ -17,6 +17,8 @@
 #include "chrome/browser/web_applications/components/manifest_update_task.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 
+class Profile;
+
 namespace content {
 class WebContents;
 }
@@ -32,7 +34,7 @@
 // of being triggered by page loads.
 class ManifestUpdateManager final : public AppRegistrarObserver {
  public:
-  ManifestUpdateManager();
+  explicit ManifestUpdateManager(Profile* profile);
   ~ManifestUpdateManager() override;
 
   void SetSubsystems(AppRegistrar* registrar,
@@ -61,11 +63,17 @@
   }
 
  private:
-  bool MaybeConsumeUpdateCheck(const AppId& app_id);
+  bool MaybeConsumeUpdateCheck(const GURL& origin, const AppId& app_id);
+  base::Optional<base::Time> GetLastUpdateCheckTime(const GURL& origin,
+                                                    const AppId& app_id) const;
+  void SetLastUpdateCheckTime(const GURL& origin,
+                              const AppId& app_id,
+                              base::Time time);
   void OnUpdateStopped(const ManifestUpdateTask& task,
                        ManifestUpdateResult result);
   void NotifyResult(const GURL& url, ManifestUpdateResult result);
 
+  Profile* const profile_ = nullptr;
   AppRegistrar* registrar_ = nullptr;
   WebAppUiManager* ui_manager_ = nullptr;
   InstallManager* install_manager_ = nullptr;
@@ -74,9 +82,6 @@
 
   base::flat_map<AppId, std::unique_ptr<ManifestUpdateTask>> tasks_;
 
-  // TODO(crbug.com/926083): Store this in prefs instead of RAM.
-  base::flat_map<AppId, base::Time> last_check_times_;
-
   base::Optional<base::Time> time_override_for_testing_;
   ResultCallback result_callback_for_testing_;
 
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index c1dbccf8..65d4719a 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -159,7 +159,7 @@
   audio_focus_id_map_ = std::make_unique<WebAppAudioFocusIdMap>();
   ui_manager_ = WebAppUiManager::Create(profile);
   install_manager_ = std::make_unique<WebAppInstallManager>(profile);
-  manifest_update_manager_ = std::make_unique<ManifestUpdateManager>();
+  manifest_update_manager_ = std::make_unique<ManifestUpdateManager>(profile);
   pending_app_manager_ = std::make_unique<PendingAppManagerImpl>(profile);
   external_web_app_manager_ = std::make_unique<ExternalWebAppManager>(profile);
   system_web_app_manager_ = std::make_unique<SystemWebAppManager>(profile);
diff --git a/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc b/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc
index 04df59b..0ea9cc1 100644
--- a/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc
+++ b/chrome/chrome_cleaner/engines/controllers/extension_removal_unittest.cc
@@ -254,10 +254,10 @@
     CHECK_EQ(RESULT_CODE_SUCCESS,
              StartSandboxTarget(MakeCmdLine("EngineSandboxMain"),
                                 &engine_setup_hooks, SandboxType::kTest));
-    json_parser_ptr_ = std::make_unique<chrome_cleaner::UniqueParserPtr>(
-        parser_setup_hooks.TakeParserPtr());
+    json_parser_ = std::make_unique<chrome_cleaner::RemoteParserPtr>(
+        parser_setup_hooks.TakeParserRemote());
     return std::make_unique<chrome_cleaner::SandboxedJsonParser>(
-        mojo_task_runner_.get(), json_parser_ptr_.get()->get());
+        mojo_task_runner_.get(), json_parser_.get()->get());
   }
 
  protected:
@@ -272,7 +272,7 @@
   testing::NiceMock<MockLoggingService> mock_logging_service_;
   base::FilePath fake_apps_dir_;
   base::FilePath chrome_dir_;
-  std::unique_ptr<chrome_cleaner::UniqueParserPtr> json_parser_ptr_;
+  std::unique_ptr<chrome_cleaner::RemoteParserPtr> json_parser_;
   scoped_refptr<chrome_cleaner::EngineClient> engine_client_;
   chrome_cleaner::TestPUPData test_pup_data_;
   std::unique_ptr<ScopedFile> uws_A_;
diff --git a/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc b/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc
index 0cedc2a..fc616d08 100644
--- a/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc
+++ b/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc
@@ -212,21 +212,20 @@
   chrome_cleaner::SandboxConnectionErrorCallback connection_error_callback =
       main_controller.GetSandboxConnectionErrorCallback();
 
-  // Initialize a null UniqueParserPtr to be set by SpawnParserSandbox.
-  chrome_cleaner::UniqueParserPtr parser_ptr(
-      nullptr, base::OnTaskRunnerDeleter(nullptr));
+  // Initialize a null RemoteParserPtr to be set by SpawnParserSandbox.
+  chrome_cleaner::RemoteParserPtr parser(nullptr,
+                                         base::OnTaskRunnerDeleter(nullptr));
   chrome_cleaner::ResultCode init_result = chrome_cleaner::SpawnParserSandbox(
-      shutdown_sequence.mojo_task_runner, connection_error_callback,
-      &parser_ptr);
+      shutdown_sequence.mojo_task_runner, connection_error_callback, &parser);
   if (init_result != chrome_cleaner::RESULT_CODE_SUCCESS) {
     return init_result;
   }
   std::unique_ptr<chrome_cleaner::SandboxedJsonParser> json_parser =
       std::make_unique<chrome_cleaner::SandboxedJsonParser>(
-          shutdown_sequence.mojo_task_runner.get(), parser_ptr.get());
+          shutdown_sequence.mojo_task_runner.get(), parser.get());
   std::unique_ptr<chrome_cleaner::SandboxedShortcutParser> shortcut_parser =
       std::make_unique<chrome_cleaner::SandboxedShortcutParser>(
-          shutdown_sequence.mojo_task_runner.get(), parser_ptr.get());
+          shutdown_sequence.mojo_task_runner.get(), parser.get());
 
   chrome_cleaner::Settings* settings = chrome_cleaner::Settings::GetInstance();
   if (!chrome_cleaner::IsSupportedEngine(settings->engine())) {
diff --git a/chrome/chrome_cleaner/executables/chrome_reporter_main.cc b/chrome/chrome_cleaner/executables/chrome_reporter_main.cc
index b86d1c0..eabbcaf7 100644
--- a/chrome/chrome_cleaner/executables/chrome_reporter_main.cc
+++ b/chrome/chrome_cleaner/executables/chrome_reporter_main.cc
@@ -286,17 +286,17 @@
   DCHECK(succeeded) << "TaskScheduler::Initialize() failed";
 
   // Initialize the sandbox for the shortcut parser.
-  chrome_cleaner::UniqueParserPtr parser_ptr(
-      nullptr, base::OnTaskRunnerDeleter(nullptr));
+  chrome_cleaner::RemoteParserPtr parser(nullptr,
+                                         base::OnTaskRunnerDeleter(nullptr));
   chrome_cleaner::ResultCode parser_result_code =
       chrome_cleaner::SpawnParserSandbox(
           shutdown_sequence.mojo_task_runner.get(),
-          sandbox_connection_error_callback, &parser_ptr);
+          sandbox_connection_error_callback, &parser);
   if (parser_result_code != chrome_cleaner::RESULT_CODE_SUCCESS)
     return FinalizeWithResultCode(parser_result_code, &registry_logger);
   std::unique_ptr<chrome_cleaner::ShortcutParserAPI> shortcut_parser =
       std::make_unique<chrome_cleaner::SandboxedShortcutParser>(
-          shutdown_sequence.mojo_task_runner.get(), parser_ptr.get());
+          shutdown_sequence.mojo_task_runner.get(), parser.get());
 
   std::unique_ptr<chrome_cleaner::ScannerController> scanner_controller =
       std::make_unique<chrome_cleaner::ScannerControllerImpl>(
diff --git a/chrome/chrome_cleaner/parsers/broker/json_parser_sandbox_setup_unittest.cc b/chrome/chrome_cleaner/parsers/broker/json_parser_sandbox_setup_unittest.cc
index e9b6ff5..94cf2bc 100644
--- a/chrome/chrome_cleaner/parsers/broker/json_parser_sandbox_setup_unittest.cc
+++ b/chrome/chrome_cleaner/parsers/broker/json_parser_sandbox_setup_unittest.cc
@@ -8,11 +8,11 @@
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
 #include "base/time/time.h"
-#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h"
 #include "chrome/chrome_cleaner/parsers/target/sandbox_setup.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
@@ -31,7 +31,7 @@
 class JsonParserSandboxSetupTest : public base::MultiProcessTest {
  public:
   JsonParserSandboxSetupTest()
-      : parser_ptr_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {}
+      : parser_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {}
 
   void SetUp() override {
     mojo_task_runner_ = MojoTaskRunner::Create();
@@ -41,12 +41,12 @@
     ASSERT_EQ(RESULT_CODE_SUCCESS,
               StartSandboxTarget(MakeCmdLine("JsonParserSandboxTargetMain"),
                                  &setup_hooks, SandboxType::kTest));
-    parser_ptr_ = setup_hooks.TakeParserPtr();
+    parser_ = setup_hooks.TakeParserRemote();
   }
 
  protected:
   scoped_refptr<MojoTaskRunner> mojo_task_runner_;
-  UniqueParserPtr parser_ptr_;
+  RemoteParserPtr parser_;
 };
 
 void ParseCallbackExpectedKeyValue(const std::string& expected_key,
@@ -93,15 +93,14 @@
                      WaitableEvent::InitialState::NOT_SIGNALED);
 
   mojo_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(
-                     [](mojom::ParserPtr* parser_ptr, WaitableEvent* done) {
-                       (*parser_ptr)
-                           ->ParseJson(
-                               kTestText,
-                               base::BindOnce(&ParseCallbackExpectedKeyValue,
-                                              kTestKey, kTestValue, done));
-                     },
-                     parser_ptr_.get(), &done));
+      FROM_HERE,
+      base::BindOnce(
+          [](mojo::Remote<mojom::Parser>* parser, WaitableEvent* done) {
+            (*parser)->ParseJson(kTestText,
+                                 base::BindOnce(&ParseCallbackExpectedKeyValue,
+                                                kTestKey, kTestValue, done));
+          },
+          parser_.get(), &done));
   EXPECT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
 }
 
@@ -112,12 +111,12 @@
   mojo_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
-          [](mojom::ParserPtr* parser_ptr, WaitableEvent* done) {
-            (*parser_ptr)
-                ->ParseJson(kInvalidText,
-                            base::BindOnce(&ParseCallbackExpectedError, done));
+          [](mojo::Remote<mojom::Parser>* parser, WaitableEvent* done) {
+            (*parser)->ParseJson(
+                kInvalidText,
+                base::BindOnce(&ParseCallbackExpectedError, done));
           },
-          parser_ptr_.get(), &done));
+          parser_.get(), &done));
   EXPECT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
 }
 
diff --git a/chrome/chrome_cleaner/parsers/broker/lnk_parser_sandbox_setup_unittest.cc b/chrome/chrome_cleaner/parsers/broker/lnk_parser_sandbox_setup_unittest.cc
index 1ddbb727..6d89d3e4 100644
--- a/chrome/chrome_cleaner/parsers/broker/lnk_parser_sandbox_setup_unittest.cc
+++ b/chrome/chrome_cleaner/parsers/broker/lnk_parser_sandbox_setup_unittest.cc
@@ -28,7 +28,7 @@
 class LnkParserSandboxSetupTest : public base::MultiProcessTest {
  public:
   LnkParserSandboxSetupTest()
-      : parser_ptr_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {}
+      : parser_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {}
 
   void SetUp() override {
     mojo_task_runner_ = MojoTaskRunner::Create();
@@ -38,9 +38,9 @@
     ASSERT_EQ(RESULT_CODE_SUCCESS,
               StartSandboxTarget(MakeCmdLine("LnkParserSandboxTargetMain"),
                                  &setup_hooks, SandboxType::kTest));
-    parser_ptr_ = setup_hooks.TakeParserPtr();
+    parser_ = setup_hooks.TakeParserRemote();
     shortcut_parser_ = std::make_unique<SandboxedShortcutParser>(
-        mojo_task_runner_.get(), parser_ptr_.get());
+        mojo_task_runner_.get(), parser_.get());
 
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.GetPath(),
@@ -49,7 +49,7 @@
 
  protected:
   scoped_refptr<MojoTaskRunner> mojo_task_runner_;
-  UniqueParserPtr parser_ptr_;
+  RemoteParserPtr parser_;
 
   std::unique_ptr<ShortcutParserAPI> shortcut_parser_;
   ParsedLnkFile test_parsed_shortcut_;
diff --git a/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.cc b/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.cc
index a0763a1d..2cd03ef 100644
--- a/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.cc
+++ b/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.cc
@@ -8,7 +8,10 @@
 
 #include "base/bind.h"
 #include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
+#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/settings/settings_types.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chrome_cleaner {
 
@@ -17,47 +20,46 @@
     base::OnceClosure connection_error_handler)
     : mojo_task_runner_(mojo_task_runner),
       connection_error_handler_(std::move(connection_error_handler)),
-      parser_ptr_(new mojom::ParserPtr(),
-                  base::OnTaskRunnerDeleter(mojo_task_runner_)) {}
+      parser_(new mojo::Remote<mojom::Parser>(),
+              base::OnTaskRunnerDeleter(mojo_task_runner_)) {}
 
 ParserSandboxSetupHooks::~ParserSandboxSetupHooks() = default;
 
 ResultCode ParserSandboxSetupHooks::UpdateSandboxPolicy(
     sandbox::TargetPolicy* policy,
     base::CommandLine* command_line) {
-  // Unretained reference is safe because the parser_ptr is taken by the
+  // Unretained reference is safe because the parser remote is taken by the
   // caller and is expected to retain it for the life of the sandboxed process.
   mojo_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&ParserSandboxSetupHooks::BindParserPtr,
+      FROM_HERE, base::BindOnce(&ParserSandboxSetupHooks::BindParserRemote,
                                 base::Unretained(this),
                                 SetupSandboxMessagePipe(policy, command_line),
-                                base::Unretained(parser_ptr_.get())));
+                                base::Unretained(parser_.get())));
 
   return RESULT_CODE_SUCCESS;
 }
 
-void ParserSandboxSetupHooks::BindParserPtr(
+void ParserSandboxSetupHooks::BindParserRemote(
     mojo::ScopedMessagePipeHandle pipe_handle,
-    mojom::ParserPtr* parser_ptr) {
-  parser_ptr->Bind(mojom::ParserPtrInfo(std::move(pipe_handle), 0));
-  parser_ptr->set_connection_error_handler(
-      std::move(connection_error_handler_));
+    mojo::Remote<mojom::Parser>* parser) {
+  parser->Bind(mojo::PendingRemote<mojom::Parser>(std::move(pipe_handle), 0));
+  parser->set_disconnect_handler(std::move(connection_error_handler_));
 }
 
-UniqueParserPtr ParserSandboxSetupHooks::TakeParserPtr() {
-  return std::move(parser_ptr_);
+RemoteParserPtr ParserSandboxSetupHooks::TakeParserRemote() {
+  return std::move(parser_);
 }
 
 ResultCode SpawnParserSandbox(
     scoped_refptr<MojoTaskRunner> mojo_task_runner,
     const SandboxConnectionErrorCallback& connection_error_callback,
-    UniqueParserPtr* parser_ptr) {
+    RemoteParserPtr* parser) {
   auto error_handler =
       base::BindOnce(connection_error_callback, SandboxType::kParser);
   ParserSandboxSetupHooks setup_hooks(mojo_task_runner,
                                       std::move(error_handler));
   ResultCode result_code = SpawnSandbox(&setup_hooks, SandboxType::kParser);
-  *parser_ptr = setup_hooks.TakeParserPtr();
+  *parser = setup_hooks.TakeParserRemote();
 
   return result_code;
 }
diff --git a/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h b/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h
index f825d55b5..c9414589 100644
--- a/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h
+++ b/chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h
@@ -8,17 +8,18 @@
 #include <memory>
 
 #include "base/command_line.h"
-#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/ipc/mojo_sandbox_hooks.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
 #include "chrome/chrome_cleaner/ipc/sandbox.h"
+#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "components/chrome_cleaner/public/constants/result_codes.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace chrome_cleaner {
 
-using UniqueParserPtr =
-    std::unique_ptr<mojom::ParserPtr, base::OnTaskRunnerDeleter>;
+using RemoteParserPtr =
+    std::unique_ptr<mojo::Remote<mojom::Parser>, base::OnTaskRunnerDeleter>;
 
 // Hooks to spawn a new sandboxed Parser process and bind a Mojo interface
 // pointer to the sandboxed implementation.
@@ -28,31 +29,31 @@
                           base::OnceClosure connection_error_handler);
   ~ParserSandboxSetupHooks() override;
 
-  // Transfers ownership of |parser_ptr_| to the caller.
-  UniqueParserPtr TakeParserPtr();
+  // Transfers ownership of |parser_| to the caller.
+  RemoteParserPtr TakeParserRemote();
 
   // SandboxSetupHooks
   ResultCode UpdateSandboxPolicy(sandbox::TargetPolicy* policy,
                                  base::CommandLine* command_line) override;
 
  private:
-  void BindParserPtr(mojo::ScopedMessagePipeHandle pipe_handle,
-                     mojom::ParserPtr* parser_ptr);
+  void BindParserRemote(mojo::ScopedMessagePipeHandle pipe_handle,
+                        mojo::Remote<mojom::Parser>* parser);
 
   scoped_refptr<MojoTaskRunner> mojo_task_runner_;
   base::OnceClosure connection_error_handler_;
 
-  UniqueParserPtr parser_ptr_;
+  RemoteParserPtr parser_;
 
   DISALLOW_COPY_AND_ASSIGN(ParserSandboxSetupHooks);
 };
 
 // Spawn a sandboxed process with type kParser, and return the bound
-// |parser_ptr|.
+// |parser|.
 ResultCode SpawnParserSandbox(
     scoped_refptr<MojoTaskRunner> mojo_task_runner,
     const SandboxConnectionErrorCallback& connection_error_callback,
-    UniqueParserPtr* parser_ptr);
+    RemoteParserPtr* parser);
 
 }  // namespace chrome_cleaner
 
diff --git a/chrome/chrome_cleaner/parsers/json_parser/json_splicer_unittest.cc b/chrome/chrome_cleaner/parsers/json_parser/json_splicer_unittest.cc
index 117f505..81e1115e 100644
--- a/chrome/chrome_cleaner/parsers/json_parser/json_splicer_unittest.cc
+++ b/chrome/chrome_cleaner/parsers/json_parser/json_splicer_unittest.cc
@@ -13,11 +13,11 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/test/test_timeouts.h"
 #include "base/values.h"
-#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h"
 #include "chrome/chrome_cleaner/parsers/target/parser_impl.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome_cleaner {
@@ -62,24 +62,25 @@
  public:
   JsonSplicerImplTest()
       : task_runner_(MojoTaskRunner::Create()),
-        parser_ptr_(new mojom::ParserPtr(),
-                    base::OnTaskRunnerDeleter(task_runner_)),
+        parser_(new mojo::Remote<mojom::Parser>(),
+                base::OnTaskRunnerDeleter(task_runner_)),
         parser_impl_(nullptr, base::OnTaskRunnerDeleter(task_runner_)),
-        sandboxed_json_parser_(task_runner_.get(), parser_ptr_.get()) {
-    task_runner_->PostTask(
-        FROM_HERE, BindOnce(BindParser, parser_ptr_.get(), &parser_impl_));
+        sandboxed_json_parser_(task_runner_.get(), parser_.get()) {
+    task_runner_->PostTask(FROM_HERE,
+                           BindOnce(BindParser, parser_.get(), &parser_impl_));
   }
 
  protected:
   static void BindParser(
-      mojom::ParserPtr* json_parser,
+      mojo::Remote<mojom::Parser>* json_parser,
       std::unique_ptr<ParserImpl, base::OnTaskRunnerDeleter>* parser_impl) {
-    parser_impl->reset(
-        new ParserImpl(mojo::MakeRequest(json_parser), base::DoNothing()));
+    parser_impl->reset(new ParserImpl(json_parser->BindNewPipeAndPassReceiver(),
+                                      base::DoNothing()));
   }
 
   scoped_refptr<MojoTaskRunner> task_runner_;
-  std::unique_ptr<mojom::ParserPtr, base::OnTaskRunnerDeleter> parser_ptr_;
+  std::unique_ptr<mojo::Remote<mojom::Parser>, base::OnTaskRunnerDeleter>
+      parser_;
   std::unique_ptr<ParserImpl, base::OnTaskRunnerDeleter> parser_impl_;
   SandboxedJsonParser sandboxed_json_parser_;
 };
diff --git a/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.cc b/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.cc
index 82808da..442d74a 100644
--- a/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.cc
+++ b/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.cc
@@ -4,23 +4,25 @@
 
 #include "chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h"
 
+#include <utility>
+
 #include "base/bind.h"
 
 namespace chrome_cleaner {
 
 SandboxedJsonParser::SandboxedJsonParser(MojoTaskRunner* mojo_task_runner,
-                                         mojom::ParserPtr* parser_ptr)
-    : mojo_task_runner_(mojo_task_runner), parser_ptr_(parser_ptr) {}
+                                         mojo::Remote<mojom::Parser>* parser)
+    : mojo_task_runner_(mojo_task_runner), parser_(parser) {}
 
 void SandboxedJsonParser::Parse(const std::string& json,
                                 ParseDoneCallback callback) {
   mojo_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(
-                     [](mojom::ParserPtr* parser_ptr, const std::string& json,
-                        ParseDoneCallback callback) {
-                       (*parser_ptr)->ParseJson(json, std::move(callback));
+                     [](mojo::Remote<mojom::Parser>* parser,
+                        const std::string& json, ParseDoneCallback callback) {
+                       (*parser)->ParseJson(json, std::move(callback));
                      },
-                     parser_ptr_, json, std::move(callback)));
+                     parser_, json, std::move(callback)));
 }
 
 }  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h b/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h
index 3fc6583..189991b 100644
--- a/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h
+++ b/chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h
@@ -5,24 +5,25 @@
 #ifndef CHROME_CHROME_CLEANER_PARSERS_JSON_PARSER_SANDBOXED_JSON_PARSER_H_
 #define CHROME_CHROME_CLEANER_PARSERS_JSON_PARSER_SANDBOXED_JSON_PARSER_H_
 
-#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/parsers/json_parser/json_parser_api.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chrome_cleaner {
 
 // An implementation of JsonParserAPI to wrap a MojoTaskRunner and
-// JsonParserPtr. Parses via |parser_ptr_| on the |mojo_task_runner_|.
+// JsonParserPtr. Parses via |parser_| on the |mojo_task_runner_|.
 // TODO(joenotcharles): Move this class to chrome_cleaner/parsers/broker.
 class SandboxedJsonParser : public JsonParserAPI {
  public:
   SandboxedJsonParser(MojoTaskRunner* mojo_task_runner,
-                      mojom::ParserPtr* parser_ptr);
+                      mojo::Remote<mojom::Parser>* parser);
   void Parse(const std::string& json, ParseDoneCallback callback) override;
 
  private:
   MojoTaskRunner* mojo_task_runner_;
-  mojom::ParserPtr* parser_ptr_;
+  mojo::Remote<mojom::Parser>* parser_;
 };
 
 }  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.cc b/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.cc
index eb16eb9..d4d603b 100644
--- a/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.cc
+++ b/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.cc
@@ -17,14 +17,15 @@
 #include "base/win/scoped_handle.h"
 #include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
 namespace chrome_cleaner {
 
 SandboxedShortcutParser::SandboxedShortcutParser(
     MojoTaskRunner* mojo_task_runner,
-    mojom::ParserPtr* parser_ptr)
-    : mojo_task_runner_(mojo_task_runner), parser_ptr_(parser_ptr) {}
+    mojo::Remote<mojom::Parser>* parser)
+    : mojo_task_runner_(mojo_task_runner), parser_(parser) {}
 
 void SandboxedShortcutParser::ParseShortcut(
     base::win::ScopedHandle shortcut_handle,
@@ -32,12 +33,11 @@
   mojo_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
-          [](mojom::ParserPtr* parser_ptr, mojo::ScopedHandle handle,
+          [](mojo::Remote<mojom::Parser>* parser, mojo::ScopedHandle handle,
              mojom::Parser::ParseShortcutCallback callback) {
-            (*parser_ptr)
-                ->ParseShortcut(std::move(handle), std::move(callback));
+            (*parser)->ParseShortcut(std::move(handle), std::move(callback));
           },
-          parser_ptr_, mojo::WrapPlatformFile(shortcut_handle.Take()),
+          parser_, mojo::WrapPlatformFile(shortcut_handle.Take()),
           std::move(callback)));
 }
 
diff --git a/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h b/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h
index a5a0ca2..651cb90b 100644
--- a/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h
+++ b/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h
@@ -8,18 +8,19 @@
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/win/scoped_handle.h"
-#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
 #include "chrome/chrome_cleaner/os/file_path_set.h"
 #include "chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.h"
 #include "chrome/chrome_cleaner/parsers/shortcut_parser/broker/shortcut_parser_api.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chrome_cleaner {
 
 class SandboxedShortcutParser : public ShortcutParserAPI {
  public:
   SandboxedShortcutParser(MojoTaskRunner* mojo_task_runner,
-                          mojom::ParserPtr* parser_ptr);
+                          mojo::Remote<mojom::Parser>* parser);
 
   // ShortcutParserAPI
   void FindAndParseChromeShortcutsInFoldersAsync(
@@ -48,7 +49,7 @@
 
   base::Lock lock_;
   MojoTaskRunner* mojo_task_runner_;
-  mojom::ParserPtr* parser_ptr_;
+  mojo::Remote<mojom::Parser>* parser_;
 };
 
 }  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser_unittest.cc b/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser_unittest.cc
index 64ac91e..5182356 100644
--- a/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser_unittest.cc
+++ b/chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser_unittest.cc
@@ -36,7 +36,7 @@
 class SandboxedShortcutParserTest : public base::MultiProcessTest {
  public:
   SandboxedShortcutParserTest()
-      : parser_ptr_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
+      : parser_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
         temp_dirs_with_chrome_lnk_(kDirQuantity) {}
 
   void SetUp() override {
@@ -48,9 +48,9 @@
         RESULT_CODE_SUCCESS,
         StartSandboxTarget(MakeCmdLine("SandboxedShortcutParserTargetMain"),
                            &setup_hooks, SandboxType::kTest));
-    parser_ptr_ = setup_hooks.TakeParserPtr();
+    parser_ = setup_hooks.TakeParserRemote();
     shortcut_parser_ = std::make_unique<SandboxedShortcutParser>(
-        mojo_task_runner_.get(), parser_ptr_.get());
+        mojo_task_runner_.get(), parser_.get());
 
     ASSERT_TRUE(temp_dir_without_chrome_lnk_.CreateUniqueTempDir());
     ASSERT_TRUE(base::CreateTemporaryFileInDir(
@@ -87,7 +87,7 @@
   size_t shortcut_quantity_ = 0;
 
   scoped_refptr<MojoTaskRunner> mojo_task_runner_;
-  UniqueParserPtr parser_ptr_;
+  RemoteParserPtr parser_;
   std::unique_ptr<SandboxedShortcutParser> shortcut_parser_;
 
   FilePathSet fake_chrome_exe_file_path_set_;
diff --git a/chrome/chrome_cleaner/parsers/target/parser_impl.cc b/chrome/chrome_cleaner/parsers/target/parser_impl.cc
index b501230..5f9aa238 100644
--- a/chrome/chrome_cleaner/parsers/target/parser_impl.cc
+++ b/chrome/chrome_cleaner/parsers/target/parser_impl.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/chrome_cleaner/parsers/target/parser_impl.h"
 
+#include <utility>
+
 #include "base/json/json_reader.h"
 #include "base/values.h"
 #include "base/win/scoped_handle.h"
@@ -12,10 +14,10 @@
 
 namespace chrome_cleaner {
 
-ParserImpl::ParserImpl(mojom::ParserRequest request,
+ParserImpl::ParserImpl(mojo::PendingReceiver<mojom::Parser> receiver,
                        base::OnceClosure connection_error_handler)
-    : binding_(this, std::move(request)) {
-  binding_.set_connection_error_handler(std::move(connection_error_handler));
+    : receiver_(this, std::move(receiver)) {
+  receiver_.set_disconnect_handler(std::move(connection_error_handler));
 }
 
 ParserImpl::~ParserImpl() = default;
diff --git a/chrome/chrome_cleaner/parsers/target/parser_impl.h b/chrome/chrome_cleaner/parsers/target/parser_impl.h
index d774de1..b9a1d80 100644
--- a/chrome/chrome_cleaner/parsers/target/parser_impl.h
+++ b/chrome/chrome_cleaner/parsers/target/parser_impl.h
@@ -6,13 +6,14 @@
 #define CHROME_CHROME_CLEANER_PARSERS_TARGET_PARSER_IMPL_H_
 
 #include "chrome/chrome_cleaner/mojom/parser_interface.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chrome_cleaner {
 
 class ParserImpl : public mojom::Parser {
  public:
-  explicit ParserImpl(mojom::ParserRequest request,
+  explicit ParserImpl(mojo::PendingReceiver<mojom::Parser> receiver,
                       base::OnceClosure connection_error_handler);
   ~ParserImpl() override;
 
@@ -25,7 +26,7 @@
                      ParserImpl::ParseShortcutCallback callback) override;
 
  private:
-  mojo::Binding<mojom::Parser> binding_;
+  mojo::Receiver<mojom::Parser> receiver_;
 };
 
 }  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/parsers/target/parser_impl_unittest.cc b/chrome/chrome_cleaner/parsers/target/parser_impl_unittest.cc
index 89b52d81..fda63a0 100644
--- a/chrome/chrome_cleaner/parsers/target/parser_impl_unittest.cc
+++ b/chrome/chrome_cleaner/parsers/target/parser_impl_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h"
 #include "chrome/chrome_cleaner/parsers/shortcut_parser/sandboxed_lnk_parser_test_util.h"
 #include "chrome/chrome_cleaner/parsers/shortcut_parser/target/lnk_parser.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -41,11 +41,11 @@
  public:
   ParserImplTest()
       : task_runner_(MojoTaskRunner::Create()),
-        parser_ptr_(new mojom::ParserPtr(),
-                    base::OnTaskRunnerDeleter(task_runner_)),
+        parser_(new mojo::Remote<mojom::Parser>(),
+                base::OnTaskRunnerDeleter(task_runner_)),
         parser_impl_(nullptr, base::OnTaskRunnerDeleter(task_runner_)),
-        sandboxed_json_parser_(task_runner_.get(), parser_ptr_.get()),
-        shortcut_parser_(task_runner_.get(), parser_ptr_.get()) {}
+        sandboxed_json_parser_(task_runner_.get(), parser_.get()),
+        shortcut_parser_(task_runner_.get(), parser_.get()) {}
 
   void SetUp() override {
     BindParser();
@@ -59,17 +59,18 @@
     task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(
-            [](mojom::ParserPtr* parser,
+            [](mojo::Remote<mojom::Parser>* parser,
                std::unique_ptr<ParserImpl, base::OnTaskRunnerDeleter>*
                    parser_impl) {
-              parser_impl->reset(
-                  new ParserImpl(mojo::MakeRequest(parser), base::DoNothing()));
+              parser_impl->reset(new ParserImpl(
+                  parser->BindNewPipeAndPassReceiver(), base::DoNothing()));
             },
-            parser_ptr_.get(), &parser_impl_));
+            parser_.get(), &parser_impl_));
   }
 
   scoped_refptr<MojoTaskRunner> task_runner_;
-  std::unique_ptr<mojom::ParserPtr, base::OnTaskRunnerDeleter> parser_ptr_;
+  std::unique_ptr<mojo::Remote<mojom::Parser>, base::OnTaskRunnerDeleter>
+      parser_;
   std::unique_ptr<ParserImpl, base::OnTaskRunnerDeleter> parser_impl_;
   SandboxedJsonParser sandboxed_json_parser_;
 
diff --git a/chrome/chrome_cleaner/parsers/target/sandbox_setup.cc b/chrome/chrome_cleaner/parsers/target/sandbox_setup.cc
index 6ca95e1..ec06298f 100644
--- a/chrome/chrome_cleaner/parsers/target/sandbox_setup.cc
+++ b/chrome/chrome_cleaner/parsers/target/sandbox_setup.cc
@@ -14,6 +14,7 @@
 #include "chrome/chrome_cleaner/os/early_exit.h"
 #include "chrome/chrome_cleaner/parsers/target/parser_impl.h"
 #include "components/chrome_cleaner/public/constants/result_codes.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace chrome_cleaner {
 
@@ -37,7 +38,8 @@
   // SandboxTargetHooks
   ResultCode TargetDroppedPrivileges(
       const base::CommandLine& command_line) override {
-    mojom::ParserRequest request(ExtractSandboxMessagePipe(command_line));
+    mojo::PendingReceiver<mojom::Parser> receiver(
+        ExtractSandboxMessagePipe(command_line));
 
     // This loop will run forever. Once the communication channel with the
     // broker process is broken, mojo error handler will abort this process.
@@ -45,14 +47,14 @@
     mojo_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&ParserSandboxTargetHooks::CreateParserImpl,
-                       base::Unretained(this), base::Passed(&request)));
+                       base::Unretained(this), base::Passed(&receiver)));
     run_loop.Run();
     return RESULT_CODE_SUCCESS;
   }
 
  private:
-  void CreateParserImpl(mojom::ParserRequest request) {
-    parser_impl_ = std::make_unique<ParserImpl>(std::move(request),
+  void CreateParserImpl(mojo::PendingReceiver<mojom::Parser> receiver) {
+    parser_impl_ = std::make_unique<ParserImpl>(std::move(receiver),
                                                 base::BindOnce(&EarlyExit, 1));
   }
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 031a22b3..df65c42 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -698,6 +698,12 @@
 const base::Feature kSoundContentSetting{"SoundContentSetting",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables or defaults splittup up server (not proxy) entries in the
+// HttpAuthCache.
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kSplitAuthCacheByNetworkIsolationKey{
+    "SplitAuthCacheByNetworkIsolationKey", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables filtering URLs by suffix to include subresources that look
 // like image resources for compression. For example,
 // http://chromium.org/image.jpg would be included.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index a964995..c428e4a6 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -423,6 +423,9 @@
 extern const base::Feature kSoundContentSetting;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kSplitAuthCacheByNetworkIsolationKey;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSubresourceRedirectIncludedMediaSuffixes;
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 94d99b68..d8f2349d 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -391,6 +391,9 @@
     static void getAllEnterprisePolicies(
         AllEnterprisePoliciesCallback callback);
 
+    // Refreshes the Enterprise Policies.
+    static void refreshEnterprisePolicies(VoidCallback callback);
+
     // Simulates a memory access bug for asan testing.
     static void simulateAsanMemoryBug();
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 492f8e7e..382ed88 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1060,7 +1060,6 @@
       "../browser/prefetch/prefetch_browsertest.cc",
       "../browser/prefs/pref_functional_browsertest.cc",
       "../browser/prefs/pref_service_browsertest.cc",
-      "../browser/prefs/synced_pref_change_registrar_browsertest.cc",
       "../browser/prefs/tracked/pref_hash_browsertest.cc",
       "../browser/prerender/prerender_browsertest.cc",
       "../browser/prerender/prerender_nostate_prefetch_browsertest.cc",
@@ -1120,6 +1119,7 @@
       "../browser/sessions/tab_restore_service_load_waiter.cc",
       "../browser/sessions/tab_restore_service_load_waiter.h",
       "../browser/sharing/click_to_call/click_to_call_browsertest.cc",
+      "../browser/sharing/shared_clipboard/shared_clipboard_browsertest.cc",
       "../browser/sharing/sharing_browsertest.cc",
       "../browser/sharing/sharing_browsertest.h",
       "../browser/signin/consistency_cookie_browsertest.cc",
@@ -2095,6 +2095,7 @@
         "../browser/chromeos/input_method/textinput_test_helper.h",
         "../browser/chromeos/lock_screen_apps/note_taking_browsertest.cc",
         "../browser/chromeos/logging_browsertest.cc",
+        "../browser/chromeos/login/accessibility_browsertest.cc",
         "../browser/chromeos/login/active_directory_login_browsertest.cc",
         "../browser/chromeos/login/arc_terms_of_service_browsertest.cc",
         "../browser/chromeos/login/auto_launched_kiosk_browsertest.cc",
@@ -5641,6 +5642,7 @@
         "../browser/ui/views/passwords/password_bubble_interactive_uitest.cc",
         "../browser/ui/views/sad_tab_view_interactive_uitest.cc",
         "../browser/ui/views/status_icons/status_tray_state_changer_interactive_uitest_win.cc",
+        "../browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc",
         "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc",
         "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h",
         "../browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
index 1519607b..a4045c9 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
@@ -186,11 +186,6 @@
         }
 
         @Override
-        protected long nativeInit(Profile profile) {
-            return 1;
-        }
-
-        @Override
         public void setProfile(Profile profile) {}
     }
 
@@ -220,11 +215,6 @@
         public void stop(boolean clear) {}
 
         @Override
-        protected long nativeInit(Profile profile) {
-            return 1;
-        }
-
-        @Override
         public void setProfile(Profile profile) {}
     }
 
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js
index bfef5e3..497dd70 100644
--- a/chrome/test/data/extensions/api_test/autotest_private/test.js
+++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -666,6 +666,14 @@
         chrome.test.succeed();
       }));
   },
+  function refreshEnterprisePolicies() {
+    chrome.autotestPrivate.refreshEnterprisePolicies(
+      chrome.test.callbackPass(function() {
+        chrome.test.succeed();
+      })
+    );
+  },
+
 ];
 
 var arcPerformanceTracingTests = [
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 2e4bb8b..366df99 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12603.0.0
\ No newline at end of file
+12605.0.0
\ No newline at end of file
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 98c4fd5..1507c85 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -183,7 +183,7 @@
 
 // Enables or disables the scrollable shelf.
 const base::Feature kShelfScrollable{"ShelfScrollable",
-                                     base::FEATURE_ENABLED_BY_DEFAULT};
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables the shelf hotseat.
 const base::Feature kShelfHotseat{"ShelfHotseat",
diff --git a/chromeos/constants/chromeos_paths.cc b/chromeos/constants/chromeos_paths.cc
index 2388ae8..152fc44 100644
--- a/chromeos/constants/chromeos_paths.cc
+++ b/chromeos/constants/chromeos_paths.cc
@@ -34,6 +34,9 @@
 const base::FilePath::CharType kUpdateRebootNeededUptimeFile[] =
     FILE_PATH_LITERAL("/run/chrome/update_reboot_needed_uptime");
 
+const base::FilePath::CharType kStartupCustomizationManifestFile[] =
+    FILE_PATH_LITERAL("/opt/oem/etc/startup_manifest.json");
+
 const base::FilePath::CharType kDeviceLocalAccountExtensionDir[] =
     FILE_PATH_LITERAL("/var/cache/device_local_account_extensions");
 
@@ -75,6 +78,9 @@
     case FILE_UPDATE_REBOOT_NEEDED_UPTIME:
       *result = base::FilePath(kUpdateRebootNeededUptimeFile);
       break;
+    case FILE_STARTUP_CUSTOMIZATION_MANIFEST:
+      *result = base::FilePath(kStartupCustomizationManifestFile);
+      break;
     case DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS:
       *result = base::FilePath(kDeviceLocalAccountExtensionDir);
       break;
diff --git a/chromeos/constants/chromeos_paths.h b/chromeos/constants/chromeos_paths.h
index 0bf26b08..86c49eb 100644
--- a/chromeos/constants/chromeos_paths.h
+++ b/chromeos/constants/chromeos_paths.h
@@ -29,6 +29,8 @@
                                      // store the uptime at which an update
                                      // became necessary. The file should be
                                      // cleared on boot.
+  FILE_STARTUP_CUSTOMIZATION_MANIFEST,     // Path to OEM partner startup
+                                           // customization manifest.
   DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,     // Directory under which a cache of
                                            // force-installed extensions is
                                            // maintained for each device-local
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index 95d22b7..6239a07 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -113,8 +113,6 @@
     "fake_virtual_file_provider_client.h",
     "fake_vm_plugin_dispatcher_client.cc",
     "fake_vm_plugin_dispatcher_client.h",
-    "fake_wilco_dtc_supportd_client.cc",
-    "fake_wilco_dtc_supportd_client.h",
     "gnubby_client.cc",
     "gnubby_client.h",
     "image_burner_client.cc",
@@ -144,8 +142,6 @@
     "virtual_file_provider_client.h",
     "vm_plugin_dispatcher_client.cc",
     "vm_plugin_dispatcher_client.h",
-    "wilco_dtc_supportd_client.cc",
-    "wilco_dtc_supportd_client.h",
   ]
 }
 
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc
index f6c3bc4..445c832 100644
--- a/chromeos/dbus/dbus_clients_browser.cc
+++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -39,7 +39,6 @@
 #include "chromeos/dbus/fake_smb_provider_client.h"
 #include "chromeos/dbus/fake_virtual_file_provider_client.h"
 #include "chromeos/dbus/fake_vm_plugin_dispatcher_client.h"
-#include "chromeos/dbus/fake_wilco_dtc_supportd_client.h"
 #include "chromeos/dbus/gnubby_client.h"
 #include "chromeos/dbus/image_burner_client.h"
 #include "chromeos/dbus/image_loader_client.h"
@@ -51,7 +50,6 @@
 #include "chromeos/dbus/update_engine_client.h"
 #include "chromeos/dbus/virtual_file_provider_client.h"
 #include "chromeos/dbus/vm_plugin_dispatcher_client.h"
-#include "chromeos/dbus/wilco_dtc_supportd_client.h"
 
 namespace chromeos {
 
@@ -110,8 +108,6 @@
       CREATE_DBUS_CLIENT(VirtualFileProviderClient, use_real_clients);
   vm_plugin_dispatcher_client_ =
       CREATE_DBUS_CLIENT(VmPluginDispatcherClient, use_real_clients);
-  wilco_dtc_supportd_client_ =
-      CREATE_DBUS_CLIENT(WilcoDtcSupportdClient, use_real_clients);
 }
 
 DBusClientsBrowser::~DBusClientsBrowser() = default;
@@ -141,7 +137,6 @@
   update_engine_client_->Init(system_bus);
   virtual_file_provider_client_->Init(system_bus);
   vm_plugin_dispatcher_client_->Init(system_bus);
-  wilco_dtc_supportd_client_->Init(system_bus);
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index bc7e1f29..02ea4eb 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -38,7 +38,6 @@
 class UpdateEngineClient;
 class VirtualFileProviderClient;
 class VmPluginDispatcherClient;
-class WilcoDtcSupportdClient;
 
 // D-Bus clients used only in the browser process.
 // TODO(jamescook): Move this under //chrome/browser. http://crbug.com/647367
@@ -77,7 +76,6 @@
   std::unique_ptr<UpdateEngineClient> update_engine_client_;
   std::unique_ptr<VirtualFileProviderClient> virtual_file_provider_client_;
   std::unique_ptr<VmPluginDispatcherClient> vm_plugin_dispatcher_client_;
-  std::unique_ptr<WilcoDtcSupportdClient> wilco_dtc_supportd_client_;
 
   DISALLOW_COPY_AND_ASSIGN(DBusClientsBrowser);
 };
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index b47d7b0..8d4443b5 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -240,11 +240,6 @@
              : nullptr;
 }
 
-WilcoDtcSupportdClient* DBusThreadManager::GetWilcoDtcSupportdClient() {
-  return clients_browser_ ? clients_browser_->wilco_dtc_supportd_client_.get()
-                          : nullptr;
-}
-
 VmPluginDispatcherClient* DBusThreadManager::GetVmPluginDispatcherClient() {
   return clients_browser_ ? clients_browser_->vm_plugin_dispatcher_client_.get()
                           : nullptr;
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index 3141542..816a8ad 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -56,7 +56,6 @@
 class UpdateEngineClient;
 class VirtualFileProviderClient;
 class VmPluginDispatcherClient;
-class WilcoDtcSupportdClient;
 
 // THIS CLASS IS BEING DEPRECATED. See README.md for guidelines and
 // https://crbug.com/647367 for details.
@@ -138,7 +137,6 @@
   UpdateEngineClient* GetUpdateEngineClient();
   VirtualFileProviderClient* GetVirtualFileProviderClient();
   VmPluginDispatcherClient* GetVmPluginDispatcherClient();
-  WilcoDtcSupportdClient* GetWilcoDtcSupportdClient();
 
   // DEPRECATED, DO NOT USE. The static getter for each of these classes should
   // be used instead. TODO(stevenjb): Remove. https://crbug.com/948390.
diff --git a/chromeos/dbus/wilco_dtc_supportd_client.h b/chromeos/dbus/wilco_dtc_supportd_client.h
deleted file mode 100644
index e4c9cdb..0000000
--- a/chromeos/dbus/wilco_dtc_supportd_client.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 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 CHROMEOS_DBUS_WILCO_DTC_SUPPORTD_CLIENT_H_
-#define CHROMEOS_DBUS_WILCO_DTC_SUPPORTD_CLIENT_H_
-
-#include <memory>
-
-#include "base/component_export.h"
-#include "base/files/scoped_file.h"
-#include "base/macros.h"
-#include "chromeos/dbus/dbus_client.h"
-#include "chromeos/dbus/dbus_method_call_status.h"
-#include "dbus/object_proxy.h"
-
-namespace chromeos {
-
-class COMPONENT_EXPORT(CHROMEOS_DBUS) WilcoDtcSupportdClient
-    : public DBusClient {
- public:
-  // Factory function.
-  static std::unique_ptr<WilcoDtcSupportdClient> Create();
-
-  // Registers |callback| to run when the wilco_dtc_supportd service becomes
-  // available.
-  virtual void WaitForServiceToBeAvailable(
-      WaitForServiceToBeAvailableCallback callback) = 0;
-
-  // Bootstrap the Mojo connection between Chrome and the wilco_dtc_supportd
-  // daemon. |fd| is the file descriptor with the child end of the Mojo pipe.
-  virtual void BootstrapMojoConnection(base::ScopedFD fd,
-                                       VoidDBusMethodCallback callback) = 0;
-
- protected:
-  // Create() should be used instead.
-  WilcoDtcSupportdClient();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WilcoDtcSupportdClient);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_DBUS_WILCO_DTC_SUPPORTD_CLIENT_H_
diff --git a/chromeos/services/ime/ime_service.cc b/chromeos/services/ime/ime_service.cc
index d0fd3e6..098d5ce 100644
--- a/chromeos/services/ime/ime_service.cc
+++ b/chromeos/services/ime/ime_service.cc
@@ -33,7 +33,8 @@
 }  // namespace
 
 ImeService::ImeService(mojo::PendingReceiver<mojom::ImeService> receiver)
-    : receiver_(this, std::move(receiver)) {
+    : receiver_(this, std::move(receiver)),
+      main_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
   input_engine_ = chromeos::features::IsImeDecoderWithSandboxEnabled()
                       ? std::make_unique<DecoderEngine>(this)
                       : std::make_unique<InputEngine>();
@@ -77,7 +78,7 @@
 }
 
 const char* ImeService::GetImeGlobalDir() {
-  // Global IME data dir will not be supported yet.
+  // Global IME data is supported yet.
   return "";
 }
 
@@ -86,10 +87,11 @@
 }
 
 void ImeService::RunInMainSequence(ImeSequencedTask task, int task_id) {
-  // Always run tasks on current SequencedTaskRunner.
-  // It's necessary for making any call on a bound Mojo Remote.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(task, task_id));
+  // This helps ensure that tasks run in the **main** SequencedTaskRunner.
+  // E.g. the Mojo Remotes are bound on the main_task_runner_, so that any task
+  // invoked Mojo call from other threads (sequences) should be posted to
+  // main_task_runner_ by this function.
+  main_task_runner_->PostTask(FROM_HERE, base::BindOnce(task, task_id));
 }
 
 int ImeService::SimpleDownloadToFile(const char* url,
diff --git a/chromeos/services/ime/ime_service.h b/chromeos/services/ime/ime_service.h
index 3c05ff31..3879f638 100644
--- a/chromeos/services/ime/ime_service.h
+++ b/chromeos/services/ime/ime_service.h
@@ -60,6 +60,7 @@
                               const base::FilePath& file);
 
   mojo::Receiver<mojom::ImeService> receiver_;
+  scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
 
   // For the duration of this service lifetime, there should be only one
   // input engine instance.
diff --git a/components/arc/camera/arc_camera_bridge.cc b/components/arc/camera/arc_camera_bridge.cc
index e1c6e4c..7bf7f08 100644
--- a/components/arc/camera/arc_camera_bridge.cc
+++ b/components/arc/camera/arc_camera_bridge.cc
@@ -135,7 +135,7 @@
 }
 
 void ArcCameraBridge::RegisterCameraHalClient(
-    cros::mojom::CameraHalClientPtr client) {
+    mojo::PendingRemote<cros::mojom::CameraHalClient> client) {
   media::CameraHalDispatcherImpl::GetInstance()->RegisterClient(
       std::move(client));
 }
diff --git a/components/arc/camera/arc_camera_bridge.h b/components/arc/camera/arc_camera_bridge.h
index e327a71..d72430f 100644
--- a/components/arc/camera/arc_camera_bridge.h
+++ b/components/arc/camera/arc_camera_bridge.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "components/arc/mojom/camera.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace content {
 class BrowserContext;
@@ -34,7 +35,8 @@
 
   // mojom::CameraHost overrides:
   void StartCameraService(StartCameraServiceCallback callback) override;
-  void RegisterCameraHalClient(cros::mojom::CameraHalClientPtr client) override;
+  void RegisterCameraHalClient(
+      mojo::PendingRemote<cros::mojom::CameraHalClient> client) override;
 
  private:
   class PendingStartCameraServiceResult;
diff --git a/components/arc/mojom/camera.mojom b/components/arc/mojom/camera.mojom
index 04106e6..6437d71 100644
--- a/components/arc/mojom/camera.mojom
+++ b/components/arc/mojom/camera.mojom
@@ -99,7 +99,8 @@
   StartCameraService@0() => (CameraService service);
 
   // Registers the camera HAL client. Used by camera HAL v3.
-  [MinVersion=2] RegisterCameraHalClient@1(cros.mojom.CameraHalClient client);
+  [MinVersion=2] RegisterCameraHalClient@1(
+      pending_remote<cros.mojom.CameraHalClient> client);
 };
 
 // Next method ID: 1
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index 60b2413..e5095c1 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -307,6 +307,12 @@
     UserTriggeredGeneratePasswordCallback callback) {
   if (SetUpUserTriggeredGeneration()) {
     LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP);
+    // If the field is not |type=password|, the list of suggestions
+    // should not be populated with passwords to avoid filling them in a
+    // clear-text field.
+    // |IsPasswordFieldForAutofill()| is deliberately not used.
+    bool is_generation_element_password_type =
+        current_generation_item_->generation_element_.IsPasswordField();
     autofill::password_generation::PasswordGenerationUIData
         password_generation_ui_data(
             render_frame()->ElementBoundsInWindow(
@@ -316,6 +322,7 @@
                 .Utf16(),
             current_generation_item_->generation_element_
                 .UniqueRendererFormControlId(),
+            is_generation_element_password_type,
             GetTextDirectionForElement(
                 current_generation_item_->generation_element_),
             current_generation_item_->form_);
@@ -511,6 +518,12 @@
   DCHECK(current_generation_item_);
   DCHECK(!current_generation_item_->generation_element_.IsNull());
   LogMessage(Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE);
+  // If the field is not |type=password|, the list of suggestions
+  // should not be populated with passwordS to avoid filling them in a
+  // clear-text field.
+  // |IsPasswordFieldForAutofill()| is deliberately not used.
+  bool is_generation_element_password_type =
+      current_generation_item_->generation_element_.IsPasswordField();
   autofill::password_generation::PasswordGenerationUIData
       password_generation_ui_data(
           render_frame()->ElementBoundsInWindow(
@@ -520,6 +533,7 @@
               .Utf16(),
           current_generation_item_->generation_element_
               .UniqueRendererFormControlId(),
+          is_generation_element_password_type,
           GetTextDirectionForElement(
               current_generation_item_->generation_element_),
           current_generation_item_->form_);
diff --git a/components/autofill/core/common/mojom/autofill_types.mojom b/components/autofill/core/common/mojom/autofill_types.mojom
index 4ab37ae..08792489 100644
--- a/components/autofill/core/common/mojom/autofill_types.mojom
+++ b/components/autofill/core/common/mojom/autofill_types.mojom
@@ -214,6 +214,7 @@
   int32 max_length;
   mojo_base.mojom.String16 generation_element;
   uint32 generation_element_id;
+  bool is_generation_element_password_type;
   mojo_base.mojom.TextDirection text_direction;
   PasswordForm password_form;
 };
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
index 117da5b..3962a51 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
@@ -223,6 +223,8 @@
 
   out->max_length = data.max_length();
   out->generation_element_id = data.generation_element_id();
+  out->is_generation_element_password_type =
+      data.is_generation_element_password_type();
 
   if (!data.ReadGenerationElement(&out->generation_element) ||
       !data.ReadTextDirection(&out->text_direction) ||
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
index a4acca8..a26fbd2 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
@@ -408,6 +408,11 @@
     return r.generation_element_id;
   }
 
+  static bool is_generation_element_password_type(
+      const autofill::password_generation::PasswordGenerationUIData& r) {
+    return r.is_generation_element_password_type;
+  }
+
   static base::i18n::TextDirection text_direction(
       const autofill::password_generation::PasswordGenerationUIData& r) {
     return r.text_direction;
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index 4419cd3..1fd64be 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -117,6 +117,7 @@
   data->max_length = 20;
   data->generation_element = base::ASCIIToUTF16("generation_element");
   data->text_direction = base::i18n::RIGHT_TO_LEFT;
+  data->is_generation_element_password_type = false;
   CreateTestPasswordForm(&data->password_form);
 }
 
@@ -154,6 +155,8 @@
   EXPECT_EQ(expected.bounds, actual.bounds);
   EXPECT_EQ(expected.max_length, actual.max_length);
   EXPECT_EQ(expected.generation_element, actual.generation_element);
+  EXPECT_EQ(expected.is_generation_element_password_type,
+            actual.is_generation_element_password_type);
   EXPECT_EQ(expected.text_direction, actual.text_direction);
   EXPECT_EQ(expected.password_form, actual.password_form);
 }
diff --git a/components/autofill/core/common/password_generation_util.cc b/components/autofill/core/common/password_generation_util.cc
index 8bab183..6f89ed2 100644
--- a/components/autofill/core/common/password_generation_util.cc
+++ b/components/autofill/core/common/password_generation_util.cc
@@ -17,12 +17,14 @@
     int max_length,
     const base::string16& generation_element,
     uint32_t generation_element_id,
+    bool is_generation_element_password_type,
     base::i18n::TextDirection text_direction,
     const autofill::PasswordForm& password_form)
     : bounds(bounds),
       max_length(max_length),
       generation_element(generation_element),
       generation_element_id(generation_element_id),
+      is_generation_element_password_type(is_generation_element_password_type),
       text_direction(text_direction),
       password_form(password_form) {}
 
diff --git a/components/autofill/core/common/password_generation_util.h b/components/autofill/core/common/password_generation_util.h
index 47c9f2098..c2143d1 100644
--- a/components/autofill/core/common/password_generation_util.h
+++ b/components/autofill/core/common/password_generation_util.h
@@ -109,6 +109,7 @@
                            int max_length,
                            const base::string16& generation_element,
                            uint32_t generation_element_id,
+                           bool is_generation_element_password_type,
                            base::i18n::TextDirection text_direction,
                            const autofill::PasswordForm& password_form);
   PasswordGenerationUIData();
@@ -132,6 +133,9 @@
   // Renderer ID of the generation element.
   uint32_t generation_element_id;
 
+  // Is the generation element |type=password|.
+  bool is_generation_element_password_type;
+
   // Direction of the text for |generation_element|.
   base::i18n::TextDirection text_direction;
 
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index 15bfb05..7aff9b5 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -1050,11 +1050,14 @@
             SelectOption(selector, "two").proto_status());
 }
 
-IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOptionInIframe) {
-  Selector selector;
-  selector.selectors.emplace_back("#iframe");
-  selector.selectors.emplace_back("select[name=state]");
-  EXPECT_EQ(ACTION_APPLIED, SelectOption(selector, "NY").proto_status());
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOptionInIFrame) {
+  Selector select_selector;
+
+  // IFrame.
+  select_selector.selectors.clear();
+  select_selector.selectors.emplace_back("#iframe");
+  select_selector.selectors.emplace_back("select[name=state]");
+  EXPECT_EQ(ACTION_APPLIED, SelectOption(select_selector, "NY").proto_status());
 
   const std::string javascript = R"(
     let iframe = document.querySelector("iframe").contentDocument;
@@ -1062,16 +1065,46 @@
     select.options[select.selectedIndex].label;
   )";
   EXPECT_EQ("NY", content::EvalJs(shell(), javascript));
+
+  // OOPIF.
+  // Checking elements through EvalJs in OOPIF is blocked by cross-site.
+  select_selector.selectors.clear();
+  select_selector.selectors.emplace_back("#iframeExternal");
+  select_selector.selectors.emplace_back("select[name=pet]");
+  EXPECT_EQ(ACTION_APPLIED,
+            SelectOption(select_selector, "Cat").proto_status());
+
+  Selector result_selector;
+  result_selector.selectors.emplace_back("#iframeExternal");
+  result_selector.selectors.emplace_back("#myPet");
+  GetFieldsValue({result_selector}, {"Cat"});
 }
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetOuterHtml) {
-  Selector selector;
-  selector.selectors.emplace_back("#testOuterHtml");
   std::string html;
-  ASSERT_EQ(ACTION_APPLIED, GetOuterHtml(selector, &html).proto_status());
+
+  // Div.
+  Selector div_selector;
+  div_selector.selectors.emplace_back("#testOuterHtml");
+  ASSERT_EQ(ACTION_APPLIED, GetOuterHtml(div_selector, &html).proto_status());
   EXPECT_EQ(
       R"(<div id="testOuterHtml"><span>Span</span><p>Paragraph</p></div>)",
       html);
+
+  // IFrame.
+  Selector iframe_selector;
+  iframe_selector.selectors.emplace_back("#iframe");
+  iframe_selector.selectors.emplace_back("#input");
+  ASSERT_EQ(ACTION_APPLIED,
+            GetOuterHtml(iframe_selector, &html).proto_status());
+  EXPECT_EQ(R"(<input id="input" type="text">)", html);
+
+  // OOPIF.
+  Selector oopif_selector;
+  oopif_selector.selectors.emplace_back("#iframeExternal");
+  oopif_selector.selectors.emplace_back("#divToRemove");
+  ASSERT_EQ(ACTION_APPLIED, GetOuterHtml(oopif_selector, &html).proto_status());
+  EXPECT_EQ(R"(<div id="divToRemove">Text</div>)", html);
 }
 
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, GetAndSetFieldValue) {
diff --git a/components/content_settings/core/browser/website_settings_registry.cc b/components/content_settings/core/browser/website_settings_registry.cc
index e7627f3..8334e076 100644
--- a/components/content_settings/core/browser/website_settings_registry.cc
+++ b/components/content_settings/core/browser/website_settings_registry.cc
@@ -203,6 +203,11 @@
            WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
            WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
            DESKTOP, WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
+  Register(CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA,
+           "installed-web-app-metadata", nullptr,
+           WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::LOSSY,
+           WebsiteSettingsInfo::SINGLE_ORIGIN_ONLY_SCOPE, DESKTOP,
+           WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
 }
 
 }  // namespace content_settings
diff --git a/components/content_settings/core/common/content_settings.cc b/components/content_settings/core/common/content_settings.cc
index e1aca34..dc447411 100644
--- a/components/content_settings/core/common/content_settings.cc
+++ b/components/content_settings/core/common/content_settings.cc
@@ -79,6 +79,7 @@
     {CONTENT_SETTINGS_TYPE_WAKE_LOCK_SYSTEM, 55},
     {CONTENT_SETTINGS_TYPE_LEGACY_COOKIE_ACCESS, 56},
     {CONTENT_SETTINGS_TYPE_NATIVE_FILE_SYSTEM_WRITE_GUARD, 57},
+    {CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA, 58},
 };
 
 }  // namespace
diff --git a/components/content_settings/core/common/content_settings_types.h b/components/content_settings/core/common/content_settings_types.h
index 8bb5011..a082aaa0 100644
--- a/components/content_settings/core/common/content_settings_types.h
+++ b/components/content_settings/core/common/content_settings_types.h
@@ -172,6 +172,10 @@
   // File System API.
   CONTENT_SETTINGS_TYPE_NATIVE_FILE_SYSTEM_WRITE_GUARD,
 
+  // Content settings for installed web apps that browsing history may be
+  // inferred from e.g. last update check timestamp.
+  CONTENT_SETTINGS_TYPE_INSTALLED_WEB_APP_METADATA,
+
   CONTENT_SETTINGS_NUM_TYPES,
 };
 
diff --git a/components/content_settings/core/common/cookie_settings_base.cc b/components/content_settings/core/common/cookie_settings_base.cc
index ef3986a..eb4304f5 100644
--- a/components/content_settings/core/common/cookie_settings_base.cc
+++ b/components/content_settings/core/common/cookie_settings_base.cc
@@ -86,14 +86,6 @@
     const GURL& url,
     const GURL& site_for_cookies,
     const base::Optional<url::Origin>& top_frame_origin) const {
-  // TODO(crbug.com/988398): top_frame_origin is not yet always available.
-  // Ensure that the DCHECK always passes and remove the FeatureList check.
-  DCHECK((!base::FeatureList::IsEnabled(kImprovedCookieControls) &&
-          !base::FeatureList::IsEnabled(
-              kImprovedCookieControlsForThirdPartyCookieBlocking)) ||
-         top_frame_origin || site_for_cookies.is_empty())
-      << url << " " << site_for_cookies;
-
   ContentSetting setting;
   GetCookieSettingInternal(
       url, top_frame_origin ? top_frame_origin->GetURL() : site_for_cookies,
diff --git a/components/dbus/menu/menu_property_list.cc b/components/dbus/menu/menu_property_list.cc
index c08b367..acbc74fc 100644
--- a/components/dbus/menu/menu_property_list.cc
+++ b/components/dbus/menu/menu_property_list.cc
@@ -67,7 +67,6 @@
   switch (menu->GetTypeAt(i)) {
     case ui::MenuModel::TYPE_COMMAND:
     case ui::MenuModel::TYPE_HIGHLIGHTED:
-    case ui::MenuModel::TYPE_TITLE:
       // Nothing special to do.
       break;
     case ui::MenuModel::TYPE_CHECK:
diff --git a/components/dbus/menu/menu_property_list_unittest.cc b/components/dbus/menu/menu_property_list_unittest.cc
index a5e9854..b02783af 100644
--- a/components/dbus/menu/menu_property_list_unittest.cc
+++ b/components/dbus/menu/menu_property_list_unittest.cc
@@ -149,9 +149,6 @@
       case ui::MenuModel::TYPE_COMMAND:
         menu->AddItem(0, label_);
         break;
-      case ui::MenuModel::TYPE_TITLE:
-        menu->AddTitle(label_);
-        break;
       case ui::MenuModel::TYPE_CHECK:
         menu->AddCheckItem(0, label_);
         break;
diff --git a/components/gwp_asan/client/gwp_asan.cc b/components/gwp_asan/client/gwp_asan.cc
index c4d2d8db..7174c96 100644
--- a/components/gwp_asan/client/gwp_asan.cc
+++ b/components/gwp_asan/client/gwp_asan.cc
@@ -36,8 +36,8 @@
 namespace internal {
 namespace {
 
-constexpr int kDefaultMaxAllocations = 35;
-constexpr int kDefaultMaxMetadata = 150;
+constexpr int kDefaultMaxAllocations = 70;
+constexpr int kDefaultMaxMetadata = 255;
 
 #if defined(ARCH_CPU_64_BITS)
 constexpr int kDefaultTotalPages = 2048;
@@ -51,17 +51,23 @@
 // multiplier * range**rand
 // where rand is a random real number in the range [0,1).
 constexpr int kDefaultAllocationSamplingMultiplier = 1000;
-constexpr int kDefaultAllocationSamplingRange = 64;
+constexpr int kDefaultAllocationSamplingRange = 16;
 
-constexpr double kDefaultProcessSamplingProbability = 0.2;
+constexpr double kDefaultProcessSamplingProbability = 0.015;
 // The multiplier to increase the ProcessSamplingProbability in scenarios where
 // we want to perform additional testing (e.g., on canary/dev builds).
-constexpr int kDefaultProcessSamplingBoost2 = 5;
+constexpr int kDefaultProcessSamplingBoost2 = 10;
 
-const base::Feature kGwpAsanMalloc{"GwpAsanMalloc",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+#if defined(OS_WIN) || defined(OS_MACOSX)
+constexpr base::FeatureState kDefaultEnabled = base::FEATURE_ENABLED_BY_DEFAULT;
+#else
+constexpr base::FeatureState kDefaultEnabled =
+    base::FEATURE_DISABLED_BY_DEFAULT;
+#endif
+
+const base::Feature kGwpAsanMalloc{"GwpAsanMalloc", kDefaultEnabled};
 const base::Feature kGwpAsanPartitionAlloc{"GwpAsanPartitionAlloc",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
+                                           kDefaultEnabled};
 
 // Returns whether this process should be sampled to enable GWP-ASan.
 bool SampleProcess(const base::Feature& feature, bool boost_sampling) {
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 17058cc0..9b9fd4e 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -62,15 +62,17 @@
 using base::Time;
 
 namespace history {
+
 namespace {
 
-const base::Feature kHistoryServiceUsesTaskScheduler{
-    "HistoryServiceUsesTaskScheduler", base::FEATURE_DISABLED_BY_DEFAULT};
-
-static const char* kHistoryThreadName = "Chrome_HistoryThread";
+const char* kHistoryThreadName = "Chrome_HistoryThread";
 
 }  // namespace
 
+// static
+const base::Feature HistoryService::kHistoryServiceUsesTaskScheduler{
+    "HistoryServiceUsesTaskScheduler", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Sends messages from the backend to us on the main thread. This must be a
 // separate class from the history service so that it can hold a reference to
 // the history service (otherwise we would have to manually AddRef and
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h
index cc8ad6e..5600f42 100644
--- a/components/history/core/browser/history_service.h
+++ b/components/history/core/browser/history_service.h
@@ -18,6 +18,7 @@
 #include "base/callback.h"
 #include "base/callback_list.h"
 #include "base/containers/flat_set.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -87,6 +88,8 @@
 // as information about downloads.
 class HistoryService : public KeyedService {
  public:
+  static const base::Feature kHistoryServiceUsesTaskScheduler;
+
   // Must call Init after construction. The empty constructor provided only for
   // unit tests. When using the full constructor, |history_client| may only be
   // null during testing, while |visit_delegate| may be null if the embedder use
diff --git a/components/password_manager/core/browser/manage_passwords_referrer.h b/components/password_manager/core/browser/manage_passwords_referrer.h
index 2420893..d202ce9 100644
--- a/components/password_manager/core/browser/manage_passwords_referrer.h
+++ b/components/password_manager/core/browser/manage_passwords_referrer.h
@@ -9,6 +9,11 @@
 
 // Enumerates referrers that can trigger a navigation to the manage passwords
 // page.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. Needs to stay in sync with
+// ManagePasswordsReferrer in enums.xml.
+//
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.password_manager
 enum class ManagePasswordsReferrer {
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index ef5b4ac..46b7f3e7 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -338,14 +339,16 @@
 
 bool PasswordAutofillManager::MaybeShowPasswordSuggestionsWithGeneration(
     const gfx::RectF& bounds,
-    base::i18n::TextDirection text_direction) {
+    base::i18n::TextDirection text_direction,
+    bool show_password_suggestions) {
   if (!fill_data_)
     return false;
   std::vector<autofill::Suggestion> suggestions;
-  GetSuggestions(*fill_data_, base::string16(), page_favicon_,
-                 true /* show_all */, true /* is_password_field */,
-                 &suggestions);
-
+  if (show_password_suggestions) {
+    GetSuggestions(*fill_data_, base::string16(), page_favicon_,
+                   true /* show_all */, true /* is_password_field */,
+                   &suggestions);
+  }
   // Add 'Generation' option.
   // The UI code will pick up an icon from the resources based on the string.
   autofill::Suggestion suggestion(
diff --git a/components/password_manager/core/browser/password_autofill_manager.h b/components/password_manager/core/browser/password_autofill_manager.h
index 618b014..967615e 100644
--- a/components/password_manager/core/browser/password_autofill_manager.h
+++ b/components/password_manager/core/browser/password_autofill_manager.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_AUTOFILL_MANAGER_H_
 
 #include <map>
+#include <memory>
 
 #include "base/callback.h"
 #include "base/i18n/rtl.h"
@@ -85,7 +86,8 @@
   // and returns false.
   bool MaybeShowPasswordSuggestionsWithGeneration(
       const gfx::RectF& bounds,
-      base::i18n::TextDirection text_direction);
+      base::i18n::TextDirection text_direction,
+      bool show_password_suggestions);
 
   // Called when main frame navigates. Not called for in-page navigations.
   void DidNavigateMainFrame();
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 15f27f81..a74e2931 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
@@ -299,9 +301,10 @@
     EXPECT_CALL(*autofill_client, HideAutofillPopup());
     base::HistogramTester histograms;
     password_autofill_manager_->DidAcceptSuggestion(
-        test_username_, is_suggestion_on_password_field
-                            ? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
-                            : autofill::POPUP_ITEM_ID_USERNAME_ENTRY,
+        test_username_,
+        is_suggestion_on_password_field
+            ? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
+            : autofill::POPUP_ITEM_ID_USERNAME_ENTRY,
         1);
     histograms.ExpectUniqueSample(
         kDropdownSelectedHistogram,
@@ -735,7 +738,8 @@
   gfx::RectF element_bounds;
   EXPECT_FALSE(
       password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
-          element_bounds, base::i18n::RIGHT_TO_LEFT));
+          element_bounds, base::i18n::RIGHT_TO_LEFT,
+          /*show_password_suggestions=*/true));
 }
 
 TEST_F(PasswordAutofillManagerTest,
@@ -770,7 +774,8 @@
           false, PopupType::kPasswords, _));
   EXPECT_TRUE(
       password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
-          element_bounds, base::i18n::RIGHT_TO_LEFT));
+          element_bounds, base::i18n::RIGHT_TO_LEFT,
+          /*show_password_suggestions=*/true));
   histograms.ExpectUniqueSample(
       kDropdownShownHistogram,
       metrics_util::PasswordDropdownState::kStandardGenerate, 1);
@@ -785,4 +790,39 @@
       metrics_util::PasswordDropdownSelectedOption::kGenerate, 1);
 }
 
+TEST_F(PasswordAutofillManagerTest,
+       MaybeShowPasswordSuggestionsWithOmittedCredentials) {
+  auto client = std::make_unique<TestPasswordManagerClient>();
+  auto autofill_client = std::make_unique<MockAutofillClient>();
+  InitializePasswordAutofillManager(client.get(), autofill_client.get());
+
+  gfx::RectF element_bounds;
+  autofill::PasswordFormFillData data;
+  data.username_field.value = test_username_;
+  data.password_field.value = test_password_;
+  data.origin = GURL("https://foo.test");
+
+  favicon::MockFaviconService favicon_service;
+  EXPECT_CALL(*client, GetFaviconService()).WillOnce(Return(&favicon_service));
+  EXPECT_CALL(favicon_service, GetFaviconImageForPageURL(data.origin, _, _));
+  password_autofill_manager_->OnAddPasswordFillData(data);
+
+  base::string16 generation_string =
+      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD);
+
+  EXPECT_CALL(
+      *autofill_client,
+      ShowAutofillPopup(
+          element_bounds, base::i18n::RIGHT_TO_LEFT,
+          AllOf(SuggestionVectorValuesAre(
+                    ElementsAreArray(GetSuggestionList({generation_string}))),
+                SuggestionVectorIconsAre(
+                    ElementsAreArray(GetIconsList({"keyIcon"})))),
+          false, PopupType::kPasswords, _));
+
+  EXPECT_TRUE(
+      password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
+          element_bounds, base::i18n::RIGHT_TO_LEFT,
+          /*show_password_suggestions=*/false));
+}
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 0232462..c48ff9d 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -124,11 +124,13 @@
       init_status_(InitStatus::kUnknown) {}
 
 bool PasswordStore::Init(const syncer::SyncableService::StartSyncFlare& flare,
-                         PrefService* prefs) {
+                         PrefService* prefs,
+                         base::RepeatingClosure sync_enabled_or_disabled_cb) {
   main_task_runner_ = base::SequencedTaskRunnerHandle::Get();
   DCHECK(main_task_runner_);
   background_task_runner_ = CreateBackgroundTaskRunner();
   DCHECK(background_task_runner_);
+  sync_enabled_or_disabled_cb_ = std::move(sync_enabled_or_disabled_cb);
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   prefs_ = prefs;
   hash_password_manager_.set_prefs(prefs);
@@ -553,7 +555,7 @@
     sync_bridge_.reset(new PasswordSyncBridge(
         std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
             syncer::PASSWORDS, base::DoNothing()),
-        /*password_store_sync=*/this));
+        /*password_store_sync=*/this, sync_enabled_or_disabled_cb_));
   } else {
     DCHECK(!syncable_service_);
     syncable_service_.reset(new PasswordSyncableService(this));
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index f610fd7..3e1a077 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -112,10 +112,11 @@
 
   PasswordStore();
 
-  // Reimplement this to add custom initialization. Always call this too on the
-  // UI thread.
-  virtual bool Init(const syncer::SyncableService::StartSyncFlare& flare,
-                    PrefService* prefs);
+  // Always call this too on the UI thread.
+  bool Init(
+      const syncer::SyncableService::StartSyncFlare& flare,
+      PrefService* prefs,
+      base::RepeatingClosure sync_enabled_or_disabled_cb = base::DoNothing());
 
   // RefcountedKeyedService:
   void ShutdownOnUIThread() override;
@@ -716,6 +717,8 @@
   std::unique_ptr<PasswordSyncableService> syncable_service_;
   std::unique_ptr<PasswordSyncBridge> sync_bridge_;
 
+  base::RepeatingClosure sync_enabled_or_disabled_cb_;
+
   std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper_;
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc
index 4ec760f..26e1d17 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -7,6 +7,7 @@
 #include <unordered_set>
 
 #include "base/auto_reset.h"
+#include "base/callback.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
@@ -172,9 +173,7 @@
 }
 
 bool ShouldRecoverPasswordsDuringMerge() {
-  return base::FeatureList::IsEnabled(
-             features::kRecoverPasswordsForSyncUsers) &&
-         !base::FeatureList::IsEnabled(features::kDeleteCorruptedPasswords);
+  return !base::FeatureList::IsEnabled(features::kDeleteCorruptedPasswords);
 }
 
 // A simple class for scoping a password store sync transaction. If the
@@ -211,10 +210,13 @@
 
 PasswordSyncBridge::PasswordSyncBridge(
     std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
-    PasswordStoreSync* password_store_sync)
+    PasswordStoreSync* password_store_sync,
+    const base::RepeatingClosure& sync_enabled_or_disabled_cb)
     : ModelTypeSyncBridge(std::move(change_processor)),
-      password_store_sync_(password_store_sync) {
+      password_store_sync_(password_store_sync),
+      sync_enabled_or_disabled_cb_(sync_enabled_or_disabled_cb) {
   DCHECK(password_store_sync_);
+  DCHECK(sync_enabled_or_disabled_cb_);
   // The metadata store could be null if the login database initialization
   // fails.
   if (!password_store_sync_->GetMetadataStore()) {
@@ -288,6 +290,8 @@
     base::UmaHistogramCounts10000(
         "Sync.DownloadedPasswordsCountWhenInitialMergeFails",
         entity_data.size());
+  } else {
+    sync_enabled_or_disabled_cb_.Run();
   }
   return error;
 }
@@ -770,6 +774,8 @@
       }
       password_store_sync_->DeleteAndRecreateDatabaseFile();
       password_store_sync_->NotifyLoginsChanged(password_store_changes);
+
+      sync_enabled_or_disabled_cb_.Run();
     }
   }
 }
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.h b/components/password_manager/core/browser/sync/password_sync_bridge.h
index 5b4bdef8..e3c75ef 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.h
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SYNC_PASSWORD_SYNC_BRIDGE_H_
 #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SYNC_PASSWORD_SYNC_BRIDGE_H_
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "components/password_manager/core/browser/password_store_change.h"
@@ -33,7 +34,8 @@
   // |password_store_sync| must not be null and must outlive this object.
   PasswordSyncBridge(
       std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
-      PasswordStoreSync* password_store_sync);
+      PasswordStoreSync* password_store_sync,
+      const base::RepeatingClosure& sync_enabled_or_disabled_cb);
   ~PasswordSyncBridge() override;
 
   // Notifies the bridge of changes to the password database. Callers are
@@ -76,6 +78,8 @@
   // Password store responsible for persistence.
   PasswordStoreSync* const password_store_sync_;
 
+  base::RepeatingClosure sync_enabled_or_disabled_cb_;
+
   // True if processing remote sync changes is in progress. Used to ignore
   // password store changes notifications while processing remote sync changes.
   bool is_processing_remote_sync_changes_ = false;
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 2aedcda1..ff93af0 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/mock_callback.h"
 #include "components/password_manager/core/browser/password_store_sync.h"
 #include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/data_batch.h"
@@ -233,8 +234,8 @@
         .WillByDefault(Invoke(&fake_db_, &FakeDatabase::RemoveLogin));
 
     bridge_ = std::make_unique<PasswordSyncBridge>(
-        mock_processor_.CreateForwardingProcessor(),
-        &mock_password_store_sync_);
+        mock_processor_.CreateForwardingProcessor(), &mock_password_store_sync_,
+        sync_enabled_or_disabled_cb_.Get());
 
     // It's the responsibility of the PasswordStoreSync to inform the bridge
     // about changes in the password store. The bridge notifies the
@@ -303,11 +304,16 @@
     return &mock_password_store_sync_;
   }
 
+  base::MockRepeatingClosure* mock_sync_enabled_or_disabled_cb() {
+    return &sync_enabled_or_disabled_cb_;
+  }
+
  private:
   FakeDatabase fake_db_;
   testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
   testing::NiceMock<MockSyncMetadataStore> mock_sync_metadata_store_sync_;
   testing::NiceMock<MockPasswordStoreSync> mock_password_store_sync_;
+  testing::NiceMock<base::MockRepeatingClosure> sync_enabled_or_disabled_cb_;
   std::unique_ptr<PasswordSyncBridge> bridge_;
 };
 
@@ -780,7 +786,7 @@
                                     /*entities=*/testing::SizeIs(1))));
 
   PasswordSyncBridge bridge(mock_processor().CreateForwardingProcessor(),
-                            mock_password_store_sync());
+                            mock_password_store_sync(), base::DoNothing());
 }
 
 // Tests that in case ReadAllLogins() during initial merge returns encryption
@@ -807,4 +813,64 @@
   bridge()->ApplyStopSyncChanges(bridge()->CreateMetadataChangeList());
 }
 
+TEST_F(PasswordSyncBridgeTest, ShouldNotifyOnSyncEnable) {
+  ON_CALL(*mock_password_store_sync(), IsAccountStore())
+      .WillByDefault(Return(true));
+
+  // New password data becoming available because sync was newly enabled should
+  // trigger the callback.
+  EXPECT_CALL(*mock_sync_enabled_or_disabled_cb(), Run());
+
+  syncer::EntityChangeList initial_entity_data;
+  initial_entity_data.push_back(syncer::EntityChange::CreateAdd(
+      /*storage_key=*/"",
+      SpecificsToEntity(CreateSpecificsWithSignonRealm(kSignonRealm1))));
+
+  base::Optional<syncer::ModelError> error = bridge()->MergeSyncData(
+      bridge()->CreateMetadataChangeList(), std::move(initial_entity_data));
+  ASSERT_FALSE(error);
+}
+
+TEST_F(PasswordSyncBridgeTest, ShouldNotNotifyOnSyncChange) {
+  ON_CALL(*mock_password_store_sync(), IsAccountStore())
+      .WillByDefault(Return(true));
+
+  // New password data becoming available due to an incoming sync change should
+  // *not* trigger the callback. This is mainly for performance reasons: In
+  // practice, this callback will cause all PasswordFormManagers to re-query
+  // from the password store, which can be expensive.
+  EXPECT_CALL(*mock_sync_enabled_or_disabled_cb(), Run()).Times(0);
+
+  syncer::EntityChangeList entity_changes;
+  entity_changes.push_back(syncer::EntityChange::CreateAdd(
+      /*storage_key=*/"",
+      SpecificsToEntity(CreateSpecificsWithSignonRealm(kSignonRealm1))));
+
+  base::Optional<syncer::ModelError> error = bridge()->ApplySyncChanges(
+      bridge()->CreateMetadataChangeList(), std::move(entity_changes));
+  ASSERT_FALSE(error);
+}
+
+TEST_F(PasswordSyncBridgeTest, ShouldNotifyOnSyncDisableIfAccountStore) {
+  ON_CALL(*mock_password_store_sync(), IsAccountStore())
+      .WillByDefault(Return(true));
+
+  // The account password store gets cleared when sync is disabled, so this
+  // should trigger the callback.
+  EXPECT_CALL(*mock_sync_enabled_or_disabled_cb(), Run());
+
+  bridge()->ApplyStopSyncChanges(bridge()->CreateMetadataChangeList());
+}
+
+TEST_F(PasswordSyncBridgeTest, ShouldNotNotifyOnSyncDisableIfProfileStore) {
+  ON_CALL(*mock_password_store_sync(), IsAccountStore())
+      .WillByDefault(Return(false));
+
+  // The profile password store does *not* get cleared when sync is disabled, so
+  // this should *not* trigger the callback.
+  EXPECT_CALL(*mock_sync_enabled_or_disabled_cb(), Run()).Times(0);
+
+  bridge()->ApplyStopSyncChanges(bridge()->CreateMetadataChangeList());
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/sync/password_syncable_service.cc b/components/password_manager/core/browser/sync/password_syncable_service.cc
index 331b0038..ae61ed5 100644
--- a/components/password_manager/core/browser/sync/password_syncable_service.cc
+++ b/components/password_manager/core/browser/sync/password_syncable_service.cc
@@ -452,9 +452,7 @@
 }
 
 bool PasswordSyncableService::ShouldRecoverPasswordsDuringMerge() const {
-  return base::FeatureList::IsEnabled(
-             features::kRecoverPasswordsForSyncUsers) &&
-         !base::FeatureList::IsEnabled(features::kDeleteCorruptedPasswords);
+  return !base::FeatureList::IsEnabled(features::kDeleteCorruptedPasswords);
 }
 
 syncer::SyncData SyncDataFromPassword(
diff --git a/components/password_manager/core/browser/sync/password_syncable_service.h b/components/password_manager/core/browser/sync/password_syncable_service.h
index 1173a3c..409838d 100644
--- a/components/password_manager/core/browser/sync/password_syncable_service.h
+++ b/components/password_manager/core/browser/sync/password_syncable_service.h
@@ -88,14 +88,8 @@
 
   // Returns true if corrupted passwords should be deleted from the local
   // database when merging data.
-  // There are two features that handle recovering lost passwords.
-  // RecoverPasswordsForSyncUsers recovers passwords for sync users when merging
-  // data with Sync. If that feature is disabled, this method returns false.
-  // Other feature, DeleteCorruptedPasswords, is introduced after the first
-  // feature and recovers both Sync and non-Sync users internally in
-  // LoginDatabase. When that feature is enabled, this method returns false.
-  // After launching DeleteCorruptedPasswords, RecoverPasswordsForSyncUsers
-  // and related code is to be cleaned up.
+  // This is true if the feature DeleteCorruptedPasswords is disabled, as it
+  // recovers both Sync and non-Sync users internally in LoginDatabase.
   bool ShouldRecoverPasswordsDuringMerge() const;
 
   // The factory that creates sync errors. |SyncError| has rich data
diff --git a/components/password_manager/core/browser/sync/password_syncable_service_unittest.cc b/components/password_manager/core/browser/sync/password_syncable_service_unittest.cc
index 78d997b..f7fe2d2 100644
--- a/components/password_manager/core/browser/sync/password_syncable_service_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_syncable_service_unittest.cc
@@ -518,10 +518,8 @@
                           syncer::PASSWORDS);
   EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
       .WillOnce(Return(false));
-  if (base::FeatureList::IsEnabled(features::kRecoverPasswordsForSyncUsers)) {
-    EXPECT_CALL(*password_store(), DeleteUndecryptableLogins())
-        .WillOnce(Return(DatabaseCleanupResult::kDatabaseUnavailable));
-  }
+  EXPECT_CALL(*password_store(), DeleteUndecryptableLogins())
+      .WillOnce(Return(DatabaseCleanupResult::kDatabaseUnavailable));
   EXPECT_CALL(*error_factory, CreateAndUploadError(_, _))
       .WillOnce(Return(error));
   // ActOnPasswordStoreChanges() below shouldn't generate any changes for Sync.
@@ -538,39 +536,23 @@
   list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
   service()->ActOnPasswordStoreChanges(list);
 }
+class PasswordSyncableServiceTestWithoutDeleteCorruptedPasswords
+    : public PasswordSyncableServiceTest {
+ public:
+  PasswordSyncableServiceTestWithoutDeleteCorruptedPasswords() {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kDeleteCorruptedPasswords);
+  }
 
-// Disable feature for deleting undecryptable logins.
-TEST_F(PasswordSyncableServiceTest, RecoverPasswordsForSyncUsersDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kRecoverPasswordsForSyncUsers);
-  auto error_factory = std::make_unique<syncer::SyncErrorFactoryMock>();
-  syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
-                          "Failed to get passwords from store.",
-                          syncer::PASSWORDS);
-  EXPECT_CALL(*error_factory, CreateAndUploadError(_, _))
-      .WillOnce(Return(error));
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
 
-  EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
-      .WillOnce(Return(false));
-  EXPECT_CALL(*password_store(), DeleteUndecryptableLogins()).Times(0);
-
-  syncer::SyncMergeResult result = service()->MergeDataAndStartSyncing(
-      syncer::PASSWORDS, SyncDataList(), std::move(processor_),
-      std::move(error_factory));
-  EXPECT_TRUE(result.error().IsSet());
-}
-
-// Test that passwords are recovered for Sync users using the older feature
-// (kRecoverPasswordsForSyncUsers) when feature for recovering passwords for
-// Sync users is enabled, while feature for deleting corrupted passwords for
-// all users is disabled.
-TEST_F(PasswordSyncableServiceTest, RecoverPasswordsForSyncUsersEnabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {features::kRecoverPasswordsForSyncUsers},
-      {features::kDeleteCorruptedPasswords});
-
+// Test that passwords are recovered for Sync users using the older logic (i.e.
+// recover passwords only for Sync users) when the feature for deleting
+// corrupted passwords for all users is disabled.
+TEST_F(PasswordSyncableServiceTestWithoutDeleteCorruptedPasswords,
+       RecoverPasswordsOnlyForSyncUsers) {
   EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
   EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
       .Times(2)
@@ -585,16 +567,22 @@
   EXPECT_FALSE(result.error().IsSet());
 }
 
-// Test that passwords are not recovered using the older feature
-// (kRecoverPasswordForSyncUsers) when merging data if both features for
-// recovering passwords for Sync users and deleting passwords for all users
-// are enabled.
-TEST_F(PasswordSyncableServiceTest, PasswordRecoveryForAllUsersEnabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures({features::kRecoverPasswordsForSyncUsers,
-                                        features::kDeleteCorruptedPasswords},
-                                       {});
+class PasswordSyncableServiceTestWithDeleteCorruptedPasswords
+    : public PasswordSyncableServiceTest {
+ public:
+  PasswordSyncableServiceTestWithDeleteCorruptedPasswords() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kDeleteCorruptedPasswords);
+  }
 
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Test that passwords are not recovered when merging data if the feature for
+// deleting passwords for all users is enabled.
+TEST_F(PasswordSyncableServiceTestWithDeleteCorruptedPasswords,
+       PasswordRecoveryForAllUsersEnabled) {
   auto error_factory = std::make_unique<syncer::SyncErrorFactoryMock>();
   syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
                           "Failed to get passwords from store.",
@@ -613,18 +601,8 @@
 }
 
 // Database cleanup fails because encryption is unavailable.
-// Flaky: crbug.com/1011193.
-#if defined(OS_WIN)
-#define MAYBE_FailedDeleteUndecryptableLogins \
-  DISABLED_FailedDeleteUndecryptableLogins
-#else
-#define MAYBE_FailedDeleteUndecryptableLogins FailedDeleteUndecryptableLogins
-#endif
-TEST_F(PasswordSyncableServiceTest, MAYBE_FailedDeleteUndecryptableLogins) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {features::kRecoverPasswordsForSyncUsers},
-      {features::kDeleteCorruptedPasswords});
+TEST_F(PasswordSyncableServiceTestWithoutDeleteCorruptedPasswords,
+       FailedDeleteUndecryptableLogins) {
   auto error_factory = std::make_unique<syncer::SyncErrorFactoryMock>();
   syncer::SyncError error(
       FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 242a0db..2230d73 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -75,11 +75,6 @@
 const base::Feature kPasswordSaveIllustration = {
     "SavePasswordIllustration", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Deletes entries from local database on Mac which cannot be decrypted when
-// merging data with Sync.
-const base::Feature kRecoverPasswordsForSyncUsers = {
-    "RecoverPasswordsForSyncUsers", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables support of filling and saving on username first flow.
 const base::Feature kUsernameFirstFlow = {"UsernameFirstFlow",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index fb76fce..41ce08a 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -31,7 +31,6 @@
 extern const base::Feature kPasswordImport;
 extern const base::Feature kPasswordManagerOnboardingAndroid;
 extern const base::Feature kPasswordSaveIllustration;
-extern const base::Feature kRecoverPasswordsForSyncUsers;
 extern const base::Feature kUsernameFirstFlow;
 extern const base::Feature kStickyBubble;
 
diff --git a/components/sync/OWNERS b/components/sync/OWNERS
index e90fa107..2381d48 100644
--- a/components/sync/OWNERS
+++ b/components/sync/OWNERS
@@ -1,5 +1,4 @@
 jkrcal@chromium.org
-mamir@chromium.org
 mastiz@chromium.org
 melandory@chromium.org
 treib@chromium.org
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.cc b/components/sync/engine_impl/loopback_server/loopback_server.cc
index 8c15595..a2135dd 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.cc
+++ b/components/sync/engine_impl/loopback_server/loopback_server.cc
@@ -15,6 +15,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/rand_util.h"
+#include "base/sequence_checker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -255,7 +256,7 @@
 bool LoopbackServer::CreatePermanentBookmarkFolder(
     const std::string& server_tag,
     const std::string& name) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<LoopbackServerEntity> entity =
       PersistentPermanentEntity::CreateNew(
           syncer::BOOKMARKS, server_tag, name,
@@ -306,7 +307,7 @@
 
 net::HttpStatusCode LoopbackServer::HandleCommand(const string& request,
                                                   std::string* response) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   response->clear();
 
   sync_pb::ClientToServerMessage message;
@@ -663,7 +664,7 @@
 }
 
 void LoopbackServer::ClearServerData() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   entities_.clear();
   keystore_keys_.clear();
   ++store_birthday_;
@@ -672,13 +673,13 @@
 }
 
 std::string LoopbackServer::GetStoreBirthday() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return base::NumberToString(store_birthday_);
 }
 
 std::vector<sync_pb::SyncEntity> LoopbackServer::GetSyncEntitiesByModelType(
     ModelType model_type) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::vector<sync_pb::SyncEntity> sync_entities;
   for (const auto& kv : entities_) {
     const LoopbackServerEntity& entity = *kv.second;
@@ -694,7 +695,7 @@
 
 std::vector<sync_pb::SyncEntity>
 LoopbackServer::GetPermanentSyncEntitiesByModelType(ModelType model_type) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::vector<sync_pb::SyncEntity> sync_entities;
   for (const auto& kv : entities_) {
     const LoopbackServerEntity& entity = *kv.second;
@@ -710,7 +711,7 @@
 
 std::unique_ptr<base::DictionaryValue>
 LoopbackServer::GetEntitiesAsDictionaryValue() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::unique_ptr<base::DictionaryValue> dictionary(
       new base::DictionaryValue());
 
@@ -783,7 +784,7 @@
 }
 
 void LoopbackServer::SerializeState(sync_pb::LoopbackServerProto* proto) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   proto->set_version(kCurrentLoopbackServerProtoVersion);
   proto->set_store_birthday(store_birthday_);
@@ -798,7 +799,7 @@
 
 bool LoopbackServer::DeSerializeState(
     const sync_pb::LoopbackServerProto& proto) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(proto.version(), kCurrentLoopbackServerProtoVersion);
 
   store_birthday_ = proto.store_birthday();
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.h b/components/sync/engine_impl/loopback_server/loopback_server.h
index 85f98e1..a0807c6 100644
--- a/components/sync/engine_impl/loopback_server/loopback_server.h
+++ b/components/sync/engine_impl/loopback_server/loopback_server.h
@@ -15,7 +15,7 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/optional.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
 #include "base/values.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
@@ -235,8 +235,8 @@
   // The file used to store the local sync data.
   base::FilePath persistent_file_;
 
-  // Used to verify that LoopbackServer is only used from one thread.
-  base::ThreadChecker thread_checker_;
+  // Used to verify that LoopbackServer is only used from one sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
 
   // Used to observe the completion of commit messages for the sake of testing.
   ObserverForTests* observer_for_tests_;
diff --git a/components/sync/nigori/cryptographer_impl.cc b/components/sync/nigori/cryptographer_impl.cc
index ac38cd4..0894b38 100644
--- a/components/sync/nigori/cryptographer_impl.cc
+++ b/components/sync/nigori/cryptographer_impl.cc
@@ -97,6 +97,10 @@
       new CryptographerImpl(key_bag_.Clone(), default_encryption_key_name_));
 }
 
+size_t CryptographerImpl::KeyBagSizeForTesting() const {
+  return key_bag_.size();
+}
+
 std::unique_ptr<Cryptographer> CryptographerImpl::Clone() const {
   return CloneImpl();
 }
diff --git a/components/sync/nigori/cryptographer_impl.h b/components/sync/nigori/cryptographer_impl.h
index 2afc6e0..3fb58d7 100644
--- a/components/sync/nigori/cryptographer_impl.h
+++ b/components/sync/nigori/cryptographer_impl.h
@@ -75,6 +75,8 @@
   // Similar to Clone() but returns CryptographerImpl.
   std::unique_ptr<CryptographerImpl> CloneImpl() const;
 
+  size_t KeyBagSizeForTesting() const;
+
   // Cryptographer overrides.
   std::unique_ptr<Cryptographer> Clone() const override;
   bool CanEncrypt() const override;
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index f04a3ce..8722caf 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -494,15 +494,18 @@
     return;
   }
 
-  const std::string new_key_name = state_.cryptographer->EmplaceKey(
-      passphrase, GetKeyDerivationParamsForPendingKeys());
-  if (new_key_name.empty()) {
-    processor_->ReportError(ModelError(
-        FROM_HERE, "Failed to add decryption passphrase to cryptographer."));
+  NigoriKeyBag tmp_key_bag = NigoriKeyBag::CreateEmpty();
+  const std::string new_key_name =
+      tmp_key_bag.AddKey(Nigori::CreateByDerivation(
+          GetKeyDerivationParamsForPendingKeys(), passphrase));
+
+  base::Optional<ModelError> error = TryDecryptPendingKeysWith(tmp_key_bag);
+  if (error.has_value()) {
+    processor_->ReportError(*error);
     return;
   }
 
-  if (!TryDecryptPendingKeys()) {
+  if (state_.pending_keys.has_value()) {
     // |pending_keys| could be changed in between of OnPassphraseRequired()
     // and SetDecryptionPassphrase() calls (remote update with different
     // keystore Nigori or with transition from keystore to custom passphrase
@@ -511,17 +514,11 @@
     return;
   }
 
-  state_.cryptographer->SelectDefaultEncryptionKey(new_key_name);
-
+  DCHECK_EQ(state_.cryptographer->GetDefaultEncryptionKeyName(), new_key_name);
   storage_->StoreData(SerializeAsNigoriLocalData());
   broadcasting_observer_->OnCryptographerStateChanged(
       state_.cryptographer.get(), state_.pending_keys.has_value());
   broadcasting_observer_->OnPassphraseAccepted();
-
-  // TODO(crbug.com/922900): we may need to rewrite encryption_keybag in Nigori
-  // node in case we have some keys in |cryptographer_| which is not stored in
-  // encryption_keybag yet.
-  NOTIMPLEMENTED();
 }
 
 void NigoriSyncBridgeImpl::AddTrustedVaultDecryptionKeys(
@@ -534,28 +531,28 @@
     return;
   }
 
+  NigoriKeyBag tmp_key_bag = NigoriKeyBag::CreateEmpty();
   for (const std::string& key : keys) {
     if (!key.empty()) {
-      state_.cryptographer->EmplaceKey(key,
-                                       GetKeyDerivationParamsForPendingKeys());
+      tmp_key_bag.AddKey(Nigori::CreateByDerivation(
+          GetKeyDerivationParamsForPendingKeys(), key));
     }
   }
 
-  const std::string pending_key_name = state_.pending_keys->key_name();
+  base::Optional<ModelError> error = TryDecryptPendingKeysWith(tmp_key_bag);
+  if (error.has_value()) {
+    processor_->ReportError(*error);
+    return;
+  }
 
-  if (TryDecryptPendingKeys()) {
-    state_.cryptographer->SelectDefaultEncryptionKey(pending_key_name);
+  if (state_.pending_keys.has_value()) {
+    return;
   }
 
   storage_->StoreData(SerializeAsNigoriLocalData());
-
   broadcasting_observer_->OnCryptographerStateChanged(
       state_.cryptographer.get(), state_.pending_keys.has_value());
-
-  if (!state_.pending_keys) {
-    broadcasting_observer_->OnTrustedVaultKeyAccepted();
-  }
-
+  broadcasting_observer_->OnTrustedVaultKeyAccepted();
   MaybeNotifyBootstrapTokenUpdated();
 }
 
@@ -813,6 +810,8 @@
   DCHECK(!encryption_keybag.blob().empty());
   DCHECK(!keystore_decryptor_token.blob().empty());
 
+  const bool had_pending_keys_before_update = state_.pending_keys.has_value();
+
   // Decryption of |keystore_decryptor_token|.
   NigoriKeyBag keystore_decryptor_key_bag = NigoriKeyBag::CreateEmpty();
   sync_pb::NigoriKey keystore_decryptor_key;
@@ -824,29 +823,27 @@
     state_.pending_keystore_decryptor_token = keystore_decryptor_token;
   }
 
-  // TODO(crbug.com/922900): issue ModelError if |keystore_decryptor_keybag| is
-  // not empty and can't decrypt the |encryption_keybag|?
-  if (keystore_decryptor_key_bag.CanDecrypt(encryption_keybag)) {
-    // |encryption_keybag| must contain the key it was encrypted with, so it's
-    // okay to add it earlier.
-    state_.cryptographer->EmplaceKeysFrom(keystore_decryptor_key_bag);
+  // In order to decrypt |encryption_keybag|, temporarily set pending keys
+  // before calling TryDecryptPendingKeysWith().
+  state_.pending_keys = encryption_keybag;
+  state_.cryptographer->ClearDefaultEncryptionKey();
+
+  base::Optional<ModelError> error =
+      TryDecryptPendingKeysWith(keystore_decryptor_key_bag);
+  if (error.has_value()) {
+    return error;
   }
 
-  // Decryption of |encryption_keybag|.
-  sync_pb::NigoriKeyBag key_bag;
-  if (!state_.cryptographer->Decrypt(encryption_keybag, &key_bag)) {
-    state_.cryptographer->ClearDefaultEncryptionKey();
-    state_.pending_keys = encryption_keybag;
+  if (state_.pending_keys.has_value()) {
+    // TODO(crbug.com/922900): issue ModelError if
+    // |keystore_decryptor_keybag| is not empty?
     return base::nullopt;
   }
 
-  state_.cryptographer->EmplaceKeysFrom(NigoriKeyBag::CreateFromProto(key_bag));
-  state_.cryptographer->SelectDefaultEncryptionKey(
-      encryption_keybag.key_name());
-  if (state_.pending_keys) {
-    state_.pending_keys.reset();
+  if (had_pending_keys_before_update) {
     broadcasting_observer_->OnPassphraseAccepted();
   }
+
   return base::nullopt;
 }
 
@@ -892,16 +889,38 @@
   state_.cryptographer->EmplaceKeysFrom(NigoriKeyBag::CreateFromProto(key_bag));
 }
 
-bool NigoriSyncBridgeImpl::TryDecryptPendingKeys() {
-  sync_pb::NigoriKeyBag decrypted_pending_keys;
-  if (!state_.cryptographer->Decrypt(*state_.pending_keys,
-                                     &decrypted_pending_keys)) {
-    return false;
+base::Optional<ModelError> NigoriSyncBridgeImpl::TryDecryptPendingKeysWith(
+    const NigoriKeyBag& key_bag) {
+  DCHECK(state_.pending_keys.has_value());
+  DCHECK(state_.cryptographer->GetDefaultEncryptionKeyName().empty());
+
+  std::string decrypted_pending_keys_str;
+  if (!key_bag.Decrypt(*state_.pending_keys, &decrypted_pending_keys_str)) {
+    return base::nullopt;
   }
-  state_.cryptographer->EmplaceKeysFrom(
-      NigoriKeyBag::CreateFromProto(decrypted_pending_keys));
+
+  sync_pb::NigoriKeyBag decrypted_pending_keys;
+  if (!decrypted_pending_keys.ParseFromString(decrypted_pending_keys_str)) {
+    return base::nullopt;
+  }
+
+  const std::string new_default_key_name = state_.pending_keys->key_name();
+  DCHECK(key_bag.HasKey(new_default_key_name));
+
+  NigoriKeyBag new_key_bag =
+      NigoriKeyBag::CreateFromProto(decrypted_pending_keys);
+
+  if (!new_key_bag.HasKey(new_default_key_name)) {
+    // Protocol violation.
+    return ModelError(FROM_HERE,
+                      "Received keybag is missing the new default key.");
+  }
+
+  state_.cryptographer->EmplaceKeysFrom(new_key_bag);
+  state_.cryptographer->SelectDefaultEncryptionKey(new_default_key_name);
   state_.pending_keys.reset();
-  return true;
+
+  return base::nullopt;
 }
 
 std::unique_ptr<EntityData> NigoriSyncBridgeImpl::GetData() {
@@ -962,7 +981,8 @@
   broadcasting_observer_->OnEncryptedTypesChanged(SensitiveTypes(), false);
 }
 
-const Cryptographer& NigoriSyncBridgeImpl::GetCryptographerForTesting() const {
+const CryptographerImpl& NigoriSyncBridgeImpl::GetCryptographerForTesting()
+    const {
   return *state_.cryptographer;
 }
 
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.h b/components/sync/nigori/nigori_sync_bridge_impl.h
index fe9dd43..97c583ac 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.h
+++ b/components/sync/nigori/nigori_sync_bridge_impl.h
@@ -85,7 +85,7 @@
   // TODO(crbug.com/922900): investigate whether we need this getter outside of
   // tests and decide whether this method should be a part of
   // SyncEncryptionHandler interface.
-  const Cryptographer& GetCryptographerForTesting() const;
+  const CryptographerImpl& GetCryptographerForTesting() const;
   sync_pb::NigoriSpecifics::PassphraseType GetPassphraseTypeForTesting() const;
   ModelTypeSet GetEncryptedTypesForTesting() const;
   bool HasPendingKeysForTesting() const;
@@ -105,11 +105,20 @@
   void UpdateCryptographerFromNonKeystoreNigori(
       const sync_pb::EncryptedData& keybag);
 
-  // Uses the cryptographer to try to decrypt pending keys. If success, the
-  // newly decrypted keys are put in the cryptographer's keybag, pending keys
-  // are cleared and the function returns true. Otherwise, it returns false and
-  // the state remains unchanged. It does not change the default key.
-  bool TryDecryptPendingKeys();
+  // Uses |key_bag| to try to decrypt pending keys as represented in
+  // |state_.pending_keys| (which must be set).
+  //
+  // If decryption is possible, the newly decrypted keys are put in the
+  // |state_.cryptographer|'s keybag and the default key is updated. In that
+  // case pending keys are cleared.
+  //
+  // If |key_bag| is not capable of decrypting pending keys,
+  // |state_.pending_keys| stays set. Such outcome is not itself considered
+  // and error and returns base::nullopt.
+  //
+  // Errors may be returned, in rare cases, for fatal protocol violations.
+  base::Optional<ModelError> TryDecryptPendingKeysWith(
+      const NigoriKeyBag& key_bag);
 
   base::Time GetExplicitPassphraseTime() const;
 
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index 06b0cb2..17de4ad 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -730,6 +730,7 @@
 TEST_F(NigoriSyncBridgeImplTest,
        ShouldNotifyWhenDecryptionWithPassphraseFailed) {
   const KeyParams kKeystoreKeyParams = KeystoreKeyParams("keystore_key");
+
   EntityData entity_data;
   *entity_data.specifics.mutable_nigori() = BuildKeystoreNigoriSpecifics(
       /*keybag_keys_params=*/{kKeystoreKeyParams},
@@ -749,6 +750,10 @@
           /*pending_keys=*/
           EncryptedDataEq(expected_pending_keys)));
   bridge()->SetDecryptionPassphrase("wrong_passphrase");
+
+  const CryptographerImpl& cryptographer =
+      bridge()->GetCryptographerForTesting();
+  EXPECT_THAT(cryptographer.KeyBagSizeForTesting(), Eq(size_t(0)));
 }
 
 // Tests that attempt to SetEncryptionPassphrase() has no effect (at least
@@ -1324,6 +1329,36 @@
   EXPECT_THAT(bridge()->GetData(), HasCustomPassphraseNigori());
 }
 
+TEST_F(NigoriSyncBridgeImplTest,
+       ShouldNotAddDecryptionKeysToTrustedVaultCryptographer) {
+  const std::string kTrustedVaultKey1 = "trusted_vault_key_1";
+  const std::string kTrustedVaultKey2 = "trusted_vault_key_2";
+  EntityData entity_data;
+  *entity_data.specifics.mutable_nigori() =
+      BuildTrustedVaultNigoriSpecifics({Pbkdf2KeyParams(kTrustedVaultKey1)});
+
+  ASSERT_TRUE(bridge()->SetKeystoreKeys({"keystore_key"}));
+  EXPECT_THAT(bridge()->MergeSyncData(std::move(entity_data)),
+              Eq(base::nullopt));
+  EXPECT_TRUE(bridge()->Init());
+  ASSERT_THAT(bridge()->GetPassphraseTypeForTesting(),
+              Eq(sync_pb::NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE));
+  ASSERT_TRUE(bridge()->HasPendingKeysForTesting());
+
+  // Note that |kTrustedVaultKey2| was not part of Nigori specifics.
+  bridge()->AddTrustedVaultDecryptionKeys(
+      {kTrustedVaultKey1, kTrustedVaultKey2});
+  ASSERT_FALSE(bridge()->HasPendingKeysForTesting());
+
+  const CryptographerImpl& cryptographer =
+      bridge()->GetCryptographerForTesting();
+  ASSERT_THAT(cryptographer,
+              CanDecryptWith(Pbkdf2KeyParams(kTrustedVaultKey1)));
+  EXPECT_THAT(cryptographer,
+              Not(CanDecryptWith(Pbkdf2KeyParams(kTrustedVaultKey2))));
+  EXPECT_THAT(cryptographer.KeyBagSizeForTesting(), Eq(size_t(1)));
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc
index f1675576..5acee27 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -209,6 +209,12 @@
   return it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr;
 }
 
+SyncedBookmarkTracker::Entity* SyncedBookmarkTracker::GetMutableEntityForSyncId(
+    const std::string& sync_id) {
+  auto it = sync_id_to_entities_map_.find(sync_id);
+  return it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr;
+}
+
 const SyncedBookmarkTracker::Entity*
 SyncedBookmarkTracker::GetEntityForBookmarkNode(
     const bookmarks::BookmarkNode* node) const {
@@ -248,9 +254,7 @@
     const sync_pb::UniquePosition& unique_position,
     const sync_pb::EntitySpecifics& specifics) {
   DCHECK_GT(specifics.ByteSize(), 0);
-  auto it = sync_id_to_entities_map_.find(sync_id);
-  DCHECK(it != sync_id_to_entities_map_.end());
-  Entity* entity = it->second.get();
+  Entity* entity = GetMutableEntityForSyncId(sync_id);
   DCHECK(entity);
   DCHECK_EQ(entity->metadata()->server_id(), sync_id);
   entity->metadata()->set_server_version(server_version);
@@ -264,25 +268,20 @@
 
 void SyncedBookmarkTracker::UpdateServerVersion(const std::string& sync_id,
                                                 int64_t server_version) {
-  auto it = sync_id_to_entities_map_.find(sync_id);
-  DCHECK(it != sync_id_to_entities_map_.end());
-  Entity* entity = it->second.get();
+  Entity* entity = GetMutableEntityForSyncId(sync_id);
   DCHECK(entity);
   entity->metadata()->set_server_version(server_version);
 }
 
 void SyncedBookmarkTracker::MarkCommitMayHaveStarted(
     const std::string& sync_id) {
-  auto it = sync_id_to_entities_map_.find(sync_id);
-  DCHECK(it != sync_id_to_entities_map_.end());
-  Entity* entity = it->second.get();
+  Entity* entity = GetMutableEntityForSyncId(sync_id);
   DCHECK(entity);
   entity->set_commit_may_have_started(true);
 }
 
 void SyncedBookmarkTracker::MarkDeleted(const std::string& sync_id) {
-  auto it = sync_id_to_entities_map_.find(sync_id);
-  Entity* entity = it->second.get();
+  Entity* entity = GetMutableEntityForSyncId(sync_id);
   DCHECK(entity);
   entity->metadata()->set_is_deleted(true);
   // Clear all references to the deleted bookmark node.
@@ -308,7 +307,7 @@
   // TODO(crbug.com/516866): The below CHECK is added to debug some crashes.
   // Should be switched to a DCHECK after figuring out the reason for the crash.
   CHECK_NE(0U, sync_id_to_entities_map_.count(sync_id));
-  Entity* entity = sync_id_to_entities_map_.find(sync_id)->second.get();
+  Entity* entity = GetMutableEntityForSyncId(sync_id);
   DCHECK(entity);
   // TODO(crbug.com/516866): Update base hash specifics here if the entity is
   // not already out of sync.
@@ -472,9 +471,7 @@
     int64_t acked_sequence_number,
     int64_t server_version) {
   // TODO(crbug.com/516866): Update specifics if we decide to keep it.
-  auto it = sync_id_to_entities_map_.find(old_id);
-  Entity* entity =
-      it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr;
+  Entity* entity = GetMutableEntityForSyncId(old_id);
   if (!entity) {
     DLOG(WARNING) << "Trying to update a non existing entity.";
     return;
@@ -516,15 +513,13 @@
     return;
   }
   bookmark_node_to_entities_map_[new_node] =
-      std::move(bookmark_node_to_entities_map_[old_node]);
+      bookmark_node_to_entities_map_[old_node];
   bookmark_node_to_entities_map_[new_node]->set_bookmark_node(new_node);
   bookmark_node_to_entities_map_.erase(old_node);
 }
 
 void SyncedBookmarkTracker::AckSequenceNumber(const std::string& sync_id) {
-  auto it = sync_id_to_entities_map_.find(sync_id);
-  Entity* entity =
-      it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr;
+  Entity* entity = GetMutableEntityForSyncId(sync_id);
   DCHECK(entity);
   entity->metadata()->set_acked_sequence_number(
       entity->metadata()->sequence_number());
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.h b/components/sync_bookmarks/synced_bookmark_tracker.h
index 487342d..9be8db4 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.h
+++ b/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -31,10 +31,10 @@
 using NodeMetadataPair = std::pair<const bookmarks::BookmarkNode*,
                                    std::unique_ptr<sync_pb::EntityMetadata>>;
 
-// This class is responsible for keeping the mapping between bookmarks node in
+// This class is responsible for keeping the mapping between bookmark nodes in
 // the local model and the server-side corresponding sync entities. It manages
-// the metadata for its entity and caches entity data upon a local change until
-// commit confirmation is received.
+// the metadata for its entities and caches entity data upon a local change
+// until commit confirmation is received.
 class SyncedBookmarkTracker {
  public:
   class Entity {
@@ -55,7 +55,7 @@
     // Check whether |specifics| matches the stored specifics_hash.
     bool MatchesSpecificsHash(const sync_pb::EntitySpecifics& specifics) const;
 
-    // Returns null for tomstones.
+    // Returns null for tombstones.
     const bookmarks::BookmarkNode* bookmark_node() const {
       return bookmark_node_;
     }
@@ -163,7 +163,7 @@
   void Remove(const std::string& sync_id);
 
   // Increment sequence number in the metadata for the entity with |sync_id|.
-  // Tracker must contain a non-tomstone entity with server id = |sync_id|.
+  // Tracker must contain a non-tombstone entity with server id = |sync_id|.
   void IncrementSequenceNumber(const std::string& sync_id);
 
   sync_pb::BookmarkModelMetadata BuildBookmarkModelMetadata() const;
@@ -232,6 +232,9 @@
       const bookmarks::BookmarkModel* bookmark_model) const;
 
  private:
+  // Returns null if no entity is found.
+  Entity* GetMutableEntityForSyncId(const std::string& sync_id);
+
   // Reorders |entities| that represents local non-deletions such that parent
   // creation/update is before child creation/update. Returns the ordered list.
   std::vector<const Entity*> ReorderUnsyncedEntitiesExceptDeletions(
diff --git a/components/sync_preferences/BUILD.gn b/components/sync_preferences/BUILD.gn
index ed1dbe4..5ef9a0c 100644
--- a/components/sync_preferences/BUILD.gn
+++ b/components/sync_preferences/BUILD.gn
@@ -14,8 +14,6 @@
     "pref_service_syncable_factory.cc",
     "pref_service_syncable_factory.h",
     "pref_service_syncable_observer.h",
-    "synced_pref_change_registrar.cc",
-    "synced_pref_change_registrar.h",
     "synced_pref_observer.h",
   ]
 
diff --git a/components/sync_preferences/synced_pref_change_registrar.cc b/components/sync_preferences/synced_pref_change_registrar.cc
deleted file mode 100644
index a900600..0000000
--- a/components/sync_preferences/synced_pref_change_registrar.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync_preferences/synced_pref_change_registrar.h"
-
-#include "base/bind.h"
-
-namespace sync_preferences {
-
-namespace {
-
-void InvokeUnnamedCallback(
-    const SyncedPrefChangeRegistrar::ChangeCallback& callback,
-    const std::string& path,
-    bool from_sync) {
-  callback.Run(from_sync);
-}
-
-}  // namespace
-
-SyncedPrefChangeRegistrar::SyncedPrefChangeRegistrar(
-    PrefServiceSyncable* pref_service) {
-  pref_service_ = pref_service;
-}
-
-SyncedPrefChangeRegistrar::~SyncedPrefChangeRegistrar() {
-  RemoveAll();
-}
-
-void SyncedPrefChangeRegistrar::Add(const char* path,
-                                    const ChangeCallback& callback) {
-  Add(path, base::Bind(InvokeUnnamedCallback, callback));
-}
-
-void SyncedPrefChangeRegistrar::Add(const char* path,
-                                    const NamedChangeCallback& callback) {
-  DCHECK(!IsObserved(path));
-  observers_[path] = callback;
-  pref_service_->AddSyncedPrefObserver(path, this);
-}
-
-void SyncedPrefChangeRegistrar::Remove(const char* path) {
-  observers_.erase(path);
-  pref_service_->RemoveSyncedPrefObserver(path, this);
-}
-
-void SyncedPrefChangeRegistrar::RemoveAll() {
-  for (auto iter = observers_.begin(); iter != observers_.end(); ++iter) {
-    pref_service_->RemoveSyncedPrefObserver(iter->first, this);
-  }
-  observers_.clear();
-}
-
-bool SyncedPrefChangeRegistrar::IsObserved(const char* path) const {
-  return observers_.find(path) != observers_.end();
-}
-
-void SyncedPrefChangeRegistrar::OnSyncedPrefChanged(const std::string& path,
-                                                    bool from_sync) {
-  if (pref_service_->IsManagedPreference(path))
-    return;
-  ObserverMap::const_iterator iter = observers_.find(path);
-  if (iter == observers_.end())
-    return;
-  iter->second.Run(path, from_sync);
-}
-
-}  // namespace sync_preferences
diff --git a/components/sync_preferences/synced_pref_change_registrar.h b/components/sync_preferences/synced_pref_change_registrar.h
deleted file mode 100644
index 23b62cc..0000000
--- a/components/sync_preferences/synced_pref_change_registrar.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_PREFERENCES_SYNCED_PREF_CHANGE_REGISTRAR_H_
-#define COMPONENTS_SYNC_PREFERENCES_SYNCED_PREF_CHANGE_REGISTRAR_H_
-
-#include <map>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "components/sync_preferences/pref_service_syncable.h"
-#include "components/sync_preferences/synced_pref_observer.h"
-
-namespace sync_preferences {
-
-// Manages the registration of one or more SyncedPrefObservers on a
-// PrefServiceSyncable. This is modeled after base::PrefChangeRegistrar, and
-// it should be used whenever it's necessary to determine whether a pref change
-// has come from sync or from some other mechanism (managed, UI, external, etc.)
-class SyncedPrefChangeRegistrar : public SyncedPrefObserver {
- public:
-  // Registered callbacks may optionally take a path argument.
-  // The boolean argument indicates whether (true) or not (false)
-  // the change was a result of syncing.
-  typedef base::Callback<void(bool)> ChangeCallback;
-  typedef base::Callback<void(const std::string&, bool)> NamedChangeCallback;
-
-  explicit SyncedPrefChangeRegistrar(PrefServiceSyncable* pref_service);
-  virtual ~SyncedPrefChangeRegistrar();
-
-  // Register an observer callback for sync change events on the pref at
-  // |path|. Only one callback may be registered per pref.
-  void Add(const char* path, const ChangeCallback& callback);
-  void Add(const char* path, const NamedChangeCallback& callback);
-
-  // Remove the registered observer for |path|.
-  void Remove(const char* path);
-
-  // Remove all registered observers.
-  void RemoveAll();
-
-  // Indicates whether or not an observer is already registered for |path|.
-  bool IsObserved(const char* path) const;
-
- private:
-  // SyncedPrefObserver implementation
-  void OnSyncedPrefChanged(const std::string& path, bool from_sync) override;
-
-  typedef std::map<std::string, NamedChangeCallback> ObserverMap;
-
-  PrefServiceSyncable* pref_service_;
-  ObserverMap observers_;
-
-  DISALLOW_COPY_AND_ASSIGN(SyncedPrefChangeRegistrar);
-};
-
-}  // namespace sync_preferences
-
-#endif  // COMPONENTS_SYNC_PREFERENCES_SYNCED_PREF_CHANGE_REGISTRAR_H_
diff --git a/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html b/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html
index 7304a41..f5cc996 100644
--- a/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html
+++ b/components/test/data/autofill_assistant/html_iframe/autofill_assistant_external_iframe.html
@@ -13,15 +13,29 @@
 
     <script>
       var removeDiv = function() {
-        var div = document.getElementById("div");
+        var div = document.getElementById("divToRemove");
         div.parentNode.removeChild(div);
       }
+
+      var setText = function() {
+        var select = document.getElementById("select");
+        var input = document.getElementById("myPet");
+        input.value = select.options[select.selectedIndex].label;
+      }
     </script>
   </head>
   <body>
     <button id="button" type="button" onclick="removeDiv()">Button</button>
-    <div id="div">Text</div>
+    <div id="divToRemove">Text</div>
 
     <input id="input" type="text" />
+
+    <select id="select" name="pet" onchange="setText()">
+      <option value="Dog">Dog</option>
+      <option value="Cat">Cat</option>
+      <option value="Hamster">Hamster</option>
+      <option value="Fish">Fish</option>
+    </select>
+    <input type="text" id="myPet"/>
   </body>
 </html>
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 2e4f209e..2fe8a97 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -110,6 +110,26 @@
         << location.ToString();
   }
 
+  void ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason reason,
+                         base::Location location) {
+    base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
+    AddSampleToBuckets(&expected_not_restored_, sample);
+
+    EXPECT_EQ(expected_not_restored_,
+              histogram_tester_.GetAllSamples(
+                  "BackForwardCache.HistoryNavigationOutcome."
+                  "NotRestoredReason"))
+        << location.ToString();
+  }
+
+  void ExpectNotRestoredIsEmpty(base::Location location) {
+    EXPECT_THAT(histogram_tester_.GetAllSamples(
+                    "BackForwardCache.HistoryNavigationOutcome."
+                    "NotRestoredReason"),
+                ElementsAre())
+        << location.ToString();
+  }
+
   void ExpectDisabledWithReason(const std::string& reason,
                                 base::Location location) {
     base::HistogramBase::Sample sample =
@@ -123,24 +143,6 @@
         << location.ToString();
   }
 
-  void ExpectEvicted(BackForwardCacheMetrics::EvictedReason reason,
-                     base::Location location) {
-    base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
-    AddSampleToBuckets(&expected_eviction_reasons_, sample);
-
-    EXPECT_EQ(expected_eviction_reasons_,
-              histogram_tester_.GetAllSamples(
-                  "BackForwardCache.HistoryNavigationOutcome.EvictedReason"))
-        << location.ToString();
-  }
-
-  void ExpectEvictedIsEmpty(base::Location location) {
-    EXPECT_THAT(histogram_tester_.GetAllSamples(
-                    "BackForwardCache.HistoryNavigationOutcome.EvictedReason"),
-                ElementsAre())
-        << location.ToString();
-  }
-
   void ExpectEvictedAfterCommitted(
       std::vector<BackForwardCacheMetrics::EvictedAfterDocumentRestoredReason>
           reasons,
@@ -175,8 +177,8 @@
   FrameTreeVisualizer visualizer_;
   base::HistogramTester histogram_tester_;
   std::vector<base::Bucket> expected_outcomes_;
+  std::vector<base::Bucket> expected_not_restored_;
   std::vector<base::Bucket> expected_disabled_reasons_;
-  std::vector<base::Bucket> expected_eviction_reasons_;
   std::vector<base::Bucket> expected_eviction_after_committing_;
 };
 
@@ -369,6 +371,30 @@
                 FROM_HERE);
 }
 
+// The BackForwardCache does not cache same-website navigations for now.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       DoesNotCacheSameWebsiteNavigations) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_a2(embedded_test_server()->GetURL("a.com", "/title2.html"));
+
+  // 1) Navigate to A1.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a1));
+  RenderFrameHostImpl* rfh_a1 = current_frame_host();
+  RenderFrameDeletedObserver delete_rfh_a1(rfh_a1);
+  int browsing_instance_id = rfh_a1->GetSiteInstance()->GetBrowsingInstanceId();
+
+  // 2) Navigate to A2.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a2));
+  RenderFrameHostImpl* rfh_a2 = current_frame_host();
+  // The BrowsingInstance shouldn't have changed.
+  EXPECT_EQ(browsing_instance_id,
+            rfh_a2->GetSiteInstance()->GetBrowsingInstanceId());
+  EXPECT_FALSE(rfh_a1->is_in_back_forward_cache());
+  // The main frame should have been reused for the navigation.
+  EXPECT_EQ(rfh_a1, rfh_a2);
+}
+
 // The current page can't enter the BackForwardCache if another page can script
 // it. This can happen when one document opens a popup using window.open() for
 // instance. It prevents the BackForwardCache from being used.
@@ -491,7 +517,7 @@
 
   // 3) Flush A.
   web_contents()->GetController().GetBackForwardCache().Flush();
-  EXPECT_TRUE(delete_observer_rfh_a.deleted());  // Flush is synchronous.
+  delete_observer_rfh_a.WaitUntilDeleted();
   EXPECT_FALSE(delete_observer_rfh_b.deleted());
 
   // 4) Go back to a new A.
@@ -501,7 +527,7 @@
 
   // 5) Flush B.
   web_contents()->GetController().GetBackForwardCache().Flush();
-  EXPECT_TRUE(delete_observer_rfh_b.deleted());  // Flush is synchronous.
+  delete_observer_rfh_b.WaitUntilDeleted();
 }
 
 // Check the visible URL in the omnibox is properly updated when restoring a
@@ -558,7 +584,8 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
                 FROM_HERE);
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kCacheLimit, FROM_HERE);
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::kCacheLimit,
+                    FROM_HERE);
 }
 
 // Test documents are evicted from the BackForwardCache at some point.
@@ -1079,6 +1106,13 @@
 
   // The page with the unsupported feature should be deleted (not cached).
   delete_observer_rfh_a.WaitUntilDeleted();
+
+  // Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1098,6 +1132,13 @@
 
   // The page with the unsupported feature should be deleted (not cached).
   delete_rfh_a.WaitUntilDeleted();
+
+  // Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1198,6 +1239,13 @@
   // The page was still recording audio when we navigated away, so it shouldn't
   // have been cached.
   deleted.WaitUntilDeleted();
+
+  // 3) Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1226,6 +1274,13 @@
   // The page was still recording audio when we navigated away, so it shouldn't
   // have been cached.
   deleted.WaitUntilDeleted();
+
+  // 3) Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1261,6 +1316,12 @@
   // The page was still loading when we navigated away, so it shouldn't have
   // been cached.
   delete_observer_rfh_a.WaitUntilDeleted();
+
+  // 3) Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_FALSE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::kLoading,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1283,6 +1344,15 @@
   // The page was still loading when we navigated away, so it shouldn't have
   // been cached.
   delete_observer_rfh_a.WaitUntilDeleted();
+
+  // 3) Go back.
+  TestNavigationManager navigation_manager_back(shell()->web_contents(), url);
+  web_contents()->GetController().GoBack();
+  navigation_manager_back.WaitForNavigationFinished();
+  // The recorded reason is 'blocklisted features: outstanding network request'.
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1314,6 +1384,12 @@
   // The page should not have been added to cache, since it had a subframe that
   // was still loading at the time it was navigated away from.
   delete_observer_rfh_a.WaitUntilDeleted();
+
+  // 3) Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::kLoading,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1346,6 +1422,12 @@
   // The page should not have been added to the cache, since it had an iframe
   // that was still loading at the time it was navigated away from.
   delete_rfh_a.WaitUntilDeleted();
+
+  // 3) Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::kLoading,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfWebGL) {
@@ -1360,9 +1442,17 @@
 
   // 2) Navigate away.
   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  delete_observer_rfh_a.WaitUntilDeleted();
 
   // The page had an active WebGL context when we navigated away,
   // so it shouldn't have been cached.
+
+  // 3) Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfHttpError) {
@@ -1381,6 +1471,12 @@
 
   // The page did not return 200 (OK), so it shouldn't have been cached.
   delete_rfh_a.WaitUntilDeleted();
+
+  // Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1415,6 +1511,12 @@
 
   // The page had a networking error, so it shouldn't have been cached.
   delete_rfh_a.WaitUntilDeleted();
+
+  // Go back.
+  web_contents()->GetController().GoBack();
+  EXPECT_FALSE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK, FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1475,8 +1577,9 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
                 FROM_HERE);
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kJavaScriptExecution,
-                FROM_HERE);
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution,
+      FROM_HERE);
 }
 
 // Similar to BackForwardCacheBrowserTest.EvictionOnJavaScriptExecution.
@@ -1521,8 +1624,9 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
                 FROM_HERE);
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kJavaScriptExecution,
-                FROM_HERE);
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution,
+      FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -1563,8 +1667,9 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
                 FROM_HERE);
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kJavaScriptExecution,
-                FROM_HERE);
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution,
+      FROM_HERE);
 }
 
 // Similar to BackForwardCacheBrowserTest.EvictionOnJavaScriptExecution, but
@@ -1919,8 +2024,51 @@
 
   ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
                 FROM_HERE);
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kJavaScriptExecution,
-                FROM_HERE);
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution,
+      FROM_HERE);
+}
+
+// Similar to ReissuesNavigationIfEvictedDuringNavigation, except that
+// BackForwardCache::Flush is the source of the eviction.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       FlushCacheDuringNavigationToCachedPage) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+  // 1) Navigate to page A.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a1 = current_frame_host();
+  RenderFrameDeletedObserver delete_observer_rfh_a1(rfh_a1);
+
+  // 2) Navigate to page B.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  RenderFrameHostImpl* rfh_b2 = current_frame_host();
+  RenderFrameDeletedObserver delete_observer_rfh_b2(rfh_b2);
+  EXPECT_FALSE(delete_observer_rfh_a1.deleted());
+  EXPECT_TRUE(rfh_a1->is_in_back_forward_cache());
+  EXPECT_NE(rfh_a1, rfh_b2);
+
+  // 3) Start navigation to page A, and flush the cache during the navigation.
+  TestNavigationManager navigation_manager(shell()->web_contents(), url_a);
+  web_contents()->GetController().GoBack();
+
+  EXPECT_TRUE(navigation_manager.WaitForRequestStart());
+
+  // Flush the cache, which contains the document being navigated to.
+  web_contents()->GetController().GetBackForwardCache().Flush();
+
+  // The navigation should get canceled, then reissued; ultimately resulting in
+  // a successful navigation using a new RenderFrameHost.
+  navigation_manager.WaitForNavigationFinished();
+
+  // rfh_a should have been deleted, and page A navigated to normally.
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  delete_observer_rfh_a1.WaitUntilDeleted();
+  EXPECT_TRUE(rfh_b2->is_in_back_forward_cache());
+  RenderFrameHostImpl* rfh_a3 = current_frame_host();
+  EXPECT_EQ(rfh_a3->GetLastCommittedURL(), url_a);
 }
 
 // Test that if the renderer process crashes while a document is in the
@@ -1963,8 +2111,9 @@
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kNotRestored,
                 FROM_HERE);
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kRendererProcessKilled,
-                FROM_HERE);
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kRendererProcessKilled,
+      FROM_HERE);
 }
 
 // The test is simulating a race condition. The scheduler tracked features are
@@ -2192,6 +2341,13 @@
   deleted.WaitUntilDeleted();
 
   ExpectOutcomeIsEmpty(FROM_HERE);
+
+  // 3) Go back to A.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  ExpectNotRestored(
+      BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
+      FROM_HERE);
 }
 
 class BackForwardCacheBrowserTestWithServiceWorkerEnabled
@@ -2270,12 +2426,31 @@
 }
 
 // Regression test for https://crbug.com/993337.
+//
+// A note about sharing BrowsingInstances and the BackForwardCache:
+//
+// We should never keep around more than one main frame that belongs to the same
+// BrowsingInstance. When swapping two pages, when one is stored in the
+// back-forward cache or one is restored from it, the current code expects the
+// two to live in different BrowsingInstances.
+//
+// History navigation can recreate a page with the same BrowsingInstance as the
+// one stored in the back-forward cache. This case must to be handled. When it
+// happens, the back-forward cache page is evicted.
+//
+// Since cache eviction is asynchronous, it's is possible for two main frames
+// belonging to the same BrowsingInstance to be alive for a brief period of time
+// (the new page being navigated to, and a page in the cache, until it is
+// destroyed asynchronously via eviction).
+//
+// The test below tests that the brief period of time where two main frames are
+// alive in the same BrowsingInstance does not cause anything to blow up.
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
                        NavigateToTwoPagesOnSameSite) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_a2(embedded_test_server()->GetURL("a.com", "/title2.html"));
-  GURL url_b1(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  GURL url_b3(embedded_test_server()->GetURL("b.com", "/title1.html"));
 
   // 1) Navigate to A1.
   EXPECT_TRUE(NavigateToURL(shell(), url_a1));
@@ -2285,21 +2460,159 @@
   RenderFrameHostImpl* rfh_a2 = current_frame_host();
   RenderFrameDeletedObserver delete_rfh_a2(current_frame_host());
 
-  // 3) Navigate to B1.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b1));
+  // 3) Navigate to B3.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b3));
   EXPECT_TRUE(rfh_a2->is_in_back_forward_cache());
-  RenderFrameHostImpl* rfh_b1 = current_frame_host();
+  RenderFrameHostImpl* rfh_b3 = current_frame_host();
 
   // 4) Do a history navigation back to A1.
   web_contents()->GetController().GoToIndex(0);
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  EXPECT_TRUE(rfh_b1->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b3->is_in_back_forward_cache());
 
-  // As a result, rfh_a2 is deleted. The history navigation tried to restore an
-  // entry using the same BrowsingInstance. They both can't live together.
+  // Note that the frame for A1 gets created before A2 is deleted from the
+  // cache, so there will be a brief period where two the main frames (A1 and
+  // A2) are alive in the same BrowsingInstance/SiteInstance, at the same time.
+  // That is the scenario this test is covering. This used to cause a CHECK,
+  // because the two main frames shared a single RenderViewHost (no longer the
+  // case after https://crrev.com/c/1833616).
+
+  // A2 should be evicted from the cache and asynchronously deleted, due to the
+  // cache size limit (B3 took its place in the cache).
   delete_rfh_a2.WaitUntilDeleted();
 }
 
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       NavigateToTwoPagesOnSameSiteWithSubframes) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // This test covers the same scenario as NavigateToTwoPagesOnSameSite, except
+  // the pages contain subframes:
+  // A1(B) -> A2(B(C)) -> D3 -> A1(B)
+  //
+  // The subframes shouldn't make a difference, so the expected behavior is the
+  // same as NavigateToTwoPagesOnSameSite.
+  GURL url_a1(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  GURL url_a2(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
+  GURL url_d3(embedded_test_server()->GetURL("d.com", "/title1.html"));
+
+  // 1) Navigate to A1(B).
+  EXPECT_TRUE(NavigateToURL(shell(), url_a1));
+
+  // 2) Navigate to A2(B(C)).
+  EXPECT_TRUE(NavigateToURL(shell(), url_a2));
+  RenderFrameHostImpl* rfh_a2 = current_frame_host();
+  RenderFrameDeletedObserver delete_rfh_a2(current_frame_host());
+
+  // 3) Navigate to D3.
+  EXPECT_TRUE(NavigateToURL(shell(), url_d3));
+  EXPECT_TRUE(rfh_a2->is_in_back_forward_cache());
+  RenderFrameHostImpl* rfh_d3 = current_frame_host();
+
+  // 4) Do a history navigation back to A1(B).
+  web_contents()->GetController().GoToIndex(0);
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+
+  // D3 takes A2(B(C))'s place in the cache.
+  EXPECT_TRUE(rfh_d3->is_in_back_forward_cache());
+  delete_rfh_a2.WaitUntilDeleted();
+}
+
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       ConflictingBrowsingInstances) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_a2(embedded_test_server()->GetURL("a.com", "/title2.html"));
+  GURL url_b3(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to A1.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a1));
+
+  // 2) Navigate to A2.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a2));
+  RenderFrameHostImpl* rfh_a2 = current_frame_host();
+  RenderFrameDeletedObserver delete_rfh_a2(current_frame_host());
+
+  // 3) Navigate to B3.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b3));
+  EXPECT_TRUE(rfh_a2->is_in_back_forward_cache());
+  RenderFrameHostImpl* rfh_b3 = current_frame_host();
+  // Make B3 ineligible for caching, so that navigating doesn't evict A2
+  // due to the cache size limit.
+  content::BackForwardCache::DisableForRenderFrameHost(
+      rfh_b3, "BackForwardCacheBrowserTest");
+
+  // 4) Do a history navigation back to A1.  At this point, A1 is going to have
+  // the same BrowsingInstance as A2. This should cause A2 to get
+  // evicted from the BackForwardCache due to its conflicting BrowsingInstance.
+  web_contents()->GetController().GoToIndex(0);
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  EXPECT_EQ(current_frame_host()->GetLastCommittedURL(), url_a1);
+  delete_rfh_a2.WaitUntilDeleted();
+}
+
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       CanCacheMultiplesPagesOnSameDomain) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b2(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  GURL url_a3(embedded_test_server()->GetURL("a.com", "/title2.html"));
+  GURL url_b4(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+  // Increase the cache size so we're able to store multiple pages for the same
+  // site in the cache at once.
+  web_contents()
+      ->GetController()
+      .GetBackForwardCache()
+      .set_cache_size_limit_for_testing(3);
+
+  // 1) Navigate to A1.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a1));
+  RenderFrameHostImpl* rfh_a1 = current_frame_host();
+
+  // 2) Navigate to B2.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b2));
+  RenderFrameHostImpl* rfh_b2 = current_frame_host();
+  EXPECT_TRUE(rfh_a1->is_in_back_forward_cache());
+
+  // 3) Navigate to A3.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a3));
+  RenderFrameHostImpl* rfh_a3 = current_frame_host();
+  EXPECT_TRUE(rfh_a1->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b2->is_in_back_forward_cache());
+  // A1 and A3 shouldn't be treated as the same site instance.
+  EXPECT_NE(rfh_a1->GetSiteInstance(), rfh_a3->GetSiteInstance());
+
+  // 4) Navigate to B4.
+  // Make sure we can store A1 and A3 in the cache at the same time.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b4));
+  RenderFrameHostImpl* rfh_b4 = current_frame_host();
+  EXPECT_TRUE(rfh_a1->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b2->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_a3->is_in_back_forward_cache());
+
+  // 5) Go back to A3.
+  // Make sure we can restore A3, while A1 remains in the cache.
+  web_contents()->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  EXPECT_TRUE(rfh_a1->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b2->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b4->is_in_back_forward_cache());
+  EXPECT_EQ(rfh_a3, current_frame_host());
+  // B2 and B4 shouldn't be treated as the same site instance.
+  EXPECT_NE(rfh_b2->GetSiteInstance(), rfh_b4->GetSiteInstance());
+
+  // 6) Do a history navigation back to A1.
+  // Make sure we can restore A1, while coming from A3.
+  web_contents()->GetController().GoToIndex(0);
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  EXPECT_TRUE(rfh_b2->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_b4->is_in_back_forward_cache());
+  EXPECT_TRUE(rfh_a3->is_in_back_forward_cache());
+  EXPECT_EQ(rfh_a1, current_frame_host());
+}
+
 class GeolocationBackForwardCacheBrowserTest
     : public BackForwardCacheBrowserTest {
  protected:
@@ -2485,7 +2798,8 @@
   // 7) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectEvicted(BackForwardCacheMetrics::EvictedReason::kTimeout, FROM_HERE);
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::kTimeout,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -2512,7 +2826,9 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectDisabledWithReason("DisabledByBackForwardCacheBrowserTest", FROM_HERE);
-  ExpectEvictedIsEmpty(FROM_HERE);
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::
+                        kDisableForRenderFrameHostCalled,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2543,7 +2859,9 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectDisabledWithReason("DisabledByBackForwardCacheBrowserTest", FROM_HERE);
-  ExpectEvictedIsEmpty(FROM_HERE);
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::
+                        kDisableForRenderFrameHostCalled,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2571,7 +2889,9 @@
   // 3) Go back to A.
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
-  ExpectEvictedIsEmpty(FROM_HERE);
+  ExpectNotRestored(BackForwardCacheMetrics::NotRestoredReason::
+                        kDisableForRenderFrameHostCalled,
+                    FROM_HERE);
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
@@ -2604,7 +2924,7 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   ExpectDisabledWithReason("DisabledByBackForwardCacheBrowserTest", FROM_HERE);
-  ExpectEvictedIsEmpty(FROM_HERE);
+  ExpectNotRestoredIsEmpty(FROM_HERE);
 }
 
 // Confirm that same-document navigation and not history-navigation does not
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc
index d625950b0..bcea784 100644
--- a/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -83,8 +83,8 @@
 }
 
 uint64_t GetDisallowedFeatures(RenderFrameHostImpl* rfh) {
-  // TODO(lowell): Finalize disallowed feature list, and test for each
-  // disallowed feature.
+  // TODO(https://crbug.com/1015784): Finalize disallowed feature list, and test
+  // for each disallowed feature.
   constexpr uint64_t kAlwaysDisallowedFeatures =
       ToFeatureBit(WebSchedulerTrackedFeature::kWebRTC) |
       ToFeatureBit(WebSchedulerTrackedFeature::kContainsPlugins) |
@@ -175,7 +175,7 @@
 BackForwardCacheImpl::Entry::~Entry() {}
 
 std::string BackForwardCacheImpl::CanStoreDocumentResult::ToString() {
-  using Reason = BackForwardCacheMetrics::CanNotStoreDocumentReason;
+  using Reason = BackForwardCacheMetrics::NotRestoredReason;
 
   if (can_store)
     return "Yes";
@@ -206,6 +206,26 @@
       return "No: HTTP method is not GET";
     case Reason::kSubframeIsNavigating:
       return "No: subframe navigation is in progress";
+    case Reason::kTimeout:
+      return "No: timeout";
+    case Reason::kCacheLimit:
+      return "No: cache limit";
+    case Reason::kJavaScriptExecution:
+      return "No: JavaScript execution";
+    case Reason::kRendererProcessKilled:
+      return "No: renderer process is killed";
+    case Reason::kRendererProcessCrashed:
+      return "No: renderer process crashed";
+    case Reason::kDialog:
+      return "No: dialog";
+    case Reason::kGrantedMediaStreamAccess:
+      return "No: granted media stream access";
+    case Reason::kSchedulerTrackedFeatureUsed:
+      return "No: scheduler tracked feature is used";
+    case Reason::kConflictingBrowsingInstance:
+      return "No: conflicting BrowsingInstance";
+    case Reason::kCacheFlushed:
+      return "No: cache flushed";
   }
 }
 
@@ -221,7 +241,7 @@
 
 BackForwardCacheImpl::CanStoreDocumentResult
 BackForwardCacheImpl::CanStoreDocumentResult::No(
-    BackForwardCacheMetrics::CanNotStoreDocumentReason reason) {
+    BackForwardCacheMetrics::NotRestoredReason reason) {
   return CanStoreDocumentResult(false, reason, 0);
 }
 
@@ -229,14 +249,13 @@
 BackForwardCacheImpl::CanStoreDocumentResult::NoDueToFeatures(
     uint64_t blocklisted_features) {
   return CanStoreDocumentResult(
-      false,
-      BackForwardCacheMetrics::CanNotStoreDocumentReason::kBlocklistedFeatures,
+      false, BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures,
       blocklisted_features);
 }
 
 BackForwardCacheImpl::CanStoreDocumentResult::CanStoreDocumentResult(
     bool can_store,
-    base::Optional<BackForwardCacheMetrics::CanNotStoreDocumentReason> reason,
+    base::Optional<BackForwardCacheMetrics::NotRestoredReason> reason,
     uint64_t blocklisted_features)
     : can_store(can_store),
       reason(reason),
@@ -278,13 +297,12 @@
   // Use the BackForwardCache only for the main frame.
   if (rfh->GetParent()) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::kNotMainFrame);
+        BackForwardCacheMetrics::NotRestoredReason::kNotMainFrame);
   }
 
   if (!IsBackForwardCacheEnabled() || is_disabled_for_testing_) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::
-            kBackForwardCacheDisabled);
+        BackForwardCacheMetrics::NotRestoredReason::kBackForwardCacheDisabled);
   }
 
   // Two pages in the same BrowsingInstance can script each other. When a page
@@ -298,7 +316,7 @@
   // BrowsingInstance.
   if (rfh->GetSiteInstance()->GetRelatedActiveContentsCount() != 0) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::
+        BackForwardCacheMetrics::NotRestoredReason::
             kRelatedActiveContentsExist);
   }
 
@@ -306,27 +324,26 @@
   // Note that for error pages, |last_http_status_code| is equal to 0.
   if (rfh->last_http_status_code() != net::HTTP_OK) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::kHTTPStatusNotOK);
+        BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK);
   }
 
   // Only store documents that were fetched via HTTP GET method.
   if (rfh->last_http_method() != net::HttpRequestHeaders::kGetMethod) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::kHTTPMethodNotGET);
+        BackForwardCacheMetrics::NotRestoredReason::kHTTPMethodNotGET);
   }
 
   // Do not store main document with non HTTP/HTTPS URL scheme. In particular,
   // this excludes the new tab page.
   if (!rfh->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::
-            kSchemeNotHTTPOrHTTPS);
+        BackForwardCacheMetrics::NotRestoredReason::kSchemeNotHTTPOrHTTPS);
   }
 
   // Only store documents that have URLs allowed through experiment.
   if (!IsAllowed(rfh->GetLastCommittedURL())) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::kDomainNotAllowed);
+        BackForwardCacheMetrics::NotRestoredReason::kDomainNotAllowed);
   }
 
   return CanStoreRenderFrameHost(rfh);
@@ -336,22 +353,22 @@
 // can be cached.
 BackForwardCacheImpl::CanStoreDocumentResult
 BackForwardCacheImpl::CanStoreRenderFrameHost(RenderFrameHostImpl* rfh) {
-  if (!rfh->dom_content_loaded())
+  if (!rfh->dom_content_loaded()) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::kLoading);
+        BackForwardCacheMetrics::NotRestoredReason::kLoading);
+  }
 
   // If the rfh has ever granted media access, prevent it from entering cache.
   // TODO(crbug.com/989379): Consider only blocking when there's an active
   //                         media stream.
   if (rfh->was_granted_media_access()) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::
-            kWasGrantedMediaAccess);
+        BackForwardCacheMetrics::NotRestoredReason::kWasGrantedMediaAccess);
   }
 
   if (rfh->is_back_forward_cache_disallowed()) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::
+        BackForwardCacheMetrics::NotRestoredReason::
             kDisableForRenderFrameHostCalled);
   }
 
@@ -369,8 +386,7 @@
   // Do not cache if we have navigations in any of the subframes.
   if (rfh->GetParent() && has_navigation_request) {
     return CanStoreDocumentResult::No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason::
-            kSubframeIsNavigating);
+        BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating);
   }
 
   for (size_t i = 0; i < rfh->child_count(); i++) {
@@ -402,7 +418,7 @@
       continue;
     if (++available_count > size_limit) {
       stored_entry->render_frame_host->EvictFromBackForwardCacheWithReason(
-          BackForwardCacheMetrics::EvictedReason::kCacheLimit);
+          BackForwardCacheMetrics::NotRestoredReason::kCacheLimit);
     }
   }
 }
@@ -449,9 +465,27 @@
 
 void BackForwardCacheImpl::Flush() {
   TRACE_EVENT0("navigation", "BackForwardCache::Flush");
+  for (std::unique_ptr<Entry>& entry : entries_) {
+    entry->render_frame_host->EvictFromBackForwardCacheWithReason(
+        BackForwardCacheMetrics::NotRestoredReason::kCacheFlushed);
+  }
+}
+
+void BackForwardCacheImpl::Shutdown() {
   entries_.clear();
 }
 
+void BackForwardCacheImpl::EvictFramesInRelatedSiteInstances(
+    SiteInstance* site_instance) {
+  for (std::unique_ptr<Entry>& entry : entries_) {
+    if (entry->render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
+            site_instance))
+      entry->render_frame_host->EvictFromBackForwardCacheWithReason(
+          BackForwardCacheMetrics::NotRestoredReason::
+              kConflictingBrowsingInstance);
+  }
+}
+
 void BackForwardCacheImpl::PostTaskToDestroyEvictedFrames() {
   base::PostTask(FROM_HERE, {BrowserThread::UI},
                  base::BindOnce(&BackForwardCacheImpl::DestroyEvictedFrames,
diff --git a/content/browser/frame_host/back_forward_cache_impl.h b/content/browser/frame_host/back_forward_cache_impl.h
index ff6a46b..4c5ff272 100644
--- a/content/browser/frame_host/back_forward_cache_impl.h
+++ b/content/browser/frame_host/back_forward_cache_impl.h
@@ -27,6 +27,7 @@
 class RenderFrameHostImpl;
 class RenderFrameProxyHost;
 class RenderViewHostImpl;
+class SiteInstance;
 
 // BackForwardCache:
 //
@@ -77,12 +78,13 @@
     ~CanStoreDocumentResult();
 
     bool can_store;
-    base::Optional<BackForwardCacheMetrics::CanNotStoreDocumentReason> reason;
+    // TODO(hajimehoshi): Accept multiple reasons.
+    base::Optional<BackForwardCacheMetrics::NotRestoredReason> reason;
     uint64_t blocklisted_features;
 
     static CanStoreDocumentResult Yes();
     static CanStoreDocumentResult No(
-        BackForwardCacheMetrics::CanNotStoreDocumentReason reason);
+        BackForwardCacheMetrics::NotRestoredReason reason);
     static CanStoreDocumentResult NoDueToFeatures(uint64_t features);
 
     std::string ToString();
@@ -92,8 +94,7 @@
    private:
     CanStoreDocumentResult(
         bool can_store,
-        base::Optional<BackForwardCacheMetrics::CanNotStoreDocumentReason>
-            reason,
+        base::Optional<BackForwardCacheMetrics::NotRestoredReason> reason,
         uint64_t blocklisted_features);
   };
 
@@ -128,9 +129,17 @@
   // knowing its |navigation_entry_id|. Returns nullptr when none is found.
   std::unique_ptr<Entry> RestoreEntry(int navigation_entry_id);
 
-  // Remove all entries from the BackForwardCache.
+  // Evict all entries from the BackForwardCache.
   void Flush();
 
+  // Evict all cached pages in the same BrowsingInstance as
+  // |site_instance|.
+  void EvictFramesInRelatedSiteInstances(SiteInstance* site_instance);
+
+  // Immediately deletes all frames in the cache. This should only be called
+  // when WebContents is being destroyed.
+  void Shutdown();
+
   // Posts a task to destroy all frames in the BackForwardCache that have been
   // marked as evicted.
   void PostTaskToDestroyEvictedFrames();
diff --git a/content/browser/frame_host/back_forward_cache_metrics.cc b/content/browser/frame_host/back_forward_cache_metrics.cc
index 871a700d..a7b5658d 100644
--- a/content/browser/frame_host/back_forward_cache_metrics.cc
+++ b/content/browser/frame_host/back_forward_cache_metrics.cc
@@ -98,11 +98,10 @@
     NavigationRequest* navigation) {
   bool is_history_navigation =
       navigation->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK;
-  if (navigation->IsInMainFrame() && !navigation->IsSameDocument() &&
-      is_history_navigation) {
-    RecordMetricsForHistoryNavigationCommit(navigation);
-    disallowed_reasons_.clear();
-    evicted_reason_ = base::nullopt;
+  if (navigation->IsInMainFrame() && !navigation->IsSameDocument()) {
+    if (is_history_navigation)
+      RecordMetricsForHistoryNavigationCommit(navigation);
+    not_restored_reasons_.reset();
   }
 
   if (last_committed_main_frame_navigation_id_ != -1 &&
@@ -178,14 +177,14 @@
   }
 }
 
-void BackForwardCacheMetrics::MarkDisableForRenderFrameHost(
-    const base::StringPiece& reason) {
-  disallowed_reasons_.push_back(reason.as_string());
+void BackForwardCacheMetrics::MarkNotRestoredWithReason(
+    NotRestoredReason reason) {
+  not_restored_reasons_.set(static_cast<size_t>(reason));
 }
 
-void BackForwardCacheMetrics::MarkEvictedFromBackForwardCacheWithReason(
-    BackForwardCacheMetrics::EvictedReason reason) {
-  evicted_reason_ = reason;
+void BackForwardCacheMetrics::MarkDisableForRenderFrameHost(
+    const base::StringPiece& reason) {
+  disallowed_reasons_.insert(reason.as_string());
 }
 
 void BackForwardCacheMetrics::RecordMetricsForHistoryNavigationCommit(
@@ -199,15 +198,20 @@
         BackForwardCacheMetrics::EvictedAfterDocumentRestoredReason::kRestored);
   }
 
-  // TODO(hajimehoshi): Do not record the outcome when the experient condition
-  // does not match.
+  // TODO(hajimehoshi): By |BackForwardCache::IsAllowed(navigation->GetURL())|,
+  // check whether the page matches the experiment condition, and do not record
+  // anything in this case.
+
   UMA_HISTOGRAM_ENUMERATION("BackForwardCache.HistoryNavigationOutcome",
                             outcome);
 
-  if (evicted_reason_.has_value()) {
+  for (int i = 0; i <= static_cast<int>(NotRestoredReason::kMaxValue); i++) {
+    if (!not_restored_reasons_.test(static_cast<size_t>(i)))
+      continue;
+    DCHECK(!navigation->IsServedFromBackForwardCache());
     UMA_HISTOGRAM_ENUMERATION(
-        "BackForwardCache.HistoryNavigationOutcome.EvictedReason",
-        evicted_reason_.value());
+        "BackForwardCache.HistoryNavigationOutcome.NotRestoredReason",
+        static_cast<NotRestoredReason>(i));
   }
 
   for (const std::string& reason : disallowed_reasons_) {
diff --git a/content/browser/frame_host/back_forward_cache_metrics.h b/content/browser/frame_host/back_forward_cache_metrics.h
index 62e95f5..0e0613c 100644
--- a/content/browser/frame_host/back_forward_cache_metrics.h
+++ b/content/browser/frame_host/back_forward_cache_metrics.h
@@ -5,6 +5,9 @@
 #ifndef CONTENT_BROWSER_FRAME_HOST_BACK_FORWARD_CACHE_METRICS_H_
 #define CONTENT_BROWSER_FRAME_HOST_BACK_FORWARD_CACHE_METRICS_H_
 
+#include <bitset>
+#include <set>
+
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
@@ -32,19 +35,32 @@
 class BackForwardCacheMetrics
     : public base::RefCounted<BackForwardCacheMetrics> {
  public:
-  enum class CanNotStoreDocumentReason : uint8_t {
-    kNotMainFrame,
-    kBackForwardCacheDisabled,
-    kRelatedActiveContentsExist,
-    kHTTPStatusNotOK,
-    kSchemeNotHTTPOrHTTPS,
-    kLoading,
-    kWasGrantedMediaAccess,
-    kBlocklistedFeatures,
-    kDisableForRenderFrameHostCalled,
-    kDomainNotAllowed,
-    kHTTPMethodNotGET,
-    kSubframeIsNavigating
+  // Please keep in sync with BackForwardCacheNotRestoredReason in
+  // tools/metrics/histograms/enums.xml. These values should not be renumbered.
+  enum class NotRestoredReason : uint8_t {
+    kNotMainFrame = 0,
+    kBackForwardCacheDisabled = 1,
+    kRelatedActiveContentsExist = 2,
+    kHTTPStatusNotOK = 3,
+    kSchemeNotHTTPOrHTTPS = 4,
+    kLoading = 5,
+    kWasGrantedMediaAccess = 6,
+    kBlocklistedFeatures = 7,
+    kDisableForRenderFrameHostCalled = 8,
+    kDomainNotAllowed = 9,
+    kHTTPMethodNotGET = 10,
+    kSubframeIsNavigating = 11,
+    kTimeout = 12,
+    kCacheLimit = 13,
+    kJavaScriptExecution = 14,
+    kRendererProcessKilled = 15,
+    kRendererProcessCrashed = 16,
+    kDialog = 17,
+    kGrantedMediaStreamAccess = 18,
+    kSchedulerTrackedFeatureUsed = 19,
+    kConflictingBrowsingInstance = 20,
+    kCacheFlushed = 21,
+    kMaxValue = kCacheFlushed,
   };
 
   // Please keep in sync with BackForwardCacheHistoryNavigationOutcome in
@@ -55,20 +71,6 @@
     kMaxValue = kNotRestored,
   };
 
-  // Please keep in sync with BackForwardCacheEvictedReason in
-  // tools/metrics/histograms/enums.xml. These values should not be renumbered.
-  enum class EvictedReason {
-    kTimeout = 0,
-    kCacheLimit = 1,
-    kJavaScriptExecution = 2,
-    kRendererProcessKilled = 3,
-    kRendererProcessCrashed = 4,
-    kDialog = 5,
-    kGrantedMediaStreamAccess = 6,
-    kSchedulerTrackedFeatureUsed = 7,
-    kMaxValue = kSchedulerTrackedFeatureUsed,
-  };
-
   // Please keep in sync with BackForwardCacheEvictedAfterDocumentRestoredReason
   // in tools/metrics/histograms/enums.xml. These values should not be
   // renumbered.
@@ -121,10 +123,9 @@
   // placed in the back-forward cache.
   void RecordFeatureUsage(RenderFrameHostImpl* main_frame);
 
-  // Marks when the page is evicted with the reason. This information is useful
-  // e.g., to know the major cause of eviction.
-  void MarkEvictedFromBackForwardCacheWithReason(
-      BackForwardCacheMetrics::EvictedReason reason);
+  // Marks when the page is not cached, or evicted. This information is useful
+  // e.g., to prioritize the tasks to improve cache-hit rate.
+  void MarkNotRestoredWithReason(NotRestoredReason reason);
 
   // Marks the frame disabled the back forward cache with the reason.
   void MarkDisableForRenderFrameHost(const base::StringPiece& reason);
@@ -170,8 +171,12 @@
   base::Optional<base::TimeTicks> started_navigation_timestamp_;
   base::Optional<base::TimeTicks> navigated_away_from_main_document_timestamp_;
 
-  std::vector<std::string> disallowed_reasons_;
-  base::Optional<EvictedReason> evicted_reason_;
+  std::bitset<static_cast<size_t>(NotRestoredReason::kMaxValue) + 1ul>
+      not_restored_reasons_;
+
+  // The reasons given at BackForwardCache::DisableForRenderFrameHost. These are
+  // a further breakdown of NotRestoredReason::kDisableForRenderFrameHostCalled.
+  std::set<std::string> disallowed_reasons_;
 
   DISALLOW_COPY_AND_ASSIGN(BackForwardCacheMetrics);
 };
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 2ab2b0c0..ce88050 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -2558,18 +2558,30 @@
     return;
   }
 
-  // By design, a page in the BackForwardCache is alone in its BrowsingInstance.
-  // History navigation might try to reuse a specific SiteInstance, already used
-  // by a page in the cache. This must not happen. It would fail creating the
-  // RenderFrame, because only one main document can live there. For this
-  // reason, the BackForwardCache is flushed.
-  // TODO(arthursonzogni): Flushing the entire cache is a bit overkill, this can
-  // be refined to only delete the page (if any) using the same
-  // BrowsingInstance.
+  // History navigation might try to reuse a specific BrowsingInstance, already
+  // used by a page in the cache. To avoid having two different main frames that
+  // live in the same BrowsingInstance, evict the all pages with this
+  // BrowsingInstance from the cache.
+  //
+  // For example, take the following scenario:
+  //
+  // A1 = Some page on a.com
+  // A2 = Some other page on a.com
+  // B3 = An uncacheable page on b.com
+  //
+  // Then the following navigations occur:
+  // A1->A2->B3->A1
+  // On the navigation from B3 to A1, A2 will remain in the cache (B3 doesn't
+  // take its place) and A1 will be created in the same BrowsingInstance (and
+  // SiteInstance), as A2.
+  //
+  // If we didn't do anything, both A1 and A2 would remain alive in the same
+  // BrowsingInstance/SiteInstance, which is unsupported by
+  // RenderFrameHostManager::CommitPending(). To avoid this conundrum, we evict
+  // A2 from the cache.
   if (pending_entry_->site_instance()) {
-    SiteInstance* current = root->current_frame_host()->GetSiteInstance();
-    if (!current->IsRelatedSiteInstance(pending_entry_->site_instance()))
-      back_forward_cache_.Flush();
+    back_forward_cache_.EvictFramesInRelatedSiteInstances(
+        pending_entry_->site_instance());
   }
 
   // If we were navigating to a slow-to-commit page, and the user performs
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 6f34a9896..9eae307 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1355,9 +1355,11 @@
 void NavigationRequest::OnRequestRedirected(
     const net::RedirectInfo& redirect_info,
     const scoped_refptr<network::ResourceResponse>& response_head) {
+  // Sanity check - this can only be set at commit time.
+  DCHECK(!auth_challenge_info_);
+
   response_head_ = response_head;
   ssl_info_ = response_head->head.ssl_info;
-  auth_challenge_info_ = response_head->head.auth_challenge_info;
 
   // Reset the page state as it can no longer be used at commit time since the
   // navigation was redirected.
@@ -3201,24 +3203,6 @@
       starting_site_instance_->GetIsolationContext(), common_params_->url);
 }
 
-net::NetworkIsolationKey NavigationRequest::GetNetworkIsolationKey() const {
-  if (network_isolation_key_)
-    return network_isolation_key_.value();
-
-  // If this is a top-frame navigation, then use the origin of the url (and
-  // update it as redirects happen). If this is a subframe navigation, get the
-  // URL from the top frame.
-  // TODO(crbug.com/979296): Consider changing this code to copy an origin
-  // instead of creating one from a URL which lacks opacity information.
-  url::Origin frame_origin = url::Origin::Create(common_params_->url);
-  url::Origin top_frame_origin =
-      frame_tree_node_->IsMainFrame()
-          ? frame_origin
-          : frame_tree_node_->frame_tree()->root()->current_origin();
-
-  return net::NetworkIsolationKey(top_frame_origin, frame_origin);
-}
-
 // TODO(zetamoo): Try to merge this function inside its callers.
 void NavigationRequest::UpdateStateFollowingRedirect(
     const GURL& new_referrer_url,
@@ -3688,6 +3672,24 @@
   return auth_challenge_info_;
 }
 
+net::NetworkIsolationKey NavigationRequest::GetNetworkIsolationKey() {
+  if (network_isolation_key_)
+    return network_isolation_key_.value();
+
+  // If this is a top-frame navigation, then use the origin of the url (and
+  // update it as redirects happen). If this is a subframe navigation, get the
+  // URL from the top frame.
+  // TODO(crbug.com/979296): Consider changing this code to copy an origin
+  // instead of creating one from a URL which lacks opacity information.
+  url::Origin frame_origin = url::Origin::Create(common_params_->url);
+  url::Origin top_frame_origin =
+      frame_tree_node_->IsMainFrame()
+          ? frame_origin
+          : frame_tree_node_->frame_tree()->root()->current_origin();
+
+  return net::NetworkIsolationKey(top_frame_origin, frame_origin);
+}
+
 bool NavigationRequest::HasSubframeNavigationEntryCommitted() {
   DCHECK(!frame_tree_node_->IsMainFrame());
   DCHECK(handle_state_ == DID_COMMIT || handle_state_ == DID_COMMIT_ERROR_PAGE);
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 281aeacf..3a19e57 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -227,6 +227,7 @@
   net::HttpResponseInfo::ConnectionInfo GetConnectionInfo() override;
   const base::Optional<net::SSLInfo>& GetSSLInfo() override;
   const base::Optional<net::AuthChallengeInfo>& GetAuthChallengeInfo() override;
+  net::NetworkIsolationKey GetNetworkIsolationKey() override;
   void RegisterThrottleForTesting(
       std::unique_ptr<NavigationThrottle> navigation_throttle) override;
   bool IsDeferredForTesting() override;
@@ -747,11 +748,6 @@
   // Note: |site_url_| should only be updated with the result of this function.
   GURL GetSiteForCommonParamsURL() const;
 
-  // Returns the network isolation key for this NavigationRequest. If
-  // |network_isolation_key_| is empty, the key will be based off request url
-  // origin and top frame origin.
-  net::NetworkIsolationKey GetNetworkIsolationKey() const;
-
   // Updates the state of the navigation handle after encountering a server
   // redirect.
   void UpdateStateFollowingRedirect(const GURL& new_referrer_url,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 120b424..3c62948 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1269,7 +1269,7 @@
       FROM_HERE, evict_after,
       base::BindOnce(&RenderFrameHostImpl::EvictFromBackForwardCacheWithReason,
                      weak_ptr_factory_.GetWeakPtr(),
-                     BackForwardCacheMetrics::EvictedReason::kTimeout));
+                     BackForwardCacheMetrics::NotRestoredReason::kTimeout));
 }
 
 void RenderFrameHostImpl::DisallowBackForwardCache() {
@@ -1281,7 +1281,7 @@
 void RenderFrameHostImpl::OnGrantedMediaStreamAccess() {
   was_granted_media_access_ = true;
   MaybeEvictFromBackForwardCache(
-      BackForwardCacheMetrics::EvictedReason::kGrantedMediaStreamAccess);
+      BackForwardCacheMetrics::NotRestoredReason::kGrantedMediaStreamAccess);
 }
 
 void RenderFrameHostImpl::OnPortalActivated(
@@ -1882,8 +1882,10 @@
   if (is_in_back_forward_cache_) {
     EvictFromBackForwardCacheWithReason(
         info.status == base::TERMINATION_STATUS_PROCESS_CRASHED
-            ? BackForwardCacheMetrics::EvictedReason::kRendererProcessCrashed
-            : BackForwardCacheMetrics::EvictedReason::kRendererProcessKilled);
+            ? BackForwardCacheMetrics::NotRestoredReason::
+                  kRendererProcessCrashed
+            : BackForwardCacheMetrics::NotRestoredReason::
+                  kRendererProcessKilled);
     return;
   }
 
@@ -2555,7 +2557,7 @@
   renderer_reported_scheduler_tracked_features_ = features_mask;
 
   MaybeEvictFromBackForwardCache(
-      BackForwardCacheMetrics::EvictedReason::kSchedulerTrackedFeatureUsed);
+      BackForwardCacheMetrics::NotRestoredReason::kSchedulerTrackedFeatureUsed);
 }
 
 void RenderFrameHostImpl::OnSchedulerTrackedFeatureUsed(
@@ -2565,7 +2567,7 @@
       1 << static_cast<uint64_t>(feature);
 
   MaybeEvictFromBackForwardCache(
-      BackForwardCacheMetrics::EvictedReason::kSchedulerTrackedFeatureUsed);
+      BackForwardCacheMetrics::NotRestoredReason::kSchedulerTrackedFeatureUsed);
 }
 
 bool RenderFrameHostImpl::IsFrozen() {
@@ -3115,7 +3117,7 @@
   // If a dialog tries to show for a document in the BackForwardCache, evict it.
   if (is_in_back_forward_cache_) {
     EvictFromBackForwardCacheWithReason(
-        BackForwardCacheMetrics::EvictedReason::kDialog);
+        BackForwardCacheMetrics::NotRestoredReason::kDialog);
     return;
   }
 
@@ -3701,11 +3703,11 @@
   // TODO(hajimehoshi): This function should take the reason from the renderer
   // side.
   EvictFromBackForwardCacheWithReason(
-      BackForwardCacheMetrics::EvictedReason::kJavaScriptExecution);
+      BackForwardCacheMetrics::NotRestoredReason::kJavaScriptExecution);
 }
 
 void RenderFrameHostImpl::EvictFromBackForwardCacheWithReason(
-    base::Optional<BackForwardCacheMetrics::EvictedReason> reason) {
+    base::Optional<BackForwardCacheMetrics::NotRestoredReason> reason) {
   DCHECK(IsBackForwardCacheEnabled());
 
   if (is_evicted_from_back_forward_cache_)
@@ -3723,7 +3725,7 @@
   // |is_in_back_forward_cache()| is false.
   BackForwardCacheMetrics* metrics = top_document->GetBackForwardCacheMetrics();
   if (is_in_back_forward_cache() && reason && metrics)
-    metrics->MarkEvictedFromBackForwardCacheWithReason(reason.value());
+    metrics->MarkNotRestoredWithReason(reason.value());
 
   if (!in_back_forward_cache) {
     BackForwardCacheMetrics::RecordEvictedAfterDocumentRestored(
@@ -7890,7 +7892,7 @@
 }
 
 void RenderFrameHostImpl::MaybeEvictFromBackForwardCache(
-    BackForwardCacheMetrics::EvictedReason reason) {
+    BackForwardCacheMetrics::NotRestoredReason reason) {
   if (!is_in_back_forward_cache_)
     return;
 
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index a46428f..54c01d4 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -323,7 +323,7 @@
       const AXEventNotificationDetails& details);
 
   void EvictFromBackForwardCacheWithReason(
-      base::Optional<BackForwardCacheMetrics::EvictedReason> reason);
+      base::Optional<BackForwardCacheMetrics::NotRestoredReason> reason);
 
   // IPC::Sender
   bool Send(IPC::Message* msg) override;
@@ -1928,7 +1928,7 @@
   // Evicts the document from the BackForwardCache if it is in the cache,
   // and ineligible for caching.
   void MaybeEvictFromBackForwardCache(
-      BackForwardCacheMetrics::EvictedReason reason);
+      BackForwardCacheMetrics::NotRestoredReason reason);
 
   // Helper for handling download-related IPCs.
   void DownloadUrl(
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 7c71c3d..7665f43d 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -474,6 +474,13 @@
   // RenderFrameHost should not be trying to commit a navigation.
   old_render_frame_host->ResetNavigationRequests();
 
+  NavigationEntryImpl* last_committed_entry =
+      delegate_->GetControllerForRenderManager().GetLastCommittedEntry();
+  BackForwardCacheMetrics* old_page_back_forward_cache_metrics =
+      (!old_render_frame_host->GetParent() && last_committed_entry)
+          ? last_committed_entry->back_forward_cache_metrics()
+          : nullptr;
+
   // Record the metrics about the state of the old main frame at the moment when
   // we navigate away from it as it matters for whether the page is eligible for
   // being put into back-forward cache.
@@ -484,16 +491,9 @@
   //
   // TODO(altimin, crbug.com/933147): Remove this logic after we are done with
   // implementing back-forward cache.
-  if (!old_render_frame_host->GetParent()) {
-    if (NavigationEntryImpl* last_committed_entry =
-            delegate_->GetControllerForRenderManager()
-                .GetLastCommittedEntry()) {
-      if (last_committed_entry->back_forward_cache_metrics()) {
-        last_committed_entry->back_forward_cache_metrics()->RecordFeatureUsage(
-            old_render_frame_host.get());
-      }
-    }
-  }
+  if (old_page_back_forward_cache_metrics)
+    old_page_back_forward_cache_metrics->RecordFeatureUsage(
+        old_render_frame_host.get());
 
   // BackForwardCache:
   //
@@ -543,6 +543,14 @@
       back_forward_cache.StoreEntry(std::move(entry));
       return;
     }
+
+    if (old_page_back_forward_cache_metrics) {
+      // TODO(hajimehoshi): For code readability, BackForwardCacheMetrics should
+      // take CanStoreDocumentResult directly and rename
+      // MarkNotRestoredWithReason to DidNotStoreDocument.
+      old_page_back_forward_cache_metrics->MarkNotRestoredWithReason(
+          can_store.reason.value());
+    }
   }
 
   // Create a replacement proxy for the old RenderFrameHost. (There should not
diff --git a/content/browser/interface_provider_filtering.cc b/content/browser/interface_provider_filtering.cc
index 0eb8a8f..f6f7aa4 100644
--- a/content/browser/interface_provider_filtering.cc
+++ b/content/browser/interface_provider_filtering.cc
@@ -25,7 +25,7 @@
     service_manager::mojom::InterfaceProviderRequest request,
     service_manager::mojom::InterfaceProviderPtr provider) {
   RenderProcessHost* process = RenderProcessHost::FromID(process_id);
-  if (!process)
+  if (!process || !process->IsInitializedAndNotDead())
     return;
 
   service_manager::Connector* connector =
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 311756e..68c4f29 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3933,6 +3933,41 @@
 }
 
 // static
+RenderProcessHost* RenderProcessHostImpl::GetUnusedProcessHostForServiceWorker(
+    SiteInstanceImpl* site_instance) {
+  DCHECK(site_instance->is_for_service_worker());
+  if (site_instance->process_reuse_policy() !=
+      SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE) {
+    return nullptr;
+  }
+
+  auto& spare_process_manager = SpareRenderProcessHostManager::GetInstance();
+  iterator iter(AllHostsIterator());
+  while (!iter.IsAtEnd()) {
+    auto* host = iter.GetCurrentValue();
+    // This function tries to choose the process that will be chosen by a
+    // navigation that will use the service worker that is being started. Prefer
+    // to not take the spare process host, since if the navigation is out of the
+    // New Tab Page on Android, it will be using the existing NTP process
+    // instead of the spare process. If this function doesn't find a suitable
+    // process, the spare can still be chosen when
+    // MaybeTakeSpareRenderProcessHost() is called later.
+    bool is_spare = (host == spare_process_manager.spare_render_process_host());
+
+    if (!is_spare && iter.GetCurrentValue()->MayReuseHost() &&
+        iter.GetCurrentValue()->IsUnused() &&
+        RenderProcessHostImpl::IsSuitableHost(
+            iter.GetCurrentValue(), site_instance->GetBrowserContext(),
+            site_instance->GetIsolationContext(), site_instance->GetSiteURL(),
+            site_instance->lock_url())) {
+      return host;
+    }
+    iter.Advance();
+  }
+  return nullptr;
+}
+
+// static
 bool RenderProcessHost::ShouldUseProcessPerSite(BrowserContext* browser_context,
                                                 const GURL& url) {
   // Returns true if we should use the process-per-site model.  This will be
@@ -4064,6 +4099,13 @@
                features::kProcessSharingWithStrictSiteInstances));
   }
 
+  // If a process hasn't been selected yet, and the site instance is for a
+  // service worker, try to use an unused process host. One might have been
+  // created for a navigation and this will let the navigation and the service
+  // worker share the same process.
+  if (!render_process_host && is_unmatched_service_worker)
+    render_process_host = GetUnusedProcessHostForServiceWorker(site_instance);
+
   // See if the spare RenderProcessHost can be used.
   auto& spare_process_manager = SpareRenderProcessHostManager::GetInstance();
   bool spare_was_taken = false;
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index b76dc4a1..4c19196 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -715,6 +715,22 @@
   FRIEND_TEST_ALL_PREFIXES(RenderProcessHostUnitTest,
                            GuestsAreNotSuitableHosts);
 
+  // Returns an existing RenderProcessHost that has not yet been used and is
+  // suitable for the given |site_instance|, or null if no such process host
+  // exists.
+  //
+  // This function is used when finding a process for a service worker. The
+  // idea is to choose the process that will be chosen by a navigation that will
+  // use the service worker. While navigations typically try to choose the
+  // process with the relevant service worker (using
+  // UnmatchedServiceWorkerProcessTracker), navigations out of the Android New
+  // Tab Page use a SiteInstance with an empty URL by design in order to choose
+  // the NTP process, and do not go through the typical matching algorithm. The
+  // goal of this function is to return the NTP process so the service worker
+  // can also use it.
+  static RenderProcessHost* GetUnusedProcessHostForServiceWorker(
+      SiteInstanceImpl* site_instance);
+
   // Returns a RenderProcessHost that is rendering a URL corresponding to
   // |site_instance| in one of its frames, or that is expecting a navigation to
   // that SiteInstance.
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 45a587b8..0cdd7417 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -847,11 +847,11 @@
   // 7. Browser:    parent RenderWidgetHost (We're here if |is_child_frame|.)
   // 8. IPC           -> WidgetMsg_UpdateVisualProperties
   // 9. Renderer B: child  RenderWidget
-  visual_properties.page_scale_factor =
-      properties_from_parent_frame_.page_scale_factor;
-  visual_properties.is_pinch_gesture_active =
-      properties_from_parent_frame_.is_pinch_gesture_active;
   if (is_child_frame) {
+    visual_properties.page_scale_factor =
+        properties_from_parent_frame_.page_scale_factor;
+    visual_properties.is_pinch_gesture_active =
+        properties_from_parent_frame_.is_pinch_gesture_active;
     visual_properties.compositor_viewport_pixel_rect =
         properties_from_parent_frame_.compositor_viewport;
 
diff --git a/content/browser/service_worker/service_worker_process_browsertest.cc b/content/browser/service_worker/service_worker_process_browsertest.cc
new file mode 100644
index 0000000..502fbe8
--- /dev/null
+++ b/content/browser/service_worker/service_worker_process_browsertest.cc
@@ -0,0 +1,205 @@
+// Copyright 2019 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 "base/command_line.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/test_content_browser_client.h"
+#include "net/dns/mock_host_resolver.h"
+
+// This file has tests involving render process selection for service workers.
+
+namespace content {
+
+class ServiceWorkerProcessBrowserTest
+    : public ContentBrowserTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  ServiceWorkerProcessBrowserTest() = default;
+  ~ServiceWorkerProcessBrowserTest() override = default;
+
+  ServiceWorkerProcessBrowserTest(const ServiceWorkerProcessBrowserTest&) =
+      delete;
+  ServiceWorkerProcessBrowserTest& operator=(
+      const ServiceWorkerProcessBrowserTest&) = delete;
+
+  void SetUpOnMainThread() override {
+    // Support multiple sites on the test server.
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
+        shell()->web_contents()->GetBrowserContext());
+    wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
+        partition->GetServiceWorkerContext());
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    if (SitePerProcess())
+      command_line->AppendSwitch(switches::kSitePerProcess);
+  }
+
+ protected:
+  bool SitePerProcess() const { return GetParam(); }
+
+  // Registers a service worker and then tears down the process it used, for a
+  // clean slate going forward.
+  void RegisterServiceWorker() {
+    // Load a page that registers a service worker.
+    Shell* start_shell = CreateBrowser();
+    ASSERT_TRUE(NavigateToURL(
+        start_shell, embedded_test_server()->GetURL(
+                         "/service_worker/create_service_worker.html")));
+    ASSERT_EQ("DONE",
+              EvalJs(start_shell, "register('fetch_event_pass_through.js');"));
+
+    auto* host = RenderProcessHost::FromID(GetServiceWorkerProcessId());
+    ASSERT_TRUE(host);
+    RenderProcessHostWatcher exit_watcher(
+        host, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+
+    // Tear down the page.
+    start_shell->Close();
+
+    // Stop the service worker. The process should exit.
+    base::RunLoop loop;
+    wrapper()->StopAllServiceWorkers(loop.QuitClosure());
+    loop.Run();
+    exit_watcher.Wait();
+  }
+
+  // Returns the number of running service workers.
+  size_t GetRunningServiceWorkerCount() {
+    return wrapper()->GetRunningServiceWorkerInfos().size();
+  }
+
+  // Returns the process id of the running service worker. There must be exactly
+  // one service worker running.
+  int GetServiceWorkerProcessId() {
+    const base::flat_map<int64_t, ServiceWorkerRunningInfo>& infos =
+        wrapper()->GetRunningServiceWorkerInfos();
+    DCHECK_EQ(infos.size(), 1u);
+    const ServiceWorkerRunningInfo& info = infos.begin()->second;
+    return info.render_process_id;
+  }
+
+  ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
+
+  WebContentsImpl* web_contents() {
+    return static_cast<WebContentsImpl*>(shell()->web_contents());
+  }
+
+  RenderFrameHostImpl* current_frame_host() {
+    return web_contents()->GetFrameTree()->root()->current_frame_host();
+  }
+
+ private:
+  scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
+};
+
+// Tests that a service worker started due to a navigation shares the same
+// process as the navigation.
+IN_PROC_BROWSER_TEST_P(ServiceWorkerProcessBrowserTest,
+                       ServiceWorkerAndPageShareProcess) {
+  // Register the service worker.
+  RegisterServiceWorker();
+
+  // Navigate to a page in the service worker's scope.
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("/service_worker/empty.html")));
+
+  // The page and service worker should be in the same process.
+  int page_process_id = current_frame_host()->GetProcess()->GetID();
+  EXPECT_NE(page_process_id, ChildProcessHost::kInvalidUniqueID);
+  ASSERT_EQ(GetRunningServiceWorkerCount(), 1u);
+  int worker_process_id = GetServiceWorkerProcessId();
+  EXPECT_EQ(page_process_id, worker_process_id);
+}
+
+// ContentBrowserClient that skips assigning a site URL for a given URL.
+class DontAssignSiteContentBrowserClient : public TestContentBrowserClient {
+ public:
+  // Any visit to |url_to_skip| will not cause the site to be assigned to the
+  // SiteInstance.
+  explicit DontAssignSiteContentBrowserClient(const GURL& url_to_skip)
+      : url_to_skip_(url_to_skip) {}
+
+  DontAssignSiteContentBrowserClient(
+      const DontAssignSiteContentBrowserClient&) = delete;
+  DontAssignSiteContentBrowserClient& operator=(
+      const DontAssignSiteContentBrowserClient&) = delete;
+
+  bool ShouldAssignSiteForURL(const GURL& url) override {
+    return url == url_to_skip_;
+  }
+
+ private:
+  GURL url_to_skip_;
+};
+
+// Tests that a service worker and navigation share the same process in the
+// special case where the service worker starts before the navigation starts,
+// and the navigation transitions out of a page with no site URL. This special
+// case happens in real life when doing a search from the omnibox while on the
+// Android native NTP page: the service worker starts first due to the
+// navigation hint from the omnibox, and the native page has no site URL. See
+// https://crbug.com/1012143.
+IN_PROC_BROWSER_TEST_P(
+    ServiceWorkerProcessBrowserTest,
+    ServiceWorkerAndPageShareProcess_NavigateFromUnassignedSiteInstance) {
+  // Set up a page URL that will have no site URL.
+  GURL empty_site = embedded_test_server()->GetURL("a.com", "/title1.html");
+  DontAssignSiteContentBrowserClient content_browser_client(empty_site);
+  ContentBrowserClient* old_client =
+      SetBrowserClientForTesting(&content_browser_client);
+
+  // Register the service worker.
+  RegisterServiceWorker();
+
+  // Navigate to the empty site instance page. Subsequent navigations from
+  // this page will prefer to use the same process by default.
+  ASSERT_TRUE(NavigateToURL(shell(), empty_site));
+
+  // Start the service worker. It will start in a new process.
+  base::RunLoop loop;
+  GURL scope = embedded_test_server()->GetURL("/service_worker/");
+  wrapper()->StartWorkerForScope(
+      scope,
+      base::BindLambdaForTesting([&loop](int64_t version_id, int process_id,
+                                         int thread_id) { loop.Quit(); }),
+      base::BindLambdaForTesting([&loop]() {
+        ASSERT_FALSE(true) << "start worker failed";
+        loop.Quit();
+      }));
+  loop.Run();
+
+  // Navigate to a page in the service worker's scope.
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("/service_worker/empty.html")));
+
+  // The page and service worker should be in the same process.
+  ASSERT_EQ(GetRunningServiceWorkerCount(), 1u);
+  int page_process_id = current_frame_host()->GetProcess()->GetID();
+  EXPECT_NE(page_process_id, ChildProcessHost::kInvalidUniqueID);
+  EXPECT_EQ(page_process_id, GetServiceWorkerProcessId());
+
+  SetBrowserClientForTesting(old_client);
+}
+
+// Toggle Site Isolation.
+INSTANTIATE_TEST_SUITE_P(, ServiceWorkerProcessBrowserTest, testing::Bool());
+
+}  // namespace content
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index fbec490b6..546acdf3 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -2555,17 +2555,15 @@
   SurfaceHitTestTestHelper(shell(), embedded_test_server());
 }
 
-// TODO(crbug/1014602): NestedSurfaceHitTestTest is flaky.
-
 // Test that mouse events are being routed to the correct RenderWidgetHostView
 // when there are nested out-of-process iframes.
 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
-                       DISABLED_NestedSurfaceHitTestTest) {
+                       NestedSurfaceHitTestTest) {
   NestedSurfaceHitTestTestHelper(shell(), embedded_test_server());
 }
 
 IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest,
-                       DISABLED_NestedSurfaceHitTestTest) {
+                       NestedSurfaceHitTestTest) {
   NestedSurfaceHitTestTestHelper(shell(), embedded_test_server());
 }
 
@@ -6282,7 +6280,7 @@
 }
 
 IN_PROC_BROWSER_TEST_P(SitePerProcessNonIntegerScaleFactorHitTestBrowserTest,
-                       DISABLED_NestedSurfaceHitTestTest) {
+                       NestedSurfaceHitTestTest) {
   NestedSurfaceHitTestTestHelper(shell(), embedded_test_server());
 }
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 21e4fc1..a615d6200 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -689,7 +689,8 @@
   // destroyed.
   RenderFrameHostManager* root = GetRenderManager();
 
-  GetController().GetBackForwardCache().Flush();
+  GetController().GetBackForwardCache().Shutdown();
+
   root->current_frame_host()->SetRenderFrameCreated(false);
   root->current_frame_host()->ResetNavigationRequests();
 
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index c59aeab..7662f7d 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -554,8 +554,8 @@
 }
 
 namespace {
-// A factory for creating DedicatedWorkerHosts. Its lifetime is managed by
-// the renderer over mojo via a StrongBinding. This lives on the UI thread.
+// A factory for creating DedicatedWorkerHosts. Its lifetime is managed by the
+// renderer over mojo via SelfOwnedReceiver. It lives on the UI thread.
 class DedicatedWorkerHostFactoryImpl final
     : public blink::mojom::DedicatedWorkerHostFactory {
  public:
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index e2bbb05..cbda5d5 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -17,6 +17,7 @@
 #include "net/base/auth.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/http/http_response_info.h"
 #include "services/network/public/cpp/resource_request_body.h"
 #include "ui/base/page_transition_types.h"
@@ -284,6 +285,12 @@
   virtual const base::Optional<net::AuthChallengeInfo>&
   GetAuthChallengeInfo() = 0;
 
+  // Gets the NetworkIsolationKey associated with the navigation. Updated as
+  // redirects are followed. When one of the origins used to construct the
+  // NetworkIsolationKey is opaque, the returned NetworkIsolationKey will not be
+  // consistent between calls.
+  virtual net::NetworkIsolationKey GetNetworkIsolationKey() = 0;
+
   // Returns the ID of the URLRequest associated with this navigation. Can only
   // be called from NavigationThrottle::WillProcessResponse and
   // WebContentsObserver::ReadyToCommitNavigation.
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index a82248cd..c69667c5 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -360,6 +360,7 @@
   //   4. Possibly more stpes, depending on the ChildThreadImpl subclass.
   virtual void BindReceiver(mojo::GenericPendingReceiver receiver) = 0;
 
+  // Can only be called when IsInitializedAndNotDead() is true.
   virtual const service_manager::Identity& GetChildIdentity() = 0;
 
   // Extracts any persistent-memory-allocator used for renderer metrics.
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index 31274f6..9ce6c85 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -11,6 +11,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "net/base/ip_endpoint.h"
+#include "net/base/network_isolation_key.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/mojom/referrer.mojom.h"
 #include "url/gurl.h"
@@ -89,6 +90,7 @@
       override {
     return auth_challenge_info_;
   }
+  MOCK_METHOD0(GetNetworkIsolationKey, net::NetworkIsolationKey());
   MOCK_METHOD0(GetGlobalRequestID, const GlobalRequestID&());
   MOCK_METHOD0(IsDownload, bool());
   bool IsFormSubmission() override { return is_form_submission_; }
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index ccb773f..dcaa305 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -13,12 +13,12 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/lazy_instance.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "content/child/child_thread_impl.h"
 #include "content/common/media/peer_connection_tracker_messages.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
 #include "content/renderer/render_thread_impl.h"
@@ -604,6 +604,24 @@
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
 };
 
+struct PeerConnectionTrackerLazyInstanceTraits
+    : public base::internal::DestructorAtExitLazyInstanceTraits<
+          PeerConnectionTracker> {
+  static PeerConnectionTracker* New(void* instance) {
+    return new (instance) PeerConnectionTracker(
+        RenderThreadImpl::current()->main_thread_runner());
+  }
+};
+
+base::LazyInstance<PeerConnectionTracker,
+                   PeerConnectionTrackerLazyInstanceTraits>
+    g_peer_connection_tracker = LAZY_INSTANCE_INITIALIZER;
+
+// static
+PeerConnectionTracker* PeerConnectionTracker::GetInstance() {
+  return &g_peer_connection_tracker.Get();
+}
+
 PeerConnectionTracker::PeerConnectionTracker(
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
     : next_local_id_(1),
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.h b/content/renderer/media/webrtc/peer_connection_tracker.h
index 3d81e5f1..29af0f4 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.h
+++ b/content/renderer/media/webrtc/peer_connection_tracker.h
@@ -45,6 +45,8 @@
     : public RenderThreadObserver,
       public base::SupportsWeakPtr<PeerConnectionTracker> {
  public:
+  static PeerConnectionTracker* GetInstance();
+
   explicit PeerConnectionTracker(
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
   PeerConnectionTracker(
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 799dcc81..c8f2fcd1 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -1015,8 +1015,9 @@
   CHECK(!initialize_called_);
   initialize_called_ = true;
 
-  peer_connection_tracker_ =
-      RenderThreadImpl::current()->peer_connection_tracker()->AsWeakPtr();
+  // TODO(crbug.com/787254): Evaluate the need for passing weak ptr since
+  // PeerConnectionTracker is now leaky with base::LazyInstance.
+  peer_connection_tracker_ = PeerConnectionTracker::GetInstance()->AsWeakPtr();
 
   configuration_ = server_configuration;
 
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index aaf5a4487..a77b6dd 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -753,9 +753,7 @@
   browser_plugin_manager_.reset(new BrowserPluginManager());
   AddObserver(browser_plugin_manager_.get());
 
-  peer_connection_tracker_.reset(
-      new PeerConnectionTracker(main_thread_runner()));
-  AddObserver(peer_connection_tracker_.get());
+  AddObserver(PeerConnectionTracker::GetInstance());
 
   unfreezable_message_filter_ = new UnfreezableMessageFilter(this);
   AddFilter(unfreezable_message_filter_.get());
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 16def76..b9ad30f 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -103,7 +103,6 @@
 class CategorizedWorkerPool;
 class GpuVideoAcceleratorFactoriesImpl;
 class LowMemoryModeController;
-class PeerConnectionTracker;
 class RenderThreadObserver;
 class RendererBlinkPlatformImpl;
 class ResourceDispatcher;
@@ -291,10 +290,6 @@
     return browser_plugin_manager_.get();
   }
 
-  PeerConnectionTracker* peer_connection_tracker() {
-    return peer_connection_tracker_.get();
-  }
-
   blink::WebVideoCaptureImplManager* video_capture_impl_manager() const {
     return vc_manager_.get();
   }
@@ -553,10 +548,6 @@
 
   std::unique_ptr<BrowserPluginManager> browser_plugin_manager_;
 
-  // This is used to communicate to the browser process the status
-  // of all the peer connections created in the renderer.
-  std::unique_ptr<PeerConnectionTracker> peer_connection_tracker_;
-
   // Filter out unfreezable messages and pass it to unfreezable task runners.
   scoped_refptr<UnfreezableMessageFilter> unfreezable_message_filter_;
 
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 541268de..0721224 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -699,8 +699,7 @@
 
 void RendererBlinkPlatformImpl::TrackGetUserMedia(
     const blink::WebUserMediaRequest& web_request) {
-  RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia(
-      web_request);
+  PeerConnectionTracker::GetInstance()->TrackGetUserMedia(web_request);
 }
 
 bool RendererBlinkPlatformImpl::IsWebRtcHWH264DecodingEnabled(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1d7fb6a..644231b1 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1031,6 +1031,7 @@
     "../browser/service_worker/service_worker_clients_api_browsertest.cc",
     "../browser/service_worker/service_worker_file_upload_browsertest.cc",
     "../browser/service_worker/service_worker_no_best_effort_tasks_browsertest.cc",
+    "../browser/service_worker/service_worker_process_browsertest.cc",
     "../browser/session_history_browsertest.cc",
     "../browser/shape_detection/shape_detection_browsertest.cc",
     "../browser/site_per_process_browsertest.cc",
diff --git a/docs/adding_to_third_party.md b/docs/adding_to_third_party.md
index 26b1c9fe..06be7f9 100644
--- a/docs/adding_to_third_party.md
+++ b/docs/adding_to_third_party.md
@@ -18,6 +18,15 @@
 other directory with third_party in the name it's okay to put new things
 there.
 
+## Before you start
+
+To make sure the inclusion of a new third_party project makes sense for the
+Chromium project, you should first obtain Chrome Eng Review approval.
+Googlers should see go/chrome-eng-review and review existing topics in
+g/chrome-eng-review. Please include information about the additional checkout
+size, build times, and binary sizes. Please also make sure that the motivation
+for your project is clear, e.g., a design doc has been circulated.
+
 ## Get the code
 
 There are two common ways to depend on third-party code: you can reference a
@@ -27,6 +36,14 @@
 of the repo or don't need to pick up every single change. And, of course, if
 the code you need isn't in a Git repo, you have to do the latter.
 
+### Node packages
+
+To include a Node package, add the dependency to the
+[Node package.json](../third_party/node/package.json). Make sure to update
+the corresponding [`npm_exclude.txt`](../third_party/node/npm_exclude.txt)
+and [`npm_include.txt`](../third_party/node/npm_include.txt) to make the code
+available during checkout.
+
 ### Pulling the code via DEPS
 
 If the code is in a Git repo that you want to mirror, please file an [infra git
@@ -126,10 +143,6 @@
 Non-Googlers can email one of the people in
 [//third_party/OWNERS](../third_party/OWNERS) for help.
 
-* Get Chrome Eng Review approval. Googlers should see
-  go/chrome-eng-review. Please include information about the additional
-  checkout size, build times, and binary sizes. Please also make sure that the
-  motivation for your project is clear, e.g., a design doc has been circulated.
 * Get security@chromium.org approval. Email the list with relevant details and
   a link to the CL. Third party code is a hot spot for security vulnerabilities.
   When adding a new package that could potentially carry security risk, make
diff --git a/docs/process/merge_request.md b/docs/process/merge_request.md
index 9ee67df5c..0905103 100644
--- a/docs/process/merge_request.md
+++ b/docs/process/merge_request.md
@@ -86,6 +86,10 @@
 Security bugs should be consulted with [chrome-security@](chrome-security@google.com)
 to determine criticality.
 
+If it is unclear whether the severity of the issue meets the bar for merging
+consult with the [TPM](https://chromiumdash.appspot.com/schedule) and your
+manager.
+
 This table below provides key dates and phases as an example, for M61 release.
 
 Key Event  | Date
diff --git a/extensions/browser/api/automation_internal/automation_internal_api.cc b/extensions/browser/api/automation_internal/automation_internal_api.cc
index f6de53d..5a60dae2 100644
--- a/extensions/browser/api/automation_internal/automation_internal_api.cc
+++ b/extensions/browser/api/automation_internal/automation_internal_api.cc
@@ -391,6 +391,10 @@
       break;
     case api::automation::ACTION_TYPE_SCROLLTOMAKEVISIBLE:
       action->action = ax::mojom::Action::kScrollToMakeVisible;
+      action->horizontal_scroll_alignment =
+          ax::mojom::ScrollAlignment::kScrollAlignmentCenter;
+      action->vertical_scroll_alignment =
+          ax::mojom::ScrollAlignment::kScrollAlignmentCenter;
       break;
     case api::automation::ACTION_TYPE_SCROLLBACKWARD:
       action->action = ax::mojom::Action::kScrollBackward;
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index a149629c..5a9afb9 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1457,6 +1457,7 @@
   AUTOTESTPRIVATE_GETAPPWINDOWLIST = 1394,
   AUTOTESTPRIVATE_SETAPPWINDOWSTATE = 1395,
   AUTOTESTPRIVATE_CLOSEAPPWINDOW = 1396,
+  AUTOTESTPRIVATE_REFRESHENTERPRISEPOLICIES = 1397,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 39323b1e..46bf69e 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -133,6 +133,7 @@
     "main_application_delegate_testing.h",
     "main_controller.h",
     "main_controller.mm",
+    "main_controller_guts.h",
     "main_controller_private.h",
     "memory_monitor.h",
     "memory_monitor.mm",
diff --git a/ios/chrome/app/main_controller.h b/ios/chrome/app/main_controller.h
index d300dc6..f1194444 100644
--- a/ios/chrome/app/main_controller.h
+++ b/ios/chrome/app/main_controller.h
@@ -12,6 +12,7 @@
 #import "ios/chrome/app/application_delegate/startup_information.h"
 #import "ios/chrome/app/application_delegate/tab_opening.h"
 #import "ios/chrome/app/application_delegate/tab_switching.h"
+#import "ios/chrome/app/main_controller_guts.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 
 @class AppState;
@@ -23,12 +24,13 @@
 //
 // By design, it has no public API of its own. Anything interacting with
 // MainController should be doing so through a specific protocol.
-@interface MainController : NSObject<ApplicationCommands,
-                                     AppNavigation,
-                                     BrowserLauncher,
-                                     StartupInformation,
-                                     TabOpening,
-                                     TabSwitching>
+@interface MainController : NSObject <ApplicationCommands,
+                                      AppNavigation,
+                                      BrowserLauncher,
+                                      MainControllerGuts,
+                                      StartupInformation,
+                                      TabOpening,
+                                      TabSwitching>
 
 // The application window.
 @property(nonatomic, strong) UIWindow* window;
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index c3f3ceaf..ad4a3c3 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -330,9 +330,6 @@
   // The object that drives the Chrome startup/shutdown logic.
   std::unique_ptr<IOSChromeMain> _chromeMain;
 
-  // The ChromeBrowserState associated with the main (non-OTR) browsing mode.
-  ios::ChromeBrowserState* _mainBrowserState;  // Weak.
-
   // Wrangler to handle BVC and tab model creation, access, and related logic.
   // Implements faetures exposed from this object through the
   // BrowserViewInformation protocol.
@@ -372,18 +369,6 @@
   // in a modal dialog.
   BOOL _isPresentingFirstRunUI;
 
-  // The tab switcher command and the voice search commands can be sent by views
-  // that reside in a different UIWindow leading to the fact that the exclusive
-  // touch property will be ineffective and a command for processing both
-  // commands may be sent in the same run of the runloop leading to
-  // inconsistencies. Those two boolean indicate if one of those commands have
-  // been processed in the last 200ms in order to only allow processing one at
-  // a time.
-  // TODO(crbug.com/560296):  Provide a general solution for handling mutually
-  // exclusive chrome commands sent at nearly the same time.
-  BOOL _isProcessingTabSwitcherCommand;
-  BOOL _isProcessingVoiceSearchCommand;
-
   // Bridge to listen to pref changes.
   std::unique_ptr<PrefObserverBridge> _localStatePrefObserverBridge;
 
@@ -406,23 +391,20 @@
   // Cached launchOptions from -didFinishLaunchingWithOptions.
   NSDictionary* _launchOptions;
 
-  // Coordinator for displaying history.
-  HistoryCoordinator* _historyCoordinator;
-
   // Variable backing metricsMediator property.
   __weak MetricsMediator* _metricsMediator;
 
   // Hander for the startup tasks, deferred or not.
   StartupTasks* _startupTasks;
 
-  // The application level component for url loading. Is passed down to
-  // browser state level UrlLoadingService instances.
-  AppUrlLoadingService* _appURLLoadingService;
-
   // If the animations were disabled.
   BOOL _animationDisabled;
 }
 
+// The ChromeBrowserState associated with the main (non-OTR) browsing mode.
+@property(nonatomic, assign)
+    ios::ChromeBrowserState* mainBrowserState;  // Weak.
+
 // The main coordinator, lazily created the first time it is accessed. Manages
 // the main view controller. This property should not be accessed before the
 // browser has started up to the FOREGROUND stage.
@@ -434,12 +416,6 @@
 @property(nonatomic, readwrite)
     NTPTabOpeningPostOpeningAction NTPActionAfterTabSwitcherDismissal;
 
-// The SigninInteractionCoordinator to present Sign In UI. It is created the
-// first time Sign In UI is needed to be presented and should not be destroyed
-// while the UI is presented.
-@property(nonatomic, strong)
-    SigninInteractionCoordinator* signinInteractionCoordinator;
-
 // Returns YES if the settings are presented, either from
 // _settingsNavigationController or from SigninInteractionCoordinator.
 @property(nonatomic, assign, readonly, getter=isSettingsViewPresented)
@@ -573,10 +549,15 @@
 @end
 
 @implementation MainController
+// Defined by MainControllerGuts.
+@synthesize historyCoordinator;
+@synthesize settingsNavigationController;
+@synthesize appURLLoadingService;
+@synthesize isProcessingTabSwitcherCommand;
+@synthesize isProcessingVoiceSearchCommand;
+@synthesize signinInteractionCoordinator;
 
 // Defined by public protocols.
-// - AppNavigation
-@synthesize settingsNavigationController = _settingsNavigationController;
 // - BrowserLauncher
 @synthesize launchOptions = _launchOptions;
 @synthesize browserInitializationStage = _browserInitializationStage;
@@ -589,7 +570,6 @@
 @synthesize mainCoordinator = _mainCoordinator;
 @synthesize NTPActionAfterTabSwitcherDismissal =
     _NTPActionAfterTabSwitcherDismissal;
-@synthesize signinInteractionCoordinator = _signinInteractionCoordinator;
 
 #pragma mark - Application lifecycle
 
@@ -721,29 +701,29 @@
     [_restoreHelper moveAsideSessionInformation];
   }
 
-  _appURLLoadingService = new AppUrlLoadingService();
-  _appURLLoadingService->SetDelegate(self);
+  self.appURLLoadingService = new AppUrlLoadingService();
+  self.appURLLoadingService->SetDelegate(self);
 
   // Initialize and set the main browser state.
   [self initializeBrowserState:chromeBrowserState];
-  _mainBrowserState = chromeBrowserState;
+  self.mainBrowserState = chromeBrowserState;
   [_browserViewWrangler shutdown];
-  _browserViewWrangler =
-      [[BrowserViewWrangler alloc] initWithBrowserState:_mainBrowserState
-                                   webStateListObserver:self
-                             applicationCommandEndpoint:self
-                                   appURLLoadingService:_appURLLoadingService
-                                        storageSwitcher:self];
+  _browserViewWrangler = [[BrowserViewWrangler alloc]
+            initWithBrowserState:self.mainBrowserState
+            webStateListObserver:self
+      applicationCommandEndpoint:self
+            appURLLoadingService:self.appURLLoadingService
+                 storageSwitcher:self];
 
   // Force an obvious initialization of the AuthenticationService. This must
   // be done before creation of the UI to ensure the service is initialised
   // before use (it is a security issue, so accessing the service CHECK if
   // this is not the case).
-  DCHECK(_mainBrowserState);
+  DCHECK(self.mainBrowserState);
   AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
-      _mainBrowserState,
+      self.mainBrowserState,
       std::make_unique<MainControllerAuthenticationServiceDelegate>(
-          _mainBrowserState, self));
+          self.mainBrowserState, self));
 
   // Send "Chrome Opened" event to the feature_engagement::Tracker on cold
   // start.
@@ -757,10 +737,10 @@
   [_browserViewWrangler createMainBrowser];
 
   _spotlightManager =
-      [SpotlightManager spotlightManagerWithBrowserState:_mainBrowserState];
+      [SpotlightManager spotlightManagerWithBrowserState:self.mainBrowserState];
 
   ShareExtensionService* service =
-      ShareExtensionServiceFactory::GetForBrowserState(_mainBrowserState);
+      ShareExtensionServiceFactory::GetForBrowserState(self.mainBrowserState);
   service->Initialize();
 
   // Before bringing up the UI, make sure the launch mode is correct, and
@@ -791,9 +771,9 @@
                            interfaceProvider:self.interfaceProvider];
   if (self.isColdStart) {
     [ContentSuggestionsSchedulerNotifications
-        notifyColdStart:_mainBrowserState];
+        notifyColdStart:self.mainBrowserState];
     [ContentSuggestionsSchedulerNotifications
-        notifyForeground:_mainBrowserState];
+        notifyForeground:self.mainBrowserState];
   }
 
   ios::GetChromeBrowserProvider()->GetOverridesProvider()->InstallOverrides();
@@ -868,9 +848,9 @@
 }
 
 - (void)clearIOSSpecificIncognitoData {
-  DCHECK(_mainBrowserState->HasOffTheRecordChromeBrowserState());
+  DCHECK(self.mainBrowserState->HasOffTheRecordChromeBrowserState());
   ios::ChromeBrowserState* otrBrowserState =
-      _mainBrowserState->GetOffTheRecordChromeBrowserState();
+      self.mainBrowserState->GetOffTheRecordChromeBrowserState();
   [self removeBrowsingDataForBrowserState:otrBrowserState
                                timePeriod:browsing_data::TimePeriod::ALL_TIME
                                removeMask:BrowsingDataRemoveMask::REMOVE_ALL
@@ -988,8 +968,8 @@
 
   _extensionSearchEngineDataUpdater = nullptr;
 
-  [_historyCoordinator stop];
-  _historyCoordinator = nil;
+  [self.historyCoordinator stop];
+  self.historyCoordinator = nil;
 
   ios::GetChromeBrowserProvider()
       ->GetMailtoHandlerProvider()
@@ -1096,7 +1076,7 @@
                     // Track changes to default search engine.
                     TemplateURLService* service =
                         ios::TemplateURLServiceFactory::GetForBrowserState(
-                            _mainBrowserState);
+                            self.mainBrowserState);
                     _extensionSearchEngineDataUpdater =
                         std::make_unique<ExtensionSearchEngineDataUpdater>(
                             service);
@@ -1108,7 +1088,7 @@
       enqueueBlockNamed:kSendInstallPingIfNecessary
                   block:^{
                     auto URLLoaderFactory =
-                        _mainBrowserState->GetSharedURLLoaderFactory();
+                        self.mainBrowserState->GetSharedURLLoaderFactory();
                     bool is_first_run = FirstRun::IsChromeFirstRun();
                     ios::GetChromeBrowserProvider()
                         ->GetAppDistributionProvider()
@@ -1168,10 +1148,10 @@
   // ClearSessionCookies() is not synchronous.
   if (cookie_util::ShouldClearSessionCookies()) {
     cookie_util::ClearSessionCookies(
-        _mainBrowserState->GetOriginalChromeBrowserState());
+        self.mainBrowserState->GetOriginalChromeBrowserState());
     if (![self.otrTabModel isEmpty]) {
       cookie_util::ClearSessionCookies(
-          _mainBrowserState->GetOffTheRecordChromeBrowserState());
+          self.mainBrowserState->GetOffTheRecordChromeBrowserState());
     }
   }
 
@@ -1201,12 +1181,12 @@
       enqueueBlockNamed:kMailtoHandlingInitialization
                   block:^{
                     __strong __typeof(weakSelf) strongSelf = weakSelf;
-                    if (!strongSelf || !strongSelf->_mainBrowserState) {
+                    if (!strongSelf || !strongSelf.mainBrowserState) {
                       return;
                     }
                     ios::GetChromeBrowserProvider()
                         ->GetMailtoHandlerProvider()
-                        ->PrepareMailtoHandling(strongSelf->_mainBrowserState);
+                        ->PrepareMailtoHandling(strongSelf.mainBrowserState);
                   }];
 }
 
@@ -1276,7 +1256,8 @@
   // Deferred tasks.
   [self schedulePrefObserverInitialization];
   [self scheduleMemoryDebuggingTools];
-  [StartupTasks scheduleDeferredBrowserStateInitialization:_mainBrowserState];
+  [StartupTasks
+      scheduleDeferredBrowserStateInitialization:self.mainBrowserState];
   [self scheduleAuthenticationServiceNotification];
   [self sendQueuedFeedback];
   [self scheduleSpotlightResync];
@@ -1294,7 +1275,7 @@
   if (GetApplicationContext()->WasLastShutdownClean()) {
     // Delay the cleanup of the unreferenced files to not impact startup
     // performance.
-    ExternalFileRemoverFactory::GetForBrowserState(_mainBrowserState)
+    ExternalFileRemoverFactory::GetForBrowserState(self.mainBrowserState)
         ->RemoveAfterDelay(
             base::TimeDelta::FromSeconds(kExternalFilesCleanupDelaySeconds),
             base::OnceClosure());
@@ -1361,7 +1342,7 @@
 }
 
 - (void)createInitialUI:(ApplicationMode)launchMode {
-  DCHECK(_mainBrowserState);
+  DCHECK(self.mainBrowserState);
 
   // In order to correctly set the mode switch icon, we need to know how many
   // tabs are in the other tab model. That means loading both models.  They
@@ -1395,8 +1376,8 @@
 
   ios::ChromeBrowserState* browserState =
       (launchMode == ApplicationMode::INCOGNITO)
-          ? _mainBrowserState->GetOffTheRecordChromeBrowserState()
-          : _mainBrowserState;
+          ? self.mainBrowserState->GetOffTheRecordChromeBrowserState()
+          : self.mainBrowserState;
   [self changeStorageFromBrowserState:nullptr toBrowserState:browserState];
 
   TabModel* tabModel;
@@ -1444,7 +1425,7 @@
 
   WelcomeToChromeViewController* welcomeToChrome =
       [[WelcomeToChromeViewController alloc]
-          initWithBrowserState:_mainBrowserState
+          initWithBrowserState:self.mainBrowserState
                       tabModel:self.mainTabModel
                      presenter:self.mainBVC
                     dispatcher:self.mainBVC.dispatcher];
@@ -1486,9 +1467,9 @@
 
   // Show the sign-in promo if needed
   if ([SigninPromoViewController
-          shouldBePresentedForBrowserState:_mainBrowserState]) {
+          shouldBePresentedForBrowserState:self.mainBrowserState]) {
     UIViewController* promoController = [[SigninPromoViewController alloc]
-        initWithBrowserState:_mainBrowserState
+        initWithBrowserState:self.mainBrowserState
                   dispatcher:self.mainBVC.dispatcher];
 
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
@@ -1533,14 +1514,14 @@
 }
 
 - (void)showHistory {
-  _historyCoordinator =
-      [[HistoryCoordinator alloc] initWithBaseViewController:self.currentBVC
-                                                browserState:_mainBrowserState];
-  _historyCoordinator.loadStrategy = [self currentPageIsIncognito]
-                                         ? UrlLoadStrategy::ALWAYS_IN_INCOGNITO
-                                         : UrlLoadStrategy::NORMAL;
-  _historyCoordinator.dispatcher = self.mainBVC.dispatcher;
-  [_historyCoordinator start];
+  self.historyCoordinator = [[HistoryCoordinator alloc]
+      initWithBaseViewController:self.currentBVC
+                    browserState:self.mainBrowserState];
+  self.historyCoordinator.loadStrategy =
+      [self currentPageIsIncognito] ? UrlLoadStrategy::ALWAYS_IN_INCOGNITO
+                                    : UrlLoadStrategy::NORMAL;
+  self.historyCoordinator.dispatcher = self.mainBVC.dispatcher;
+  [self.historyCoordinator start];
 }
 
 - (void)closeSettingsUIAndOpenURL:(OpenNewTabCommand*)command {
@@ -1582,14 +1563,14 @@
 
 - (void)displayTabSwitcher {
   DCHECK(!_tabSwitcherIsActive);
-  if (!_isProcessingVoiceSearchCommand) {
+  if (!self.isProcessingVoiceSearchCommand) {
     [self.currentBVC userEnteredTabSwitcher];
     [self showTabSwitcher];
-    _isProcessingTabSwitcherCommand = YES;
+    self.isProcessingTabSwitcherCommand = YES;
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                  kExpectedTransitionDurationInNanoSeconds),
                    dispatch_get_main_queue(), ^{
-                     _isProcessingTabSwitcherCommand = NO;
+                     self.isProcessingTabSwitcherCommand = NO;
                    });
   }
 }
@@ -1642,7 +1623,7 @@
   params.from_chrome = command.fromChrome;
   params.user_initiated = command.userInitiated;
   params.should_focus_omnibox = command.shouldFocusOmnibox;
-  _appURLLoadingService->LoadUrlInNewTab(params);
+  self.appURLLoadingService->LoadUrlInNewTab(params);
 }
 
 // TODO(crbug.com/779791) : Do not pass |baseViewController| through dispatcher.
@@ -1650,7 +1631,7 @@
     baseViewController:(UIViewController*)baseViewController {
   if (!self.signinInteractionCoordinator) {
     self.signinInteractionCoordinator = [[SigninInteractionCoordinator alloc]
-        initWithBrowserState:_mainBrowserState
+        initWithBrowserState:self.mainBrowserState
                   dispatcher:self.mainBVC.dispatcher];
   }
 
@@ -1675,7 +1656,7 @@
 - (void)showAdvancedSigninSettingsFromViewController:
     (UIViewController*)baseViewController {
   self.signinInteractionCoordinator = [[SigninInteractionCoordinator alloc]
-      initWithBrowserState:_mainBrowserState
+      initWithBrowserState:self.mainBrowserState
                 dispatcher:self.mainBVC.dispatcher];
   [self.signinInteractionCoordinator
       showAdvancedSigninSettingsWithPresentingViewController:
@@ -1686,7 +1667,7 @@
 - (void)showAddAccountFromViewController:(UIViewController*)baseViewController {
   if (!self.signinInteractionCoordinator) {
     self.signinInteractionCoordinator = [[SigninInteractionCoordinator alloc]
-        initWithBrowserState:_mainBrowserState
+        initWithBrowserState:self.mainBrowserState
                   dispatcher:self.mainBVC.dispatcher];
   }
 
@@ -1959,7 +1940,7 @@
   // browser state.
   breakpad_helper::SetDestroyingAndRebuildingIncognitoBrowserState(
       /*in_progress=*/true);
-  DCHECK(_mainBrowserState->HasOffTheRecordChromeBrowserState());
+  DCHECK(self.mainBrowserState->HasOffTheRecordChromeBrowserState());
   [self clearIOSSpecificIncognitoData];
 
   // OffTheRecordProfileIOData cannot be deleted before all the requests are
@@ -2004,13 +1985,13 @@
 #pragma mark - Mode Switching
 
 - (void)startVoiceSearch {
-  if (!_isProcessingTabSwitcherCommand) {
+  if (!self.isProcessingTabSwitcherCommand) {
     [self startVoiceSearchInCurrentBVC];
-    _isProcessingVoiceSearchCommand = YES;
+    self.isProcessingVoiceSearchCommand = YES;
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                  kExpectedTransitionDurationInNanoSeconds),
                    dispatch_get_main_queue(), ^{
-                     _isProcessingVoiceSearchCommand = NO;
+                     self.isProcessingVoiceSearchCommand = NO;
                    });
   }
 }
@@ -2633,7 +2614,7 @@
 
   payments::IOSPaymentInstrumentLauncher* paymentAppLauncher =
       payments::IOSPaymentInstrumentLauncherFactory::GetInstance()
-          ->GetForBrowserState(_mainBrowserState);
+          ->GetForBrowserState(self.mainBrowserState);
 
   if (!paymentAppLauncher->delegate())
     return NO;
@@ -2710,6 +2691,12 @@
   return username.empty() ? nil : base::SysUTF8ToNSString(username);
 }
 
+#pragma mark - MainControllerGuts
+
+- (id<TabSwitcher>)tabSwitcher {
+  return _tabSwitcher;
+}
+
 @end
 
 #pragma mark - TestingOnly
@@ -2720,10 +2707,6 @@
   return [_browserViewWrangler deviceSharingManager];
 }
 
-- (id<TabSwitcher>)tabSwitcher {
-  return _tabSwitcher;
-}
-
 - (void)setTabSwitcher:(id<TabSwitcher>)switcher {
   _tabSwitcher = switcher;
 }
diff --git a/ios/chrome/app/main_controller_guts.h b/ios/chrome/app/main_controller_guts.h
new file mode 100644
index 0000000..67754969
--- /dev/null
+++ b/ios/chrome/app/main_controller_guts.h
@@ -0,0 +1,79 @@
+// Copyright 2019 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_APP_MAIN_CONTROLLER_GUTS_H_
+#define IOS_CHROME_APP_MAIN_CONTROLLER_GUTS_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
+#import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
+
+@class HistoryCoordinator;
+@class SigninInteractionCoordinator;
+@class TabGridCoordinator;
+@protocol BrowserInterfaceProvider;
+@protocol TabSwitcher;
+class AppUrlLoadingService;
+
+// TODO(crbug.com/1012697): Remove this protocol when SceneController is
+// operational. Move the private internals back into MainController, and pass
+// ownership of Scene-related objects to SceneController.
+@protocol MainControllerGuts <SettingsNavigationControllerDelegate,
+                              UserFeedbackDataSource>
+
+// Coordinator for displaying history.
+@property(nonatomic, strong) HistoryCoordinator* historyCoordinator;
+@property(nonatomic, strong)
+    SettingsNavigationController* settingsNavigationController;
+
+// The application level component for url loading. Is passed down to
+// browser state level UrlLoadingService instances.
+@property(nonatomic, assign) AppUrlLoadingService* appURLLoadingService;
+
+// The tab switcher command and the voice search commands can be sent by views
+// that reside in a different UIWindow leading to the fact that the exclusive
+// touch property will be ineffective and a command for processing both
+// commands may be sent in the same run of the runloop leading to
+// inconsistencies. Those two boolean indicate if one of those commands have
+// been processed in the last 200ms in order to only allow processing one at
+// a time.
+// TODO(crbug.com/560296):  Provide a general solution for handling mutually
+// exclusive chrome commands sent at nearly the same time.
+@property(nonatomic, assign) BOOL isProcessingTabSwitcherCommand;
+@property(nonatomic, assign) BOOL isProcessingVoiceSearchCommand;
+// The SigninInteractionCoordinator to present Sign In UI. It is created the
+// first time Sign In UI is needed to be presented and should not be destroyed
+// while the UI is presented.
+@property(nonatomic, strong)
+    SigninInteractionCoordinator* signinInteractionCoordinator;
+
+- (BOOL)isTabSwitcherActive;
+
+- (id<TabSwitcher>)tabSwitcher;
+- (ios::ChromeBrowserState*)mainBrowserState;
+- (ios::ChromeBrowserState*)currentBrowserState;
+- (BrowserViewController*)currentBVC;
+- (BrowserViewController*)mainBVC;
+- (BrowserViewController*)otrBVC;
+- (TabGridCoordinator*)mainCoordinator;
+- (id<BrowserInterfaceProvider>)interfaceProvider;
+- (void)startVoiceSearchInCurrentBVC;
+
+- (void)dismissModalDialogsWithCompletion:(ProceduralBlock)completion
+                           dismissOmnibox:(BOOL)dismissOmnibox;
+- (void)closeSettingsAnimated:(BOOL)animated
+                   completion:(ProceduralBlock)completion;
+
+- (void)dismissModalsAndOpenSelectedTabInMode:
+            (ApplicationModeForTabOpening)targetMode
+                            withUrlLoadParams:
+                                (const UrlLoadParams&)urlLoadParams
+                               dismissOmnibox:(BOOL)dismissOmnibox
+                                   completion:(ProceduralBlock)completion;
+- (void)showTabSwitcher;
+
+@end
+
+#endif  // IOS_CHROME_APP_MAIN_CONTROLLER_GUTS_H_
diff --git a/ios/chrome/browser/signin/authentication_service.h b/ios/chrome/browser/signin/authentication_service.h
index 33b9bb7..f14dc1b 100644
--- a/ios/chrome/browser/signin/authentication_service.h
+++ b/ios/chrome/browser/signin/authentication_service.h
@@ -66,7 +66,7 @@
   // they were stored in the browser state prefs. This storing happens every
   // time the accounts change in foreground.
   // This reloads the cached accounts if the information might be stale.
-  virtual bool HaveAccountsChanged() const;
+  virtual bool HaveAccountsChangedWhileInBackground() const;
 
   // ChromeIdentity management
 
@@ -126,14 +126,23 @@
   // ids.
   void MigrateAccountsStoredInPrefsIfNeeded();
 
-  // Stores the token service accounts in the browser state prefs.
-  void StoreAccountsInPrefs();
+  // Saves the last known list of accounts from the token service when
+  // the app is in foreground. This can be used when app comes back from
+  // background to detect if any changes occurred to the list. Must only
+  // be called when the application is in foreground.
+  // See HaveAccountsChangesWhileInBackground().
+  void StoreKnownAccountsWhileInForeground();
 
-  // Gets the accounts previously stored in the  browser state prefs.
-  std::vector<std::string> GetAccountsInPrefs();
+  // Gets the accounts previously stored as the foreground accounts in the
+  // browser state prefs.
+  // Returns the list of previously stored known accounts. This list
+  // is only updated when the app is in foreground and used to detect
+  // if any change occurred while the app was in background.
+  // See HaveAccountsChangesWhileInBackground().
+  std::vector<std::string> GetLastKnownAccountsFromForeground();
 
-  // Returns the cached MDM infos associated with |identity|. If the cache is
-  // stale for |identity|, the entry might be removed.
+  // Returns the cached MDM infos associated with |identity|. If the cache
+  // is stale for |identity|, the entry might be removed.
   NSDictionary* GetCachedMDMInfo(ChromeIdentity* identity) const;
 
   // Handles an MDM notification |user_info| associated with |identity|.
@@ -147,7 +156,7 @@
   //
   // |in_foreground| indicates whether the application was in foreground when
   // the identity list change notification was received.
-  void HandleIdentityListChanged(bool in_foreground);
+  void HandleIdentityListChanged();
 
   // Verifies that the authenticated user is still associated with a valid
   // ChromeIdentity. This method must only be called when the user is
@@ -177,6 +186,9 @@
   // or when the application is entering foregorund.
   void UpdateHaveAccountsChangedWhileInBackground();
 
+  // Returns whether the application is currently in the foreground or not.
+  bool InForeground() const;
+
   // signin::IdentityManager::Observer implementation.
   void OnEndBatchOfRefreshTokenStateChanges() override;
 
@@ -203,7 +215,7 @@
   // Whether the accounts have changed while the AuthenticationService was in
   // background. When the AuthenticationService is in background, this value
   // cannot be trusted.
-  bool have_accounts_changed_ = false;
+  bool have_accounts_changed_while_in_background_ = false;
 
   // Whether the AuthenticationService is currently reloading credentials, used
   // to avoid an infinite reloading loop.
diff --git a/ios/chrome/browser/signin/authentication_service.mm b/ios/chrome/browser/signin/authentication_service.mm
index f2837852..2fa22201 100644
--- a/ios/chrome/browser/signin/authentication_service.mm
+++ b/ios/chrome/browser/signin/authentication_service.mm
@@ -123,7 +123,7 @@
 }
 
 void AuthenticationService::OnApplicationWillEnterForeground() {
-  if (identity_manager_observer_.IsObservingSources())
+  if (InForeground())
     return;
 
   identity_manager_observer_.Add(identity_manager_);
@@ -133,7 +133,7 @@
   // changed (both are done by |UpdateHaveAccountsChangedWhileInBackground|).
   // After that, save the current list of accounts.
   UpdateHaveAccountsChangedWhileInBackground();
-  StoreAccountsInPrefs();
+  StoreKnownAccountsWhileInForeground();
 
   if (IsAuthenticated()) {
     bool sync_enabled = sync_setup_service_->IsSyncEnabled();
@@ -166,13 +166,23 @@
 }
 
 void AuthenticationService::OnApplicationDidEnterBackground() {
-  if (!identity_manager_observer_.IsObservingSources())
+  if (!InForeground())
     return;
 
   // Stop observing |identity_manager_| when in the background. Note that
   // this allows checking whether the app is in background without having a
   // separate bool by using identity_manager_observer_.IsObservingSources().
   identity_manager_observer_.Remove(identity_manager_);
+
+  // Reset the state |have_accounts_changed_while_in_background_| as the
+  // application just entered background.
+  have_accounts_changed_while_in_background_ = false;
+}
+
+bool AuthenticationService::InForeground() const {
+  // The application is in foreground when |identity_manager_observer_| is
+  // observing sources.
+  return identity_manager_observer_.IsObservingSources();
 }
 
 void AuthenticationService::SetPromptForSignIn() {
@@ -191,8 +201,9 @@
   // Load accounts from preference before synchronizing the accounts with
   // the system, otherwiser we would never detect any changes to the list
   // of accounts.
-  std::vector<std::string> old_accounts = GetAccountsInPrefs();
-  std::sort(old_accounts.begin(), old_accounts.end());
+  std::vector<std::string> last_foreground_accounts =
+      GetLastKnownAccountsFromForeground();
+  std::sort(last_foreground_accounts.begin(), last_foreground_accounts.end());
 
   // Reload credentials to ensure the accounts from the token service are
   // up-to-date.
@@ -201,18 +212,19 @@
   // must be set to true.
   ReloadCredentialsFromIdentities(/*should_prompt=*/true);
 
-  std::vector<CoreAccountInfo> new_accounts_info =
+  std::vector<CoreAccountInfo> current_accounts_info =
       identity_manager_->GetAccountsWithRefreshTokens();
-  std::vector<std::string> new_accounts;
-  for (const CoreAccountInfo& account_info : new_accounts_info)
-    new_accounts.push_back(account_info.account_id);
-  std::sort(new_accounts.begin(), new_accounts.end());
+  std::vector<std::string> current_accounts;
+  for (const CoreAccountInfo& account_info : current_accounts_info)
+    current_accounts.push_back(account_info.account_id);
+  std::sort(current_accounts.begin(), current_accounts.end());
 
-  have_accounts_changed_ = old_accounts != new_accounts;
+  have_accounts_changed_while_in_background_ =
+      last_foreground_accounts != current_accounts;
 }
 
-bool AuthenticationService::HaveAccountsChanged() const {
-  return have_accounts_changed_;
+bool AuthenticationService::HaveAccountsChangedWhileInBackground() const {
+  return have_accounts_changed_while_in_background_;
 }
 
 void AuthenticationService::MigrateAccountsStoredInPrefsIfNeeded() {
@@ -227,7 +239,7 @@
     return;
   }
 
-  std::vector<std::string> account_ids = GetAccountsInPrefs();
+  std::vector<std::string> account_ids = GetLastKnownAccountsFromForeground();
   std::vector<base::Value> accounts_pref_value;
   for (const std::string& account_id : account_ids) {
     if (identity_manager_->HasAccountWithRefreshToken(account_id)) {
@@ -235,8 +247,8 @@
     } else {
       // The account for |email| was removed since the last application cold
       // start. Insert |kFakeAccountIdForRemovedAccount| to ensure
-      // |have_accounts_changed_| will be set to true and the removal won't be
-      // silently ignored.
+      // |have_accounts_changed_while_in_background_| will be set to true and
+      // the removal won't be silently ignored.
       accounts_pref_value.emplace_back(kFakeAccountIdForRemovedAccount);
     }
   }
@@ -245,7 +257,8 @@
   pref_service_->SetBoolean(prefs::kSigninLastAccountsMigrated, true);
 }
 
-void AuthenticationService::StoreAccountsInPrefs() {
+void AuthenticationService::StoreKnownAccountsWhileInForeground() {
+  DCHECK(InForeground());
   std::vector<CoreAccountInfo> accounts(
       identity_manager_->GetAccountsWithRefreshTokens());
   std::vector<base::Value> accounts_pref_value;
@@ -255,7 +268,8 @@
                      base::Value(std::move(accounts_pref_value)));
 }
 
-std::vector<std::string> AuthenticationService::GetAccountsInPrefs() {
+std::vector<std::string>
+AuthenticationService::GetLastKnownAccountsFromForeground() {
   const base::Value* accounts_pref =
       pref_service_->GetList(prefs::kSigninLastAccounts);
 
@@ -418,7 +432,7 @@
   // Accounts maybe have been excluded or included from the current browser
   // state, without any change to the identity list.
   // Store the current list of accounts to make sure it is up-to-date.
-  StoreAccountsInPrefs();
+  StoreKnownAccountsWhileInForeground();
 }
 
 void AuthenticationService::OnIdentityListChanged() {
@@ -429,8 +443,7 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&AuthenticationService::HandleIdentityListChanged,
-                     GetWeakPtr(),
-                     identity_manager_observer_.IsObservingSources()));
+                     GetWeakPtr()));
 }
 
 bool AuthenticationService::HandleMDMNotification(ChromeIdentity* identity,
@@ -494,10 +507,10 @@
   identity_service_observer_.RemoveAll();
 }
 
-void AuthenticationService::HandleIdentityListChanged(bool in_foreground) {
+void AuthenticationService::HandleIdentityListChanged() {
   // Only notify the user about an identity change notification if the
   // application was in background.
-  if (in_foreground) {
+  if (InForeground()) {
     // Do not update the have accounts change state when in foreground.
     ReloadCredentialsFromIdentities(/*should_prompt=*/false);
     return;
diff --git a/ios/chrome/browser/signin/authentication_service_fake.h b/ios/chrome/browser/signin/authentication_service_fake.h
index f6cf90e..0502c213 100644
--- a/ios/chrome/browser/signin/authentication_service_fake.h
+++ b/ios/chrome/browser/signin/authentication_service_fake.h
@@ -31,9 +31,9 @@
   void SignOut(signin_metrics::ProfileSignout signout_source,
                ProceduralBlock completion) override;
 
-  void SetHaveAccountsChanged(bool changed);
+  void SetHaveAccountsChangedWhileInBackground(bool changed);
 
-  bool HaveAccountsChanged() const override;
+  bool HaveAccountsChangedWhileInBackground() const override;
 
   bool IsAuthenticated() const override;
 
@@ -46,7 +46,7 @@
                             syncer::SyncService* sync_service);
 
   __strong ChromeIdentity* authenticated_identity_;
-  bool have_accounts_changed_;
+  bool have_accounts_changed_while_in_background_;
 };
 
 #endif  // IOS_CHROME_BROWSER_SIGNIN_AUTHENTICATION_SERVICE_FAKE_H_
diff --git a/ios/chrome/browser/signin/authentication_service_fake.mm b/ios/chrome/browser/signin/authentication_service_fake.mm
index cdf7324..09292fce 100644
--- a/ios/chrome/browser/signin/authentication_service_fake.mm
+++ b/ios/chrome/browser/signin/authentication_service_fake.mm
@@ -29,7 +29,7 @@
                             sync_setup_service,
                             identity_manager,
                             sync_service),
-      have_accounts_changed_(false) {}
+      have_accounts_changed_while_in_background_(false) {}
 
 AuthenticationServiceFake::~AuthenticationServiceFake() {}
 
@@ -48,12 +48,13 @@
     completion();
 }
 
-void AuthenticationServiceFake::SetHaveAccountsChanged(bool changed) {
-  have_accounts_changed_ = changed;
+void AuthenticationServiceFake::SetHaveAccountsChangedWhileInBackground(
+    bool changed) {
+  have_accounts_changed_while_in_background_ = changed;
 }
 
-bool AuthenticationServiceFake::HaveAccountsChanged() const {
-  return have_accounts_changed_;
+bool AuthenticationServiceFake::HaveAccountsChangedWhileInBackground() const {
+  return have_accounts_changed_while_in_background_;
 }
 
 bool AuthenticationServiceFake::IsAuthenticated() const {
diff --git a/ios/chrome/browser/signin/authentication_service_unittest.mm b/ios/chrome/browser/signin/authentication_service_unittest.mm
index 52dd32a..8aa0116b 100644
--- a/ios/chrome/browser/signin/authentication_service_unittest.mm
+++ b/ios/chrome/browser/signin/authentication_service_unittest.mm
@@ -108,12 +108,12 @@
     EXPECT_CALL(*sync_setup_service_mock(), PrepareForFirstSyncSetup());
   }
 
-  void StoreAccountsInPrefs() {
-    authentication_service()->StoreAccountsInPrefs();
+  void StoreKnownAccountsWhileInForeground() {
+    authentication_service()->StoreKnownAccountsWhileInForeground();
   }
 
-  std::vector<std::string> GetAccountsInPrefs() {
-    return authentication_service()->GetAccountsInPrefs();
+  std::vector<std::string> GetLastKnownAccountsFromForeground() {
+    return authentication_service()->GetLastKnownAccountsFromForeground();
   }
 
   void FireApplicationWillEnterForeground() {
@@ -249,7 +249,7 @@
 
 TEST_F(AuthenticationServiceTest, StoreAndGetAccountsInPrefs) {
   // Profile starts empty.
-  std::vector<std::string> accounts = GetAccountsInPrefs();
+  std::vector<std::string> accounts = GetLastKnownAccountsFromForeground();
   EXPECT_TRUE(accounts.empty());
 
   // Sign in.
@@ -258,12 +258,11 @@
 
   // Store the accounts and get them back from the prefs. They should be the
   // same as the token service accounts.
-  StoreAccountsInPrefs();
-  accounts = GetAccountsInPrefs();
+  StoreKnownAccountsWhileInForeground();
+  accounts = GetLastKnownAccountsFromForeground();
   ASSERT_EQ(2u, accounts.size());
-
-      EXPECT_EQ("foo2ID", accounts[0]);
-      EXPECT_EQ("fooID", accounts[1]);
+  EXPECT_EQ("foo2ID", accounts[0]);
+  EXPECT_EQ("fooID", accounts[1]);
 }
 
 TEST_F(AuthenticationServiceTest,
@@ -282,9 +281,8 @@
       identity_manager()->GetAccountsWithRefreshTokens();
   std::sort(accounts.begin(), accounts.end(), account_compare_func);
   ASSERT_EQ(2u, accounts.size());
-
-      EXPECT_EQ("foo2ID", accounts[0].account_id);
-      EXPECT_EQ("fooID", accounts[1].account_id);
+  EXPECT_EQ("foo2ID", accounts[0].account_id);
+  EXPECT_EQ("fooID", accounts[1].account_id);
 
   // Simulate a switching to background and back to foreground, triggering a
   // credentials reload.
@@ -296,13 +294,14 @@
   accounts = identity_manager()->GetAccountsWithRefreshTokens();
   std::sort(accounts.begin(), accounts.end(), account_compare_func);
   ASSERT_EQ(3u, accounts.size());
-      EXPECT_EQ("foo2ID", accounts[0].account_id);
-      EXPECT_EQ("foo3ID", accounts[1].account_id);
-      EXPECT_EQ("fooID", accounts[2].account_id);
+  EXPECT_EQ("foo2ID", accounts[0].account_id);
+  EXPECT_EQ("foo3ID", accounts[1].account_id);
+  EXPECT_EQ("fooID", accounts[2].account_id);
 }
 
 TEST_F(AuthenticationServiceTest, HaveAccountsChanged_Default) {
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 }
 
 TEST_F(AuthenticationServiceTest, HaveAccountsChanged_NoChange) {
@@ -315,15 +314,18 @@
 
   // If an account is added while the application is in foreground, then the
   // have accounts changed state should stay false.
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Backgrounding the app should not change the have accounts changed state.
   FireApplicationDidEnterBackground();
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Foregrounding the app should not change the have accounts changed state.
   FireApplicationWillEnterForeground();
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 }
 
 TEST_F(AuthenticationServiceTest, HaveAccountsChanged_ChangedInBackground) {
@@ -333,14 +335,15 @@
   identity_service()->AddIdentities(@[ @"foo3" ]);
   FireIdentityListChanged();
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Simulate a switching to background and back to foreground, changing the
   // accounts while in background (no notification fired by |identity_service|).
   FireApplicationDidEnterBackground();
   identity_service()->AddIdentities(@[ @"foo4" ]);
   FireApplicationWillEnterForeground();
-  EXPECT_TRUE(authentication_service()->HaveAccountsChanged());
+  EXPECT_TRUE(authentication_service()->HaveAccountsChangedWhileInBackground());
 }
 
 TEST_F(AuthenticationServiceTest, HaveAccountsChanged_CalledInBackground) {
@@ -350,7 +353,8 @@
   identity_service()->AddIdentities(@[ @"foo3" ]);
   FireIdentityListChanged();
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Simulate a switching to background, changing the accounts while in
   // background.
@@ -358,11 +362,11 @@
   identity_service()->AddIdentities(@[ @"foo4" ]);
   FireIdentityListChanged();
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(authentication_service()->HaveAccountsChanged());
+  EXPECT_TRUE(authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Entering foreground should not change the have accounts changed state.
   FireApplicationWillEnterForeground();
-  EXPECT_TRUE(authentication_service()->HaveAccountsChanged());
+  EXPECT_TRUE(authentication_service()->HaveAccountsChangedWhileInBackground());
 }
 
 // Regression test for http://crbug.com/1006717
@@ -373,7 +377,8 @@
   identity_service()->AddIdentities(@[ @"foo3" ]);
   FireIdentityListChanged();
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Simulate a switching to background, changing the accounts while in
   // background.
@@ -386,14 +391,15 @@
   // When entering foreground, the have accounts changed state should be
   // updated.
   FireApplicationWillEnterForeground();
-  EXPECT_TRUE(authentication_service()->HaveAccountsChanged());
+  EXPECT_TRUE(authentication_service()->HaveAccountsChangedWhileInBackground());
 
   // Backgrounding and foregrounding the application a second time should update
   // the list of accounts in |kSigninLastAccounts| and should reset the have
   // account changed state.
   FireApplicationDidEnterBackground();
   FireApplicationWillEnterForeground();
-  EXPECT_FALSE(authentication_service()->HaveAccountsChanged());
+  EXPECT_FALSE(
+      authentication_service()->HaveAccountsChangedWhileInBackground());
 }
 
 TEST_F(AuthenticationServiceTest, IsAuthenticatedBackground) {
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 507d7dfe..5ceb7be 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -574,11 +574,14 @@
   int oldCount = _webStateList->count();
   DCHECK_GE(oldCount, 0);
 
-  web::WebState::CreateParams createParams(_browserState);
-  DeserializeWebStateList(
-      _webStateList, window,
-      base::BindRepeating(&web::WebState::CreateWithStorageSession,
-                          createParams));
+  _webStateList->PerformBatchOperation(
+      base::BindOnce(^(WebStateList* web_state_list) {
+        web::WebState::CreateParams createParams(_browserState);
+        DeserializeWebStateList(
+            web_state_list, window,
+            base::BindRepeating(&web::WebState::CreateWithStorageSession,
+                                createParams));
+      }));
 
   DCHECK_GT(_webStateList->count(), oldCount);
   int restoredCount = _webStateList->count() - oldCount;
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
index d1762cc..afcb362 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
@@ -206,11 +206,12 @@
   if (prevSessionInfo.isFirstSessionAfterUpgrade &&
       [prevSessionInfo.previousSessionVersion hasPrefix:@"77."]) {
     // In M77, showing the signed-in account view was disabled due to the fact
-    // that the preferences used to compute authService->HaveAccountsChanged()
-    // were not correctly updated (see crbug.com/1006717).
-    // To avoid user confusion, it is important to avoid showing the signed-in
-    // accounts dialog on the first session after an update from M77 in order
-    // to allow the authentication service to update its internal preferences.
+    // that the preferences used to compute
+    // authService->HaveAccountsChangedWhileInBackground() were not correctly
+    // updated (see crbug.com/1006717). To avoid user confusion, it is important
+    // to avoid showing the signed-in accounts dialog on the first session after
+    // an update from M77 in order to allow the authentication service to update
+    // its internal preferences.
     //
     // TODO(crbug.com/1007990) Remove this code after M81 (revert
     // https://chromium-review.googlesource.com/c/chromium/src/+/1824259 ).
@@ -220,7 +221,8 @@
   AuthenticationService* authService =
       AuthenticationServiceFactory::GetForBrowserState(browserState);
   return !gSignedInAccountsViewControllerIsShown &&
-         authService->IsAuthenticated() && authService->HaveAccountsChanged();
+         authService->IsAuthenticated() &&
+         authService->HaveAccountsChangedWhileInBackground();
 }
 
 #pragma mark Initialization
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
index b46bb33..aeb2ca8c 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
@@ -63,7 +63,7 @@
 // have changed.
 TEST_F(SignedInAccountsViewControllerTest,
        ShouldBePresentedForBrowserStateNecessary) {
-  auth_service_->SetHaveAccountsChanged(true);
+  auth_service_->SetHaveAccountsChangedWhileInBackground(true);
   EXPECT_TRUE([SignedInAccountsViewController
       shouldBePresentedForBrowserState:browser_state_.get()]);
 }
@@ -72,7 +72,7 @@
 // session after upgrade.
 TEST_F(SignedInAccountsViewControllerTest,
        ShouldBePresentedForBrowserStateAfterUpgrade) {
-  auth_service_->SetHaveAccountsChanged(true);
+  auth_service_->SetHaveAccountsChangedWhileInBackground(true);
 
   {
     [PreviousSessionInfo resetSharedInstanceForTesting];
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index 4ad14fa..cb925d1 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -330,26 +330,11 @@
 - (void)undoCloseAllItems {
   if (!self.closedSessionWindow)
     return;
-  DCHECK(self.tabModel.browserState);
-  // Don't trigger the initial load for these restored WebStates since the
-  // number of WKWebViews is unbounded and may lead to an OOM crash.
-  WebStateListWebUsageEnabler* webUsageEnabler =
-      WebStateListWebUsageEnablerFactory::GetInstance()->GetForBrowserState(
-          self.tabModel.browserState);
-  webUsageEnabler->SetTriggersInitialLoad(false);
-  web::WebState::CreateParams createParams(self.tabModel.browserState);
-  DeserializeWebStateList(
-      self.webStateList, self.closedSessionWindow,
-      base::BindRepeating(&web::WebState::CreateWithStorageSession,
-                          createParams));
-  webUsageEnabler->SetTriggersInitialLoad(true);
-
-  self.closedSessionWindow = nil;
-  [self removeEntriesFromTabRestoreService];
-  self.closedTabsCount = 0;
-  // Unmark all images for deletion since they are now active tabs again.
-  ios::ChromeBrowserState* browserState = self.tabModel.browserState;
-  [SnapshotCacheFactory::GetForBrowserState(browserState) unmarkAllImages];
+  __weak TabGridMediator* weakSelf = self;
+  self.webStateList->PerformBatchOperation(
+      base::BindOnce(^(WebStateList* web_state_list) {
+        [weakSelf restoreClosedSessionWindowAndUpdateTabRestoreService];
+      }));
 }
 
 - (void)discardSavedClosedItems {
@@ -451,6 +436,33 @@
   }
 }
 
+// Restores the saved |self.closedSessionWindow| and updates the
+// TabRestoreService.
+- (void)restoreClosedSessionWindowAndUpdateTabRestoreService {
+  if (!self.closedSessionWindow)
+    return;
+  DCHECK(self.tabModel.browserState);
+  // Don't trigger the initial load for these restored WebStates since the
+  // number of WKWebViews is unbounded and may lead to an OOM crash.
+  WebStateListWebUsageEnabler* webUsageEnabler =
+      WebStateListWebUsageEnablerFactory::GetInstance()->GetForBrowserState(
+          self.tabModel.browserState);
+  webUsageEnabler->SetTriggersInitialLoad(false);
+  web::WebState::CreateParams createParams(self.tabModel.browserState);
+  DeserializeWebStateList(
+      self.webStateList, self.closedSessionWindow,
+      base::BindRepeating(&web::WebState::CreateWithStorageSession,
+                          createParams));
+  webUsageEnabler->SetTriggersInitialLoad(true);
+
+  self.closedSessionWindow = nil;
+  [self removeEntriesFromTabRestoreService];
+  self.closedTabsCount = 0;
+  // Unmark all images for deletion since they are now active tabs again.
+  ios::ChromeBrowserState* browserState = self.tabModel.browserState;
+  [SnapshotCacheFactory::GetForBrowserState(browserState) unmarkAllImages];
+}
+
 // Returns a SnapshotCache for the current BrowserState.
 - (SnapshotCache*)snapshotCache {
   if (!_tabModel.browserState)
diff --git a/ios/chrome/browser/web_state_list/web_state_list_serialization.mm b/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
index d56339e9..f21dca8 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
@@ -27,54 +27,6 @@
 // the WebStates stored in the WebStateList.
 NSString* const kOpenerIndexKey = @"OpenerIndex";
 NSString* const kOpenerNavigationIndexKey = @"OpenerNavigationIndex";
-
-// Helper for DeserializeWebStateList allowing the mutation to appears as a
-// single batched operation.
-void DeserializeWebStateListHelper(SessionWindowIOS* session_window,
-                                   const WebStateFactory& web_state_factory,
-                                   WebStateList* web_state_list) {
-  const int old_count = web_state_list->count();
-  for (CRWSessionStorage* session in session_window.sessions) {
-    std::unique_ptr<web::WebState> web_state = web_state_factory.Run(session);
-    web_state_list->InsertWebState(
-        web_state_list->count(), std::move(web_state),
-        WebStateList::INSERT_FORCE_INDEX, WebStateOpener());
-  }
-
-  // Restore the WebStates opener-opened relationship.
-  for (int index = old_count; index < web_state_list->count(); ++index) {
-    web::WebState* web_state = web_state_list->GetWebStateAt(index);
-    web::SerializableUserDataManager* user_data_manager =
-        web::SerializableUserDataManager::FromWebState(web_state);
-
-    NSNumber* boxed_opener_index = base::mac::ObjCCast<NSNumber>(
-        user_data_manager->GetValueForSerializationKey(kOpenerIndexKey));
-
-    NSNumber* boxed_opener_navigation_index = base::mac::ObjCCast<NSNumber>(
-        user_data_manager->GetValueForSerializationKey(
-            kOpenerNavigationIndexKey));
-
-    if (!boxed_opener_index || !boxed_opener_navigation_index)
-      continue;
-
-    // If opener index is out of bound then assume there is no opener.
-    const int opener_index = [boxed_opener_index intValue] + old_count;
-    if (opener_index < old_count || opener_index >= web_state_list->count())
-      continue;
-
-    web::WebState* opener = web_state_list->GetWebStateAt(opener_index);
-    web_state_list->SetOpenerOfWebStateAt(
-        index,
-        WebStateOpener(opener, [boxed_opener_navigation_index intValue]));
-  }
-
-  if (session_window.selectedIndex != NSNotFound) {
-    DCHECK_LT(session_window.selectedIndex, session_window.sessions.count);
-    DCHECK_LT(session_window.selectedIndex, static_cast<NSUInteger>(INT_MAX));
-    web_state_list->ActivateWebStateAt(
-        old_count + static_cast<int>(session_window.selectedIndex));
-  }
-}
 }  // namespace
 
 SessionWindowIOS* SerializeWebStateList(WebStateList* web_state_list) {
@@ -116,7 +68,45 @@
 void DeserializeWebStateList(WebStateList* web_state_list,
                              SessionWindowIOS* session_window,
                              const WebStateFactory& web_state_factory) {
-  web_state_list->PerformBatchOperation(
-      base::BindOnce(&DeserializeWebStateListHelper,
-                     base::Unretained(session_window), web_state_factory));
+  const int old_count = web_state_list->count();
+  for (CRWSessionStorage* session in session_window.sessions) {
+    std::unique_ptr<web::WebState> web_state = web_state_factory.Run(session);
+    web_state_list->InsertWebState(
+        web_state_list->count(), std::move(web_state),
+        WebStateList::INSERT_FORCE_INDEX, WebStateOpener());
+  }
+
+  // Restore the WebStates opener-opened relationship.
+  for (int index = old_count; index < web_state_list->count(); ++index) {
+    web::WebState* web_state = web_state_list->GetWebStateAt(index);
+    web::SerializableUserDataManager* user_data_manager =
+        web::SerializableUserDataManager::FromWebState(web_state);
+
+    NSNumber* boxed_opener_index = base::mac::ObjCCast<NSNumber>(
+        user_data_manager->GetValueForSerializationKey(kOpenerIndexKey));
+
+    NSNumber* boxed_opener_navigation_index = base::mac::ObjCCast<NSNumber>(
+        user_data_manager->GetValueForSerializationKey(
+            kOpenerNavigationIndexKey));
+
+    if (!boxed_opener_index || !boxed_opener_navigation_index)
+      continue;
+
+    // If opener index is out of bound then assume there is no opener.
+    const int opener_index = [boxed_opener_index intValue] + old_count;
+    if (opener_index < old_count || opener_index >= web_state_list->count())
+      continue;
+
+    web::WebState* opener = web_state_list->GetWebStateAt(opener_index);
+    web_state_list->SetOpenerOfWebStateAt(
+        index,
+        WebStateOpener(opener, [boxed_opener_navigation_index intValue]));
+  }
+
+  if (session_window.selectedIndex != NSNotFound) {
+    DCHECK_LT(session_window.selectedIndex, session_window.sessions.count);
+    DCHECK_LT(session_window.selectedIndex, static_cast<NSUInteger>(INT_MAX));
+    web_state_list->ActivateWebStateAt(
+        old_count + static_cast<int>(session_window.selectedIndex));
+  }
 }
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
index 0d28566..2858f46 100644
--- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
@@ -66,17 +66,18 @@
 
 class MojoCameraClientObserver : public CameraClientObserver {
  public:
-  explicit MojoCameraClientObserver(cros::mojom::CameraHalClientPtr client)
+  explicit MojoCameraClientObserver(
+      mojo::PendingRemote<cros::mojom::CameraHalClient> client)
       : client_(std::move(client)) {}
 
   void OnChannelCreated(cros::mojom::CameraModulePtr camera_module) override {
     client_->SetUpChannel(std::move(camera_module));
   }
 
-  cros::mojom::CameraHalClientPtr& client() { return client_; }
+  mojo::Remote<cros::mojom::CameraHalClient>& client() { return client_; }
 
  private:
-  cros::mojom::CameraHalClientPtr client_;
+  mojo::Remote<cros::mojom::CameraHalClient> client_;
   DISALLOW_IMPLICIT_CONSTRUCTORS(MojoCameraClientObserver);
 };
 
@@ -164,17 +165,17 @@
 }
 
 void CameraHalDispatcherImpl::RegisterServer(
-    cros::mojom::CameraHalServerPtr camera_hal_server) {
+    mojo::PendingRemote<cros::mojom::CameraHalServer> camera_hal_server) {
   DCHECK(proxy_task_runner_->BelongsToCurrentThread());
 
   if (camera_hal_server_) {
     LOG(ERROR) << "Camera HAL server is already registered";
     return;
   }
-  camera_hal_server.set_connection_error_handler(
+  camera_hal_server_.Bind(std::move(camera_hal_server));
+  camera_hal_server_.set_disconnect_handler(
       base::BindOnce(&CameraHalDispatcherImpl::OnCameraHalServerConnectionError,
                      base::Unretained(this)));
-  camera_hal_server_ = std::move(camera_hal_server);
   VLOG(1) << "Camera HAL server registered";
 
   // Set up the Mojo channels for clients which registered before the server
@@ -185,14 +186,14 @@
 }
 
 void CameraHalDispatcherImpl::RegisterClient(
-    cros::mojom::CameraHalClientPtr client) {
+    mojo::PendingRemote<cros::mojom::CameraHalClient> client) {
   // RegisterClient can be called locally by ArcCameraBridge. Unretained
   // reference is safe here because CameraHalDispatcherImpl owns
   // |proxy_thread_|.
   proxy_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImpl::RegisterClientOnProxyThread,
-                     base::Unretained(this), client.PassInterface()));
+                     base::Unretained(this), std::move(client)));
 }
 
 void CameraHalDispatcherImpl::GetJpegDecodeAccelerator(
@@ -333,12 +334,11 @@
 }
 
 void CameraHalDispatcherImpl::RegisterClientOnProxyThread(
-    mojo::InterfacePtrInfo<cros::mojom::CameraHalClient> client_ptr_info) {
+    mojo::PendingRemote<cros::mojom::CameraHalClient> client) {
   DCHECK(proxy_task_runner_->BelongsToCurrentThread());
-  cros::mojom::CameraHalClientPtr client_ptr(std::move(client_ptr_info));
   auto client_observer =
-      std::make_unique<MojoCameraClientObserver>(std::move(client_ptr));
-  client_observer->client().set_connection_error_handler(base::BindOnce(
+      std::make_unique<MojoCameraClientObserver>(std::move(client));
+  client_observer->client().set_disconnect_handler(base::BindOnce(
       &CameraHalDispatcherImpl::OnCameraHalClientConnectionError,
       base::Unretained(this), base::Unretained(client_observer.get())));
   AddClientObserver(std::move(client_observer));
@@ -367,8 +367,9 @@
 void CameraHalDispatcherImpl::OnPeerConnected(
     mojo::ScopedMessagePipeHandle message_pipe) {
   DCHECK(proxy_task_runner_->BelongsToCurrentThread());
-  binding_set_.AddBinding(
-      this, cros::mojom::CameraHalDispatcherRequest(std::move(message_pipe)));
+  receiver_set_.Add(this,
+                    mojo::PendingReceiver<cros::mojom::CameraHalDispatcher>(
+                        std::move(message_pipe)));
   VLOG(1) << "New CameraHalDispatcher binding added";
 }
 
@@ -398,7 +399,7 @@
   cancel_pipe_.reset();
   client_observers_.clear();
   camera_hal_server_.reset();
-  binding_set_.CloseAllBindings();
+  receiver_set_.Clear();
 }
 
 void CameraHalDispatcherImpl::OnTraceLogEnabledOnProxyThread() {
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.h b/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
index 0c34d6f..b8370d00 100644
--- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
@@ -18,8 +18,10 @@
 #include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"
 #include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
 #include "media/capture/video/video_capture_device_factory.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
 
 namespace base {
@@ -64,8 +66,10 @@
   bool IsStarted();
 
   // CameraHalDispatcher implementations.
-  void RegisterServer(cros::mojom::CameraHalServerPtr server) final;
-  void RegisterClient(cros::mojom::CameraHalClientPtr client) final;
+  void RegisterServer(
+      mojo::PendingRemote<cros::mojom::CameraHalServer> server) final;
+  void RegisterClient(
+      mojo::PendingRemote<cros::mojom::CameraHalClient> client) final;
   void GetJpegDecodeAccelerator(
       mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
           jda_receiver) final;
@@ -96,7 +100,7 @@
   void StartServiceLoop(base::ScopedFD socket_fd, base::WaitableEvent* started);
 
   void RegisterClientOnProxyThread(
-      mojo::InterfacePtrInfo<cros::mojom::CameraHalClient> client_ptr_info);
+      mojo::PendingRemote<cros::mojom::CameraHalClient> client);
   void AddClientObserverOnProxyThread(
       std::unique_ptr<CameraClientObserver> observer);
 
@@ -122,9 +126,9 @@
   scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> blocking_io_task_runner_;
 
-  mojo::BindingSet<cros::mojom::CameraHalDispatcher> binding_set_;
+  mojo::ReceiverSet<cros::mojom::CameraHalDispatcher> receiver_set_;
 
-  cros::mojom::CameraHalServerPtr camera_hal_server_;
+  mojo::Remote<cros::mojom::CameraHalServer> camera_hal_server_;
 
   std::set<std::unique_ptr<CameraClientObserver>, base::UniquePtrComparator>
       client_observers_;
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc
index 80aa63cd..e93be07 100644
--- a/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc
@@ -13,7 +13,8 @@
 #include "base/test/task_environment.h"
 #include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
 #include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,7 +26,7 @@
 
 class MockCameraHalServer : public cros::mojom::CameraHalServer {
  public:
-  MockCameraHalServer() : binding_(this) {}
+  MockCameraHalServer() = default;
 
   ~MockCameraHalServer() = default;
 
@@ -38,22 +39,18 @@
 
   MOCK_METHOD1(SetTracingEnabled, void(bool enabled));
 
-  cros::mojom::CameraHalServerPtrInfo GetInterfacePtrInfo() {
-    cros::mojom::CameraHalServerPtrInfo camera_hal_server_ptr_info;
-    cros::mojom::CameraHalServerRequest camera_hal_server_request =
-        mojo::MakeRequest(&camera_hal_server_ptr_info);
-    binding_.Bind(std::move(camera_hal_server_request));
-    return camera_hal_server_ptr_info;
+  mojo::PendingRemote<cros::mojom::CameraHalServer> GetPendingRemote() {
+    return receiver_.BindNewPipeAndPassRemote();
   }
 
  private:
-  mojo::Binding<cros::mojom::CameraHalServer> binding_;
+  mojo::Receiver<cros::mojom::CameraHalServer> receiver_{this};
   DISALLOW_COPY_AND_ASSIGN(MockCameraHalServer);
 };
 
 class MockCameraHalClient : public cros::mojom::CameraHalClient {
  public:
-  MockCameraHalClient() : binding_(this) {}
+  MockCameraHalClient() = default;
 
   ~MockCameraHalClient() = default;
 
@@ -63,16 +60,12 @@
   MOCK_METHOD1(DoSetUpChannel,
                void(cros::mojom::CameraModulePtr& camera_module_ptr));
 
-  cros::mojom::CameraHalClientPtrInfo GetInterfacePtrInfo() {
-    cros::mojom::CameraHalClientPtrInfo camera_hal_client_ptr_info;
-    cros::mojom::CameraHalClientRequest camera_hal_client_request =
-        mojo::MakeRequest(&camera_hal_client_ptr_info);
-    binding_.Bind(std::move(camera_hal_client_request));
-    return camera_hal_client_ptr_info;
+  mojo::PendingRemote<cros::mojom::CameraHalClient> GetPendingRemote() {
+    return receiver_.BindNewPipeAndPassRemote();
   }
 
  private:
-  mojo::Binding<cros::mojom::CameraHalClient> binding_;
+  mojo::Receiver<cros::mojom::CameraHalClient> receiver_{this};
   DISALLOW_COPY_AND_ASSIGN(MockCameraHalClient);
 };
 
@@ -106,14 +99,16 @@
     }
   }
 
-  static void RegisterServer(CameraHalDispatcherImpl* dispatcher,
-                             cros::mojom::CameraHalServerPtrInfo server) {
-    dispatcher->RegisterServer(mojo::MakeProxy(std::move(server)));
+  static void RegisterServer(
+      CameraHalDispatcherImpl* dispatcher,
+      mojo::PendingRemote<cros::mojom::CameraHalServer> server) {
+    dispatcher->RegisterServer(std::move(server));
   }
 
-  static void RegisterClient(CameraHalDispatcherImpl* dispatcher,
-                             cros::mojom::CameraHalClientPtrInfo client) {
-    dispatcher->RegisterClient(mojo::MakeProxy(std::move(client)));
+  static void RegisterClient(
+      CameraHalDispatcherImpl* dispatcher,
+      mojo::PendingRemote<cros::mojom::CameraHalClient> client) {
+    dispatcher->RegisterClient(std::move(client));
   }
 
  protected:
@@ -141,16 +136,16 @@
       .WillOnce(
           InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
 
-  auto server_ptr = mock_server->GetInterfacePtrInfo();
+  auto server = mock_server->GetPendingRemote();
   GetProxyTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImplTest::RegisterServer,
-                     base::Unretained(dispatcher_), base::Passed(&server_ptr)));
-  auto client_ptr = mock_client->GetInterfacePtrInfo();
+                     base::Unretained(dispatcher_), std::move(server)));
+  auto client = mock_client->GetPendingRemote();
   GetProxyTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImplTest::RegisterClient,
-                     base::Unretained(dispatcher_), base::Passed(&client_ptr)));
+                     base::Unretained(dispatcher_), std::move(client)));
 
   // Wait until the client gets the established Mojo channel.
   DoLoop();
@@ -166,11 +161,11 @@
       .WillOnce(
           InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
 
-  server_ptr = mock_server->GetInterfacePtrInfo();
+  server = mock_server->GetPendingRemote();
   GetProxyTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImplTest::RegisterServer,
-                     base::Unretained(dispatcher_), base::Passed(&server_ptr)));
+                     base::Unretained(dispatcher_), std::move(server)));
 
   // Wait until the clients gets the newly established Mojo channel.
   DoLoop();
@@ -190,16 +185,16 @@
       .WillOnce(
           InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
 
-  auto server_ptr = mock_server->GetInterfacePtrInfo();
+  auto server = mock_server->GetPendingRemote();
   GetProxyTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImplTest::RegisterServer,
-                     base::Unretained(dispatcher_), base::Passed(&server_ptr)));
-  auto client_ptr = mock_client->GetInterfacePtrInfo();
+                     base::Unretained(dispatcher_), std::move(server)));
+  auto client = mock_client->GetPendingRemote();
   GetProxyTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImplTest::RegisterClient,
-                     base::Unretained(dispatcher_), base::Passed(&client_ptr)));
+                     base::Unretained(dispatcher_), std::move(client)));
 
   // Wait until the client gets the established Mojo channel.
   DoLoop();
@@ -215,11 +210,11 @@
       .WillOnce(
           InvokeWithoutArgs(this, &CameraHalDispatcherImplTest::QuitRunLoop));
 
-  client_ptr = mock_client->GetInterfacePtrInfo();
+  client = mock_client->GetPendingRemote();
   GetProxyTaskRunner()->PostTask(
       FROM_HERE,
       base::BindOnce(&CameraHalDispatcherImplTest::RegisterClient,
-                     base::Unretained(dispatcher_), base::Passed(&client_ptr)));
+                     base::Unretained(dispatcher_), std::move(client)));
 
   // Wait until the clients gets the newly established Mojo channel.
   DoLoop();
diff --git a/media/capture/video/chromeos/mojom/cros_camera_service.mojom b/media/capture/video/chromeos/mojom/cros_camera_service.mojom
index d2bd0f2..a8e388c 100644
--- a/media/capture/video/chromeos/mojom/cros_camera_service.mojom
+++ b/media/capture/video/chromeos/mojom/cros_camera_service.mojom
@@ -21,11 +21,11 @@
 interface CameraHalDispatcher {
   // A CameraHalServer calls RegisterServer to register itself with the
   // dispatcher.
-  RegisterServer@0(CameraHalServer server);
+  RegisterServer@0(pending_remote<CameraHalServer> server);
 
   // A CameraHalClient calls RegisterClient to register itself with the
   // dispatcher.
-  RegisterClient@1(CameraHalClient client);
+  RegisterClient@1(pending_remote<CameraHalClient> client);
 
   // Get JpegDecodeAccelerator from dispatcher.
   [MinVersion=1] GetJpegDecodeAccelerator@2(
diff --git a/media/gpu/linux/video_decoder_pipeline.cc b/media/gpu/linux/video_decoder_pipeline.cc
index 58d0a3c..9777c07 100644
--- a/media/gpu/linux/video_decoder_pipeline.cc
+++ b/media/gpu/linux/video_decoder_pipeline.cc
@@ -108,25 +108,19 @@
 int VideoDecoderPipeline::GetMaxDecodeRequests() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
 
-  if (!decoder_)
-    DVLOGF(1) << "Call before Initialize() success.";
-  return decoder_ ? decoder_->GetMaxDecodeRequests() : 1;
+  return 4;
 }
 
 bool VideoDecoderPipeline::NeedsBitstreamConversion() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
 
-  if (!decoder_)
-    DVLOGF(1) << "Call before Initialize() success.";
-  return decoder_ ? decoder_->NeedsBitstreamConversion() : false;
+  return needs_bitstream_conversion_;
 }
 
 bool VideoDecoderPipeline::CanReadWithoutStalling() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
 
-  if (!decoder_)
-    DVLOGF(1) << "Call before Initialize() success.";
-  return decoder_ ? decoder_->CanReadWithoutStalling() : false;
+  return frame_pool_ && !frame_pool_->IsExhausted();
 }
 
 void VideoDecoderPipeline::Initialize(const VideoDecoderConfig& config,
@@ -139,35 +133,46 @@
   DCHECK(!init_cb_);
   VLOGF(2) << "config: " << config.AsHumanReadableString();
 
+  if (!config.IsValidConfig()) {
+    VLOGF(1) << "config is not valid";
+    std::move(init_cb).Run(false);
+    return;
+  }
+  if (config.is_encrypted()) {
+    VLOGF(1) << "Encrypted streams are not supported for this VD";
+    std::move(init_cb).Run(false);
+    return;
+  }
+  if (cdm_context) {
+    VLOGF(1) << "cdm_context is not supported.";
+    std::move(init_cb).Run(false);
+    return;
+  }
+
+  needs_bitstream_conversion_ = (config.codec() == kCodecH264);
   client_output_cb_ = std::move(output_cb);
   init_cb_ = std::move(init_cb);
   base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs =
       get_create_vd_functions_cb_.Run(used_create_vd_func_);
 
   if (!decoder_) {
-    CreateAndInitializeVD(std::move(create_vd_funcs), config, low_delay,
-                          cdm_context, waiting_cb);
+    CreateAndInitializeVD(std::move(create_vd_funcs), config);
   } else {
     decoder_->Initialize(
-        config, low_delay, cdm_context,
+        config,
         // If it fails to re-initialize current |decoder_|, it will create
         // another decoder instance by trying available VD creation functions
         // again. See |OnInitializeDone| for detail.
         base::BindOnce(&VideoDecoderPipeline::OnInitializeDone, weak_this_,
-                       std::move(create_vd_funcs), config, low_delay,
-                       cdm_context, waiting_cb),
+                       std::move(create_vd_funcs), config),
         base::BindRepeating(&VideoDecoderPipeline::OnFrameDecodedThunk,
-                            client_task_runner_, weak_this_),
-        waiting_cb);
+                            client_task_runner_, weak_this_));
   }
 }
 
 void VideoDecoderPipeline::CreateAndInitializeVD(
     base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs,
-    VideoDecoderConfig config,
-    bool low_delay,
-    CdmContext* cdm_context,
-    WaitingCB waiting_cb) {
+    VideoDecoderConfig config) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
   DCHECK(init_cb_);
   DCHECK(!decoder_);
@@ -189,26 +194,20 @@
   if (!decoder_) {
     DVLOGF(2) << "Failed to create VideoDecoder.";
     used_create_vd_func_ = nullptr;
-    return CreateAndInitializeVD(std::move(create_vd_funcs), config, low_delay,
-                                 cdm_context, std::move(waiting_cb));
+    return CreateAndInitializeVD(std::move(create_vd_funcs), config);
   }
 
   decoder_->Initialize(
-      config, low_delay, cdm_context,
+      config,
       base::BindOnce(&VideoDecoderPipeline::OnInitializeDone, weak_this_,
-                     std::move(create_vd_funcs), config, low_delay, cdm_context,
-                     waiting_cb),
+                     std::move(create_vd_funcs), config),
       base::BindRepeating(&VideoDecoderPipeline::OnFrameDecodedThunk,
-                          client_task_runner_, weak_this_),
-      waiting_cb);
+                          client_task_runner_, weak_this_));
 }
 
 void VideoDecoderPipeline::OnInitializeDone(
     base::queue<VideoDecoderPipeline::CreateVDFunc> create_vd_funcs,
     VideoDecoderConfig config,
-    bool low_delay,
-    CdmContext* cdm_context,
-    WaitingCB waiting_cb,
     bool success) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
   DCHECK(init_cb_);
@@ -223,8 +222,7 @@
   DVLOGF(3) << "Reset VD, try the next create function.";
   decoder_ = nullptr;
   used_create_vd_func_ = nullptr;
-  CreateAndInitializeVD(std::move(create_vd_funcs), config, low_delay,
-                        cdm_context, std::move(waiting_cb));
+  CreateAndInitializeVD(std::move(create_vd_funcs), config);
 }
 
 void VideoDecoderPipeline::Reset(base::OnceClosure closure) {
diff --git a/media/gpu/linux/video_decoder_pipeline.h b/media/gpu/linux/video_decoder_pipeline.h
index 85014b1..32aa613 100644
--- a/media/gpu/linux/video_decoder_pipeline.h
+++ b/media/gpu/linux/video_decoder_pipeline.h
@@ -26,8 +26,66 @@
 
 class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder {
  public:
+  // An interface that defines methods to operate on video decoder components
+  // inside the VideoDecoderPipeline. The interface is similar to
+  // media::VideoDecoder. The reason not using media::VideoDecoder is that some
+  // decoders might need to attach an image processor to perform frame
+  // processing, the output of VideoDecoder is not suitable when the
+  // intermediate output cannot be rendered by the compositor.
+  //
+  // Note: All methods and callbacks should be called on the same sequence.
+  class MEDIA_GPU_EXPORT DecoderInterface {
+   public:
+    using InitCB = base::OnceCallback<void(bool success)>;
+    // TODO(crbug.com/998413): Replace VideoFrame to GpuMemoryBuffer-based
+    // instance.
+    using OutputCB = base::RepeatingCallback<void(scoped_refptr<VideoFrame>)>;
+    using DecodeCB = base::OnceCallback<void(DecodeStatus)>;
+
+    DecoderInterface() = default;
+    virtual ~DecoderInterface() = default;
+
+    // Initializes a DecoderInterface with the given |config|, executing the
+    // |init_cb| upon completion. |output_cb| is called for each output frame
+    // decoded by Decode().
+    //
+    // Note:
+    // 1) DecoderInterface will be reinitialized if it was initialized before.
+    // 2) This method should not be called during pending decode or reset.
+    // 3) No DecoderInterface calls should be made before |init_cb| is executed
+    //    successfully.
+    // TODO(akahuang): Add an error notification method to handle misused case.
+    // 4) |init_cb| may be called before this returns.
+    virtual void Initialize(const VideoDecoderConfig& config,
+                            InitCB init_cb,
+                            const OutputCB& output_cb) = 0;
+
+    // Requests a |buffer| to be decoded. The decode result will be returned via
+    // |decode_cb|.
+    //
+    // After decoding is finished the decoder calls |output_cb| specified in
+    // Initialize() for each decoded frame. |output_cb| may be called before or
+    // after |decode_cb|, including before Decode() returns.
+    //
+    // If |buffer| is an EOS buffer then the decoder must be flushed, i.e.
+    // |output_cb| must be called for each frame pending in the queue and
+    // |decode_cb| must be called after that. Callers will not call Decode()
+    // again until after the flush completes.
+    // TODO(akahuang): Add an error notification method to handle misused case.
+    virtual void Decode(scoped_refptr<DecoderBuffer> buffer,
+                        DecodeCB decode_cb) = 0;
+
+    // Resets decoder state. All pending Decode() requests will be finished or
+    // aborted before |closure| is called.
+    // Note: No VideoDecoder calls should be made before |closure| is executed.
+    // TODO(akahuang): Add an error notification method to handle misused case.
+    virtual void Reset(base::OnceClosure closure) = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(DecoderInterface);
+  };
+
   // Function signature for creating VideoDecoder.
-  using CreateVDFunc = std::unique_ptr<VideoDecoder> (*)(
+  using CreateVDFunc = std::unique_ptr<DecoderInterface> (*)(
       scoped_refptr<base::SequencedTaskRunner>,
       scoped_refptr<base::SequencedTaskRunner>,
       base::RepeatingCallback<DmabufVideoFramePool*()>);
@@ -72,15 +130,9 @@
   void DestroyTask();
 
   void CreateAndInitializeVD(base::queue<CreateVDFunc> create_vd_funcs,
-                             VideoDecoderConfig config,
-                             bool low_delay,
-                             CdmContext* cdm_context,
-                             WaitingCB waiting_cb);
+                             VideoDecoderConfig config);
   void OnInitializeDone(base::queue<CreateVDFunc> create_vd_funcs,
                         VideoDecoderConfig config,
-                        bool low_delay,
-                        CdmContext* cdm_context,
-                        WaitingCB waiting_cb,
                         bool success);
 
   void OnDecodeDone(bool eos_buffer, DecodeCB decode_cb, DecodeStatus status);
@@ -116,12 +168,12 @@
   // |client_task_runner_|.
   std::unique_ptr<VideoFrameConverter> frame_converter_;
 
-  // The callback to get a list of function for creating VideoDecoder.
+  // The callback to get a list of function for creating DecoderInterface.
   GetCreateVDFunctionsCB get_create_vd_functions_cb_;
 
   // The current video decoder implementation. Valid after initialization is
   // successfully done.
-  std::unique_ptr<VideoDecoder> decoder_;
+  std::unique_ptr<DecoderInterface> decoder_;
   // The create function of |decoder_|. nullptr iff |decoder_| is nullptr.
   CreateVDFunc used_create_vd_func_ = nullptr;
 
@@ -132,6 +184,9 @@
   DecodeCB client_flush_cb_;
   base::OnceClosure client_reset_cb_;
 
+  // True if the decoder needs bitstream conversion before decoding.
+  bool needs_bitstream_conversion_ = false;
+
   // Set to true when any unexpected error occurs.
   bool has_error_ = false;
 
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
index 3f33618..a43bd70b 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
@@ -1501,9 +1501,17 @@
 
   auto output_gmb_handle = CreateGpuMemoryBufferHandle(output_frame.get());
   DCHECK(!output_gmb_handle.is_null());
+
+  // In this case, we use the R_8 buffer with height == 1 to represent a data
+  // container. As a result, we use plane.stride as size of the data here since
+  // plane.size might be larger due to height alignment.
+  const gfx::Size output_gmb_buffer_size(
+      base::checked_cast<int32_t>(output_frame->layout().planes()[0].stride),
+      1);
+
   auto output_gmb_buffer =
       gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
-          std::move(output_gmb_handle), output_frame->coded_size(),
+          std::move(output_gmb_handle), output_gmb_buffer_size,
           gfx::BufferFormat::R_8, gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
           base::DoNothing());
 
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
index d4a716a..aca260b0 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -40,7 +40,8 @@
 }  // namespace
 
 // static
-std::unique_ptr<VideoDecoder> V4L2SliceVideoDecoder::Create(
+std::unique_ptr<VideoDecoderPipeline::DecoderInterface>
+V4L2SliceVideoDecoder::Create(
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
     GetFramePoolCB get_pool_cb) {
@@ -53,9 +54,10 @@
     return nullptr;
   }
 
-  return base::WrapUnique<VideoDecoder>(new V4L2SliceVideoDecoder(
-      std::move(client_task_runner), std::move(decoder_task_runner),
-      std::move(device), std::move(get_pool_cb)));
+  return base::WrapUnique<VideoDecoderPipeline::DecoderInterface>(
+      new V4L2SliceVideoDecoder(std::move(client_task_runner),
+                                std::move(decoder_task_runner),
+                                std::move(device), std::move(get_pool_cb)));
 }
 
 // static
@@ -87,52 +89,20 @@
 }
 
 V4L2SliceVideoDecoder::~V4L2SliceVideoDecoder() {
-  // We might be called from either the client or the decoder sequence.
-  DETACH_FROM_SEQUENCE(client_sequence_checker_);
-  DETACH_FROM_SEQUENCE(decoder_sequence_checker_);
-  VLOGF(2);
-}
-
-std::string V4L2SliceVideoDecoder::GetDisplayName() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return "V4L2SliceVideoDecoder";
-}
-
-bool V4L2SliceVideoDecoder::IsPlatformDecoder() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return true;
-}
-
-int V4L2SliceVideoDecoder::GetMaxDecodeRequests() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return 4;
-}
-
-bool V4L2SliceVideoDecoder::NeedsBitstreamConversion() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return needs_bitstream_conversion_;
-}
-
-bool V4L2SliceVideoDecoder::CanReadWithoutStalling() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return frame_pool_ && !frame_pool_->IsExhausted();
-}
-
-void V4L2SliceVideoDecoder::Destroy() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
   VLOGF(2);
 
+  // We need this event to synchronously destroy this instance.
+  // TODO(akahuang): Remove this event by manipulating this instance only on one
+  // sequence.
+  base::WaitableEvent event;
   decoder_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&V4L2SliceVideoDecoder::DestroyTask, weak_this_));
+      FROM_HERE, base::BindOnce(&V4L2SliceVideoDecoder::DestroyTask, weak_this_,
+                                base::Unretained(&event)));
+  event.Wait();
 }
 
-void V4L2SliceVideoDecoder::DestroyTask() {
+void V4L2SliceVideoDecoder::DestroyTask(base::WaitableEvent* event) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
   DVLOGF(2);
 
@@ -155,29 +125,14 @@
 
   weak_this_factory_.InvalidateWeakPtrs();
 
-  delete this;
-  VLOGF(2) << "Destroyed";
+  event->Signal();
 }
 
 void V4L2SliceVideoDecoder::Initialize(const VideoDecoderConfig& config,
-                                       bool low_delay,
-                                       CdmContext* cdm_context,
                                        InitCB init_cb,
-                                       const OutputCB& output_cb,
-                                       const WaitingCB& /* waiting_cb */) {
+                                       const OutputCB& output_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-  VLOGF(2) << "config: " << config.AsHumanReadableString();
-
-  if (!config.IsValidConfig()) {
-    VLOGF(1) << "config is not valid";
-    std::move(init_cb).Run(false);
-    return;
-  }
-  if (cdm_context) {
-    VLOGF(1) << "cdm_context is not supported.";
-    std::move(init_cb).Run(false);
-    return;
-  }
+  DCHECK(config.IsValidConfig());
 
   decoder_task_runner_->PostTask(
       FROM_HERE,
@@ -246,7 +201,6 @@
     return;
   }
 
-  needs_bitstream_conversion_ = (config.codec() == kCodecH264);
   pixel_aspect_ratio_ = config.GetPixelAspectRatio();
 
   // Create Input/Output V4L2Queue
@@ -661,10 +615,6 @@
     frame = std::move(wrapped_frame);
   }
 
-  // Although the document of VideoDecoder says "should run |output_cb| as soon
-  // as possible (without thread trampolining)", MojoVideoDecoderService still
-  // assumes the callback is called at original thread.
-  // TODO(akahuang): call the callback directly after updating MojoVDService.
   client_task_runner_->PostTask(FROM_HERE,
                                 base::BindOnce(output_cb_, std::move(frame)));
 }
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.h b/media/gpu/v4l2/v4l2_slice_video_decoder.h
index 5f36a12..89afd737 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.h
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.h
@@ -21,11 +21,12 @@
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "media/base/video_decoder.h"
 #include "media/base/video_frame_layout.h"
 #include "media/base/video_types.h"
+#include "media/gpu/linux/video_decoder_pipeline.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "media/gpu/v4l2/v4l2_video_decoder_backend.h"
@@ -36,7 +37,7 @@
 class DmabufVideoFramePool;
 
 class MEDIA_GPU_EXPORT V4L2SliceVideoDecoder
-    : public VideoDecoder,
+    : public VideoDecoderPipeline::DecoderInterface,
       public V4L2VideoDecoderBackend::Client {
  public:
   using GetFramePoolCB = base::RepeatingCallback<DmabufVideoFramePool*()>;
@@ -44,26 +45,17 @@
   // Create V4L2SliceVideoDecoder instance. The success of the creation doesn't
   // ensure V4L2SliceVideoDecoder is available on the device. It will be
   // determined in Initialize().
-  static std::unique_ptr<VideoDecoder> Create(
+  static std::unique_ptr<VideoDecoderPipeline::DecoderInterface> Create(
       scoped_refptr<base::SequencedTaskRunner> client_task_runner,
       scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
       GetFramePoolCB get_pool_cb);
 
   static SupportedVideoDecoderConfigs GetSupportedConfigs();
 
-  // VideoDecoder implementation.
-  std::string GetDisplayName() const override;
-  bool IsPlatformDecoder() const override;
-  int GetMaxDecodeRequests() const override;
-  bool NeedsBitstreamConversion() const override;
-  bool CanReadWithoutStalling() const override;
-
+  // VideoDecoderPipeline::DecoderInterface implementation.
   void Initialize(const VideoDecoderConfig& config,
-                  bool low_delay,
-                  CdmContext* cdm_context,
                   InitCB init_cb,
-                  const OutputCB& output_cb,
-                  const WaitingCB& waiting_cb) override;
+                  const OutputCB& output_cb) override;
   void Reset(base::OnceClosure closure) override;
   void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
 
@@ -89,7 +81,6 @@
       scoped_refptr<V4L2Device> device,
       GetFramePoolCB get_pool_cb);
   ~V4L2SliceVideoDecoder() override;
-  void Destroy() override;
 
   enum class State {
     // Initial state. Transitions to |kDecoding| if Initialize() is successful,
@@ -147,7 +138,7 @@
       const gfx::Rect& visible_rect);
 
   // Destroy on decoder thread.
-  void DestroyTask();
+  void DestroyTask(base::WaitableEvent* event);
   // Reset on decoder thread.
   void ResetTask(base::OnceClosure closure);
 
@@ -177,8 +168,8 @@
   GetFramePoolCB get_pool_cb_;
   DmabufVideoFramePool* frame_pool_ = nullptr;
 
-  // Client task runner. All public methods of VideoDecoder interface are
-  // executed at this task runner.
+  // Client task runner. All public methods of
+  // VideoDecoderPipeline::DecoderInterface are executed at this task runner.
   const scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
   // Thread to communicate with the device on. Most of internal methods and data
   // members are manipulated on this thread.
@@ -201,9 +192,6 @@
 
   BitstreamIdGenerator bitstream_id_generator_;
 
-  // True if the decoder needs bitstream conversion before decoding.
-  bool needs_bitstream_conversion_ = false;
-
   SEQUENCE_CHECKER(client_sequence_checker_);
   SEQUENCE_CHECKER(decoder_sequence_checker_);
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index f697e0a3..f965406 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -243,8 +243,13 @@
   // size, where buffer_size can be obtained from the first plane's size.
   auto output_gmb_handle = CreateGpuMemoryBufferHandle(output_frame.get());
   DCHECK(!output_gmb_handle.is_null());
+
+  // In this case, we use the R_8 buffer with height == 1 to represent a data
+  // container. As a result, we use plane.stride as size of the data here since
+  // plane.size might be larger due to height alignment.
   const gfx::Size output_gmb_buffer_size(
-      base::checked_cast<int32_t>(output_frame->layout().planes()[0].size), 1);
+      base::checked_cast<int32_t>(output_frame->layout().planes()[0].stride),
+      1);
   auto output_gmb_buffer =
       gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
           std::move(output_gmb_handle), output_gmb_buffer_size,
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 37b7b40b..7dfbaf0 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -27,9 +27,6 @@
 
 namespace {
 
-// Maximum number of parallel decode requests.
-constexpr int kMaxDecodeRequests = 4;
-
 // Size of the timestamp cache, needs to be large enough for frame-reordering.
 constexpr size_t kTimestampCacheSize = 128;
 
@@ -66,13 +63,15 @@
 VaapiVideoDecoder::DecodeTask::DecodeTask(DecodeTask&&) = default;
 
 // static
-std::unique_ptr<VideoDecoder> VaapiVideoDecoder::Create(
+std::unique_ptr<VideoDecoderPipeline::DecoderInterface>
+VaapiVideoDecoder::Create(
     scoped_refptr<base::SequencedTaskRunner> client_task_runner,
     scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
     GetFramePoolCB get_pool_cb) {
-  return base::WrapUnique<VideoDecoder>(new VaapiVideoDecoder(
-      std::move(client_task_runner), std::move(decoder_task_runner),
-      std::move(get_pool_cb)));
+  return base::WrapUnique<VideoDecoderPipeline::DecoderInterface>(
+      new VaapiVideoDecoder(std::move(client_task_runner),
+                            std::move(decoder_task_runner),
+                            std::move(get_pool_cb)));
 }
 
 // static
@@ -96,62 +95,42 @@
   weak_this_ = weak_this_factory_.GetWeakPtr();
 }
 
-VaapiVideoDecoder::~VaapiVideoDecoder() {}
-
-std::string VaapiVideoDecoder::GetDisplayName() const {
+VaapiVideoDecoder::~VaapiVideoDecoder() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  VLOGF(2);
 
-  return "VaapiVideoDecoder";
+  // We need this event to synchronously destroy this instance.
+  // TODO(akahuang): Remove this event by manipulating this instance only on one
+  // sequence.
+  base::WaitableEvent event;
+  decoder_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&VaapiVideoDecoder::DestroyTask, weak_this_,
+                                base::Unretained(&event)));
+  event.Wait();
 }
 
-bool VaapiVideoDecoder::IsPlatformDecoder() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+void VaapiVideoDecoder::DestroyTask(base::WaitableEvent* event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
+  DVLOGF(2);
 
-  return true;
-}
+  // Abort all currently scheduled decode tasks.
+  ClearDecodeTaskQueue(DecodeStatus::ABORTED);
 
-bool VaapiVideoDecoder::NeedsBitstreamConversion() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  if (decoder_) {
+    decoder_->Reset();
+    decoder_ = nullptr;
+  }
 
-  return needs_bitstream_conversion_;
-}
+  weak_this_factory_.InvalidateWeakPtrs();
 
-bool VaapiVideoDecoder::CanReadWithoutStalling() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return frame_pool_ && !frame_pool_->IsExhausted();
-}
-
-int VaapiVideoDecoder::GetMaxDecodeRequests() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-
-  return kMaxDecodeRequests;
+  event->Signal();
 }
 
 void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config,
-                                   bool low_delay,
-                                   CdmContext* cdm_context,
                                    InitCB init_cb,
-                                   const OutputCB& output_cb,
-                                   const WaitingCB& /*waiting_cb*/) {
+                                   const OutputCB& output_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-  VLOGF(2) << "config: " << config.AsHumanReadableString();
-
-  if (cdm_context) {
-    VLOGF(1) << "cdm_context is not supported.";
-    std::move(init_cb).Run(false);
-    return;
-  }
-  if (!config.IsValidConfig()) {
-    VLOGF(1) << "config is not valid";
-    std::move(init_cb).Run(false);
-    return;
-  }
-  if (config.is_encrypted()) {
-    VLOGF(1) << "Encrypted streams are not supported for this VD";
-    std::move(init_cb).Run(false);
-    return;
-  }
+  DCHECK(config.IsValidConfig());
 
   decoder_task_runner_->PostTask(
       FROM_HERE,
@@ -175,8 +154,8 @@
   }
 
   // We expect the decoder to have released all output buffers (by the client
-  // triggering a flush or reset), even if the media::VideoDecoder API doesn't
-  // explicitly specify this.
+  // triggering a flush or reset), even if the
+  // VideoDecoderPipeline::DecoderInterface API doesn't explicitly specify this.
   DCHECK(output_frames_.empty());
 
   if (state_ != State::kUninitialized) {
@@ -219,7 +198,6 @@
                                   base::BindOnce(std::move(init_cb), false));
     return;
   }
-  needs_bitstream_conversion_ = (config.codec() == kCodecH264);
 
   // Get and initialize the frame pool.
   frame_pool_ = get_pool_cb_.Run();
@@ -235,32 +213,6 @@
                                 base::BindOnce(std::move(init_cb), true));
 }
 
-void VaapiVideoDecoder::Destroy() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
-  VLOGF(2);
-
-  decoder_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&VaapiVideoDecoder::DestroyTask, weak_this_));
-}
-
-void VaapiVideoDecoder::DestroyTask() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
-  DVLOGF(2);
-
-  // Abort all currently scheduled decode tasks.
-  ClearDecodeTaskQueue(DecodeStatus::ABORTED);
-
-  if (decoder_) {
-    decoder_->Reset();
-    decoder_ = nullptr;
-  }
-
-  weak_this_factory_.InvalidateWeakPtrs();
-
-  delete this;
-  VLOGF(2) << "Destroying VAAPI VD done";
-}
-
 void VaapiVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
                                DecodeCB decode_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
@@ -487,9 +439,6 @@
     video_frame = std::move(wrapped_frame);
   }
 
-  // TODO(dstaessens): MojoVideoDecoderService expects the |output_cb_| to be
-  // called on the client task runner, even though media::VideoDecoder states
-  // frames should be output without any thread jumping.
   client_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(output_cb_, std::move(video_frame)));
 }
diff --git a/media/gpu/vaapi/vaapi_video_decoder.h b/media/gpu/vaapi/vaapi_video_decoder.h
index 3c5f8121..7771889 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.h
+++ b/media/gpu/vaapi/vaapi_video_decoder.h
@@ -20,12 +20,13 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "media/base/video_codecs.h"
-#include "media/base/video_decoder.h"
 #include "media/base/video_frame_layout.h"
 #include "media/gpu/decode_surface_handler.h"
+#include "media/gpu/linux/video_decoder_pipeline.h"
 #include "media/video/supported_video_decoder_config.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -38,30 +39,22 @@
 class VideoFrame;
 class VASurface;
 
-class VaapiVideoDecoder : public media::VideoDecoder,
+class VaapiVideoDecoder : public VideoDecoderPipeline::DecoderInterface,
                           public DecodeSurfaceHandler<VASurface> {
  public:
   using GetFramePoolCB = base::RepeatingCallback<DmabufVideoFramePool*()>;
 
-  static std::unique_ptr<VideoDecoder> Create(
+  static std::unique_ptr<VideoDecoderPipeline::DecoderInterface> Create(
       scoped_refptr<base::SequencedTaskRunner> client_task_runner,
       scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
       GetFramePoolCB get_pool);
 
   static SupportedVideoDecoderConfigs GetSupportedConfigs();
 
-  // media::VideoDecoder implementation.
-  std::string GetDisplayName() const override;
-  bool IsPlatformDecoder() const override;
-  bool NeedsBitstreamConversion() const override;
-  bool CanReadWithoutStalling() const override;
-  int GetMaxDecodeRequests() const override;
+  // VideoDecoderPipeline::DecoderInterface implementation.
   void Initialize(const VideoDecoderConfig& config,
-                  bool low_delay,
-                  CdmContext* cdm_context,
                   InitCB init_cb,
-                  const OutputCB& output_cb,
-                  const WaitingCB& waiting_cb) override;
+                  const OutputCB& output_cb) override;
   void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
   void Reset(base::OnceClosure reset_cb) override;
 
@@ -102,16 +95,12 @@
       GetFramePoolCB get_pool);
   ~VaapiVideoDecoder() override;
 
-  // Destroy the VAAPIVideoDecoder, aborts pending decode requests and blocks
-  // until destroyed.
-  void Destroy() override;
-
   // Initialize the VAAPI video decoder on the decoder thread.
   void InitializeTask(const VideoDecoderConfig& config,
                       InitCB init_cb,
                       OutputCB output_cb);
   // Destroy the VAAPI video decoder on the decoder thread.
-  void DestroyTask();
+  void DestroyTask(base::WaitableEvent* event);
 
   // Queue a decode task on the decoder thread. If the decoder is currently
   // waiting for input buffers decoding will be started.
@@ -169,8 +158,6 @@
 
   // The video stream's profile.
   VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;
-  // True if the decoder needs bitstream conversion before decoding.
-  bool needs_bitstream_conversion_ = false;
 
   // Output frame properties.
   base::Optional<VideoFrameLayout> frame_layout_;
diff --git a/media/mojo/clients/mojo_audio_decoder.cc b/media/mojo/clients/mojo_audio_decoder.cc
index 82373666..46c9db6 100644
--- a/media/mojo/clients/mojo_audio_decoder.cc
+++ b/media/mojo/clients/mojo_audio_decoder.cc
@@ -25,8 +25,7 @@
     : task_runner_(task_runner),
       pending_remote_decoder_(std::move(remote_decoder)),
       writer_capacity_(
-          GetDefaultDecoderBufferConverterCapacity(DemuxerStream::AUDIO)),
-      client_binding_(this) {
+          GetDefaultDecoderBufferConverterCapacity(DemuxerStream::AUDIO)) {
   DVLOG(1) << __func__;
 }
 
@@ -150,10 +149,7 @@
   remote_decoder_.set_disconnect_handler(
       base::Bind(&MojoAudioDecoder::OnConnectionError, base::Unretained(this)));
 
-  mojom::AudioDecoderClientAssociatedPtrInfo client_ptr_info;
-  client_binding_.Bind(mojo::MakeRequest(&client_ptr_info));
-
-  remote_decoder_->Construct(std::move(client_ptr_info));
+  remote_decoder_->Construct(client_receiver_.BindNewEndpointAndPassRemote());
 }
 
 void MojoAudioDecoder::OnBufferDecoded(mojom::AudioBufferPtr buffer) {
diff --git a/media/mojo/clients/mojo_audio_decoder.h b/media/mojo/clients/mojo_audio_decoder.h
index eafee14..0fbe21b 100644
--- a/media/mojo/clients/mojo_audio_decoder.h
+++ b/media/mojo/clients/mojo_audio_decoder.h
@@ -12,8 +12,7 @@
 #include "media/base/audio_decoder.h"
 #include "media/mojo/mojom/audio_decoder.mojom.h"
 #include "media/mojo/mojom/media_types.mojom.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -82,8 +81,8 @@
 
   uint32_t writer_capacity_ = 0;
 
-  // Binding for AudioDecoderClient, bound to the |task_runner_|.
-  mojo::AssociatedBinding<AudioDecoderClient> client_binding_;
+  // Receiver for AudioDecoderClient, bound to the |task_runner_|.
+  mojo::AssociatedReceiver<AudioDecoderClient> client_receiver_{this};
 
   InitCB init_cb_;
   OutputCB output_cb_;
diff --git a/media/mojo/mojom/audio_decoder.mojom b/media/mojo/mojom/audio_decoder.mojom
index 204bf4e1..73b6ac0 100644
--- a/media/mojo/mojom/audio_decoder.mojom
+++ b/media/mojo/mojom/audio_decoder.mojom
@@ -11,7 +11,7 @@
   //
   // TODO(sandersd): Rename to Initialize() if/when
   // media::AudioDecoder::Initialize() is renamed to Configure().
-  Construct(associated AudioDecoderClient client);
+  Construct(pending_associated_remote<AudioDecoderClient> client);
 
   // Initializes the AudioDecoder with the audio codec configuration and CDM id.
   // For the unencrypted streams the |cdm_id| is ignored. Executed the callback
diff --git a/media/mojo/services/mojo_audio_decoder_service.cc b/media/mojo/services/mojo_audio_decoder_service.cc
index 2434f71..2bfa5ace 100644
--- a/media/mojo/services/mojo_audio_decoder_service.cc
+++ b/media/mojo/services/mojo_audio_decoder_service.cc
@@ -27,7 +27,7 @@
 MojoAudioDecoderService::~MojoAudioDecoderService() = default;
 
 void MojoAudioDecoderService::Construct(
-    mojom::AudioDecoderClientAssociatedPtrInfo client) {
+    mojo::PendingAssociatedRemote<mojom::AudioDecoderClient> client) {
   DVLOG(1) << __func__;
   client_.Bind(std::move(client));
 }
diff --git a/media/mojo/services/mojo_audio_decoder_service.h b/media/mojo/services/mojo_audio_decoder_service.h
index 43f977c..872f9f7 100644
--- a/media/mojo/services/mojo_audio_decoder_service.h
+++ b/media/mojo/services/mojo_audio_decoder_service.h
@@ -14,6 +14,8 @@
 #include "media/base/audio_decoder.h"
 #include "media/mojo/mojom/audio_decoder.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 
 namespace media {
 
@@ -29,7 +31,8 @@
   ~MojoAudioDecoderService() final;
 
   // mojom::AudioDecoder implementation
-  void Construct(mojom::AudioDecoderClientAssociatedPtrInfo client) final;
+  void Construct(
+      mojo::PendingAssociatedRemote<mojom::AudioDecoderClient> client) final;
   void Initialize(const AudioDecoderConfig& config,
                   int32_t cdm_id,
                   InitializeCallback callback) final;
@@ -69,7 +72,7 @@
   MojoCdmServiceContext* const mojo_cdm_service_context_ = nullptr;
 
   // The destination for the decoded buffers.
-  mojom::AudioDecoderClientAssociatedPtr client_;
+  mojo::AssociatedRemote<mojom::AudioDecoderClient> client_;
 
   // Holds the CdmContextRef to keep the CdmContext alive for the lifetime of
   // the |decoder_|.
diff --git a/net/docs/code-patterns.md b/net/docs/code-patterns.md
index 2b89459..97ee64a 100644
--- a/net/docs/code-patterns.md
+++ b/net/docs/code-patterns.md
@@ -210,7 +210,7 @@
                result = DoLoop(result);
 
                if (result != ERR_IO_PENDING && !callback_.is_null())
-                 base::ResetAndReturn(&callback_).Run(result);
+                 std::move(callback_).Run(result);
              }
     
 *   The DoLoop pattern has no concept of different events arriving for
diff --git a/net/docs/life-of-a-feature.md b/net/docs/life-of-a-feature.md
index 5d5b783..54ddd50b 100644
--- a/net/docs/life-of-a-feature.md
+++ b/net/docs/life-of-a-feature.md
@@ -224,8 +224,7 @@
 further expanded upon in [Code Patterns](code-patterns.md), features and
 changes should be designed such that any callback invocation is the last
 bit of code executed, and that the callback is accessed via the stack (such
-as through the use of either `base::ResetAndReturn(callback_).Run()` or
-`std::move(callback_).Run()`.
+as through the use of `std::move(callback_).Run()`.
 
 ### Specs: What Are They Good For
 
diff --git a/net/http/http_auth_cache.cc b/net/http/http_auth_cache.cc
index dafca1a7..d622aec 100644
--- a/net/http/http_auth_cache.cc
+++ b/net/http/http_auth_cache.cc
@@ -65,29 +65,38 @@
 
 namespace net {
 
-HttpAuthCache::HttpAuthCache() = default;
+HttpAuthCache::HttpAuthCache(bool key_server_entries_by_network_isolation_key)
+    : key_server_entries_by_network_isolation_key_(
+          key_server_entries_by_network_isolation_key) {}
 
 HttpAuthCache::~HttpAuthCache() = default;
 
 // Performance: O(logN+n), where N is the total number of entries, n is the
-// number of realm entries for the given origin.
-HttpAuthCache::Entry* HttpAuthCache::Lookup(const GURL& origin,
-                                            HttpAuth::Target target,
-                                            const std::string& realm,
-                                            HttpAuth::Scheme scheme) {
-  EntryMap::iterator entry_it = LookupEntryIt(origin, target, realm, scheme);
+// number of realm entries for the given origin, target, and with a matching
+// NetworkIsolationKey.
+HttpAuthCache::Entry* HttpAuthCache::Lookup(
+    const GURL& origin,
+    HttpAuth::Target target,
+    const std::string& realm,
+    HttpAuth::Scheme scheme,
+    const NetworkIsolationKey& network_isolation_key) {
+  EntryMap::iterator entry_it =
+      LookupEntryIt(origin, target, realm, scheme, network_isolation_key);
   if (entry_it == entries_.end())
     return nullptr;
   return &(entry_it->second);
 }
 
 // Performance: O(logN+n*m), where N is the total number of entries, n is the
-// number of realm entries for the given origin and NIK, m is the number of path
-// entries per realm. Both n and m are expected to be small; m is kept small
-// because AddPath() only keeps the shallowest entry.
-HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin,
-                                                  HttpAuth::Target target,
-                                                  const std::string& path) {
+// number of realm entries for the given origin, target, and
+// NetworkIsolationKey, m is the number of path entries per realm. Both n and m
+// are expected to be small; m is kept small because AddPath() only keeps the
+// shallowest entry.
+HttpAuthCache::Entry* HttpAuthCache::LookupByPath(
+    const GURL& origin,
+    HttpAuth::Target target,
+    const NetworkIsolationKey& network_isolation_key,
+    const std::string& path) {
 #if DCHECK_IS_ON()
   CheckOriginIsValid(origin);
   CheckPathIsValid(path);
@@ -100,7 +109,9 @@
   std::string parent_dir = GetParentDirectory(path);
 
   // Linear scan through the <scheme, realm> entries for the given origin.
-  auto entry_range = entries_.equal_range(EntryMapKey(origin, target));
+  auto entry_range = entries_.equal_range(
+      EntryMapKey(origin, target, network_isolation_key,
+                  key_server_entries_by_network_isolation_key_));
   auto best_match_it = entries_.end();
   size_t best_match_length = 0;
   for (auto it = entry_range.first; it != entry_range.second; ++it) {
@@ -121,13 +132,15 @@
   return nullptr;
 }
 
-HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin,
-                                         HttpAuth::Target target,
-                                         const std::string& realm,
-                                         HttpAuth::Scheme scheme,
-                                         const std::string& auth_challenge,
-                                         const AuthCredentials& credentials,
-                                         const std::string& path) {
+HttpAuthCache::Entry* HttpAuthCache::Add(
+    const GURL& origin,
+    HttpAuth::Target target,
+    const std::string& realm,
+    HttpAuth::Scheme scheme,
+    const NetworkIsolationKey& network_isolation_key,
+    const std::string& auth_challenge,
+    const AuthCredentials& credentials,
+    const std::string& path) {
 #if DCHECK_IS_ON()
   CheckOriginIsValid(origin);
   CheckPathIsValid(path);
@@ -136,23 +149,29 @@
   base::TimeTicks now_ticks = tick_clock_->NowTicks();
 
   // Check for existing entry (we will re-use it if present).
-  HttpAuthCache::Entry* entry = Lookup(origin, target, realm, scheme);
+  HttpAuthCache::Entry* entry =
+      Lookup(origin, target, realm, scheme, network_isolation_key);
   if (!entry) {
     bool evicted = false;
     // Failsafe to prevent unbounded memory growth of the cache.
     //
-    // Data collected in June of 2019 indicate that the eviction rate is at
-    // around 0.05%. I.e. 0.05% of the time the number of entries in the cache
-    // exceed kMaxNumRealmEntries. The evicted entry is roughly half an hour old
-    // (median), and it's been around 25 minutes since its last use (median).
+    // Data was collected in June of 2019, before entries were keyed on either
+    // HttpAuth::Target or NetworkIsolationKey. That data indicated that the
+    // eviction rate was at around 0.05%. I.e. 0.05% of the time the number of
+    // entries in the cache exceed kMaxNumRealmEntries. The evicted entry is
+    // roughly half an hour old (median), and it's been around 25 minutes since
+    // its last use (median).
     if (entries_.size() >= kMaxNumRealmEntries) {
       DLOG(WARNING) << "Num auth cache entries reached limit -- evicting";
       EvictLeastRecentlyUsedEntry();
       evicted = true;
     }
-    entry = &(
-        entries_.emplace(std::make_pair(EntryMapKey(origin, target), Entry()))
-            ->second);
+    entry = &(entries_
+                  .emplace(std::make_pair(
+                      EntryMapKey(origin, target, network_isolation_key,
+                                  key_server_entries_by_network_isolation_key_),
+                      Entry()))
+                  ->second);
     entry->origin_ = origin;
     entry->realm_ = realm;
     entry->scheme_ = scheme;
@@ -253,8 +272,10 @@
                            HttpAuth::Target target,
                            const std::string& realm,
                            HttpAuth::Scheme scheme,
+                           const NetworkIsolationKey& network_isolation_key,
                            const AuthCredentials& credentials) {
-  EntryMap::iterator entry_it = LookupEntryIt(origin, target, realm, scheme);
+  EntryMap::iterator entry_it =
+      LookupEntryIt(origin, target, realm, scheme, network_isolation_key);
   if (entry_it == entries_.end())
     return false;
   Entry& entry = entry_it->second;
@@ -280,12 +301,15 @@
   entries_.clear();
 }
 
-bool HttpAuthCache::UpdateStaleChallenge(const GURL& origin,
-                                         HttpAuth::Target target,
-                                         const std::string& realm,
-                                         HttpAuth::Scheme scheme,
-                                         const std::string& auth_challenge) {
-  HttpAuthCache::Entry* entry = Lookup(origin, target, realm, scheme);
+bool HttpAuthCache::UpdateStaleChallenge(
+    const GURL& origin,
+    HttpAuth::Target target,
+    const std::string& realm,
+    HttpAuth::Scheme scheme,
+    const NetworkIsolationKey& network_isolation_key,
+    const std::string& auth_challenge) {
+  HttpAuthCache::Entry* entry =
+      Lookup(origin, target, realm, scheme, network_isolation_key);
   if (!entry)
     return false;
   entry->UpdateStaleChallenge(auth_challenge);
@@ -294,12 +318,16 @@
 }
 
 void HttpAuthCache::UpdateAllFrom(const HttpAuthCache& other) {
+  DCHECK_EQ(key_server_entries_by_network_isolation_key_,
+            other.key_server_entries_by_network_isolation_key());
+
   for (auto it = other.entries_.begin(); it != other.entries_.end(); ++it) {
     // Add an Entry with one of the original entry's paths.
     const Entry& e = it->second;
     DCHECK(e.paths_.size() > 0);
     Entry* entry = Add(e.origin(), it->first.target, e.realm(), e.scheme(),
-                       e.auth_challenge(), e.credentials(), e.paths_.back());
+                       it->first.network_isolation_key, e.auth_challenge(),
+                       e.credentials(), e.paths_.back());
     // Copy all other paths.
     for (auto it2 = std::next(e.paths_.rbegin()); it2 != e.paths_.rend(); ++it2)
       entry->AddPath(*it2);
@@ -308,14 +336,23 @@
   }
 }
 
-HttpAuthCache::EntryMapKey::EntryMapKey(const GURL& url,
-                                        HttpAuth::Target target)
-    : url(url), target(target) {}
+HttpAuthCache::EntryMapKey::EntryMapKey(
+    const GURL& url,
+    HttpAuth::Target target,
+    const NetworkIsolationKey& network_isolation_key,
+    bool key_server_entries_by_network_isolation_key)
+    : url(url),
+      target(target),
+      network_isolation_key(target == HttpAuth::AUTH_SERVER &&
+                                    key_server_entries_by_network_isolation_key
+                                ? network_isolation_key
+                                : NetworkIsolationKey()) {}
 
 HttpAuthCache::EntryMapKey::~EntryMapKey() = default;
 
 bool HttpAuthCache::EntryMapKey::operator<(const EntryMapKey& other) const {
-  return std::tie(url, target) < std::tie(other.url, other.target);
+  return std::tie(url, target, network_isolation_key) <
+         std::tie(other.url, other.target, other.network_isolation_key);
 }
 
 size_t HttpAuthCache::GetEntriesSizeForTesting() {
@@ -326,14 +363,17 @@
     const GURL& origin,
     HttpAuth::Target target,
     const std::string& realm,
-    HttpAuth::Scheme scheme) {
+    HttpAuth::Scheme scheme,
+    const NetworkIsolationKey& network_isolation_key) {
 #if DCHECK_IS_ON()
   CheckOriginIsValid(origin);
 #endif
 
   // Linear scan through the <scheme, realm> entries for the given origin and
   // NetworkIsolationKey.
-  auto entry_range = entries_.equal_range(EntryMapKey(origin, target));
+  auto entry_range = entries_.equal_range(
+      EntryMapKey(origin, target, network_isolation_key,
+                  key_server_entries_by_network_isolation_key_));
   for (auto it = entry_range.first; it != entry_range.second; ++it) {
     Entry& entry = it->second;
     DCHECK(entry.origin() == origin);
diff --git a/net/http/http_auth_cache.h b/net/http/http_auth_cache.h
index 44fda757..bb9b40c 100644
--- a/net/http/http_auth_cache.h
+++ b/net/http/http_auth_cache.h
@@ -17,6 +17,7 @@
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/http/http_auth.h"
 #include "url/gurl.h"
 
@@ -121,7 +122,10 @@
   enum { kMaxNumPathsPerRealmEntry = 10 };
   enum { kMaxNumRealmEntries = 20 };
 
-  HttpAuthCache();
+  // If |key_server_entries_by_network_isolation_key| is true, all
+  // HttpAuth::AUTH_SERVER operations are keyed by NetworkIsolationKey.
+  // Otherwise, NetworkIsolationKey arguments are ignored.
+  explicit HttpAuthCache(bool key_server_entries_by_network_isolation_key);
   ~HttpAuthCache();
 
   // Find the realm entry on server |origin| for realm |realm| and
@@ -136,7 +140,8 @@
   Entry* Lookup(const GURL& origin,
                 HttpAuth::Target target,
                 const std::string& realm,
-                HttpAuth::Scheme scheme);
+                HttpAuth::Scheme scheme,
+                const NetworkIsolationKey& network_isolation_key);
 
   // Find the entry on server |origin| whose protection space includes
   // |path|. This uses the assumption in RFC 2617 section 2 that deeper
@@ -149,6 +154,7 @@
   //   returns  - the matched entry or nullptr.
   Entry* LookupByPath(const GURL& origin,
                       HttpAuth::Target target,
+                      const NetworkIsolationKey& network_isolation_key,
                       const std::string& path);
 
   // Add an entry on server |origin| for realm |handler->realm()| and
@@ -166,6 +172,7 @@
              HttpAuth::Target target,
              const std::string& realm,
              HttpAuth::Scheme scheme,
+             const NetworkIsolationKey& network_isolation_key,
              const std::string& auth_challenge,
              const AuthCredentials& credentials,
              const std::string& path);
@@ -181,6 +188,7 @@
               HttpAuth::Target target,
               const std::string& realm,
               HttpAuth::Scheme scheme,
+              const NetworkIsolationKey& network_isolation_key,
               const AuthCredentials& credentials);
 
   // Clears cache entries added since |begin_time| or all entries if
@@ -199,9 +207,11 @@
                             HttpAuth::Target target,
                             const std::string& realm,
                             HttpAuth::Scheme scheme,
+                            const NetworkIsolationKey& network_isolation_key,
                             const std::string& auth_challenge);
 
-  // Copies all entries from |other| cache.
+  // Copies all entries from |other| cache. Both |this| and |other| must have
+  // the same key_server_entries_by_network_isolation_key() value.
   void UpdateAllFrom(const HttpAuthCache& other);
 
   size_t GetEntriesSizeForTesting();
@@ -210,15 +220,26 @@
   }
   void set_clock_for_testing(const base::Clock* clock) { clock_ = clock; }
 
+  bool key_server_entries_by_network_isolation_key() const {
+    return key_server_entries_by_network_isolation_key_;
+  }
+
  private:
   struct EntryMapKey {
-    EntryMapKey(const GURL& url, HttpAuth::Target target);
+    EntryMapKey(const GURL& url,
+                HttpAuth::Target target,
+                const NetworkIsolationKey& network_isolation_key,
+                bool key_server_entries_by_network_isolation_key);
     ~EntryMapKey();
 
     bool operator<(const EntryMapKey& other) const;
 
     GURL url;
     HttpAuth::Target target;
+    // Empty if |key_server_entries_by_network_isolation_key| is false, |target|
+    // is HttpAuth::AUTH_PROXY, or an empty NetworkIsolationKey is passed in to
+    // the EntryMap constructor.
+    NetworkIsolationKey network_isolation_key;
   };
 
   using EntryMap = std::multimap<EntryMapKey, Entry>;
@@ -226,12 +247,17 @@
   const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
   const base::Clock* clock_ = base::DefaultClock::GetInstance();
 
-  EntryMap::iterator LookupEntryIt(const GURL& origin,
-                                   HttpAuth::Target target,
-                                   const std::string& realm,
-                                   HttpAuth::Scheme scheme);
+  EntryMap::iterator LookupEntryIt(
+      const GURL& origin,
+      HttpAuth::Target target,
+      const std::string& realm,
+      HttpAuth::Scheme scheme,
+      const NetworkIsolationKey& network_isolation_key);
+
   void EvictLeastRecentlyUsedEntry();
 
+  const bool key_server_entries_by_network_isolation_key_;
+
   EntryMap entries_;
 
   DISALLOW_COPY_AND_ASSIGN(HttpAuthCache);
diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc
index 556bd7c..ec8b31dd 100644
--- a/net/http/http_auth_cache_unittest.cc
+++ b/net/http/http_auth_cache_unittest.cc
@@ -11,8 +11,10 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/http/http_auth_cache.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
 
 using base::ASCIIToUTF16;
 
@@ -46,67 +48,71 @@
 TEST(HttpAuthCacheTest, Basic) {
   GURL origin("http://www.google.com");
   GURL origin2("http://www.foobar.com");
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   HttpAuthCache::Entry* entry;
 
   // Add cache entries for 4 realms: "Realm1", "Realm2", "Realm3" and
   // "Realm4"
 
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "Basic realm=Realm1",
+            NetworkIsolationKey(), "Basic realm=Realm1",
             CreateASCIICredentials("realm1-user", "realm1-password"),
             "/foo/bar/index.html");
 
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "Basic realm=Realm2",
+            NetworkIsolationKey(), "Basic realm=Realm2",
             CreateASCIICredentials("realm2-user", "realm2-password"),
             "/foo2/index.html");
 
   cache.Add(
       origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
-      "Basic realm=Realm3",
+      NetworkIsolationKey(), "Basic realm=Realm3",
       CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"),
       std::string());
 
   cache.Add(
       origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
-      "Digest realm=Realm3",
+      NetworkIsolationKey(), "Digest realm=Realm3",
       CreateASCIICredentials("realm3-digest-user", "realm3-digest-password"),
       "/baz/index.html");
 
   cache.Add(
       origin, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC,
-      "Basic realm=Realm4",
+      NetworkIsolationKey(), "Basic realm=Realm4",
       CreateASCIICredentials("realm4-basic-user", "realm4-basic-password"),
       "/");
 
   cache.Add(origin2, HttpAuth::AUTH_SERVER, kRealm5,
-            HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm5",
+            HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+            "Basic realm=Realm5",
             CreateASCIICredentials("realm5-user", "realm5-password"), "/");
   cache.Add(
       origin2, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
-      "Basic realm=Realm3",
+      NetworkIsolationKey(), "Basic realm=Realm3",
       CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"),
       std::string());
 
   // There is no Realm5 in origin
   entry = cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm5,
-                       HttpAuth::AUTH_SCHEME_BASIC);
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   EXPECT_FALSE(entry);
 
   // While Realm3 does exist, the origin scheme is wrong.
-  entry = cache.Lookup(GURL("https://www.google.com"), HttpAuth::AUTH_SERVER,
-                       kRealm3, HttpAuth::AUTH_SCHEME_BASIC);
+  entry =
+      cache.Lookup(GURL("https://www.google.com"), HttpAuth::AUTH_SERVER,
+                   kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   EXPECT_FALSE(entry);
 
   // Realm, origin scheme ok, authentication scheme wrong
   entry = cache.Lookup(GURL("http://www.google.com"), HttpAuth::AUTH_SERVER,
-                       kRealm1, HttpAuth::AUTH_SCHEME_DIGEST);
+                       kRealm1, HttpAuth::AUTH_SCHEME_DIGEST,
+                       NetworkIsolationKey());
   EXPECT_FALSE(entry);
 
   // Valid lookup by origin, realm, scheme.
-  entry = cache.Lookup(GURL("http://www.google.com:80"), HttpAuth::AUTH_SERVER,
-                       kRealm3, HttpAuth::AUTH_SCHEME_BASIC);
+  entry =
+      cache.Lookup(GURL("http://www.google.com:80"), HttpAuth::AUTH_SERVER,
+                   kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
   EXPECT_EQ(kRealm3, entry->realm());
@@ -118,14 +124,15 @@
   // Same realm, scheme with different origins
   HttpAuthCache::Entry* entry2 =
       cache.Lookup(GURL("http://www.foobar.com:80"), HttpAuth::AUTH_SERVER,
-                   kRealm3, HttpAuth::AUTH_SCHEME_BASIC);
+                   kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry2);
   EXPECT_NE(entry, entry2);
 
   // Valid lookup by origin, realm, scheme when there's a duplicate
   // origin, realm in the cache
   entry = cache.Lookup(GURL("http://www.google.com:80"), HttpAuth::AUTH_SERVER,
-                       kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
+                       kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
+                       NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, entry->scheme());
   EXPECT_EQ(kRealm3, entry->realm());
@@ -137,7 +144,7 @@
 
   // Valid lookup by realm.
   entry = cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                       HttpAuth::AUTH_SCHEME_BASIC);
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
   EXPECT_EQ(kRealm2, entry->realm());
@@ -146,10 +153,12 @@
   EXPECT_EQ(ASCIIToUTF16("realm2-password"), entry->credentials().password());
 
   // Check that subpaths are recognized.
-  HttpAuthCache::Entry* p_realm2_entry = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC);
-  HttpAuthCache::Entry* p_realm4_entry = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC);
+  HttpAuthCache::Entry* p_realm2_entry =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
+                   HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
+  HttpAuthCache::Entry* p_realm4_entry =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
+                   HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   EXPECT_TRUE(p_realm2_entry);
   EXPECT_TRUE(p_realm4_entry);
   HttpAuthCache::Entry realm2_entry = *p_realm2_entry;
@@ -157,53 +166,68 @@
   // Realm4 applies to '/' and Realm2 applies to '/foo2/'.
   // LookupByPath() should return the closest enclosing path.
   // Positive tests:
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/foo2/index.html");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/foo2/index.html");
   EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry));
-  entry =
-      cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/foo2/foobar.html");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/foo2/foobar.html");
   EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry));
-  entry =
-      cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/foo2/bar/index.html");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/foo2/bar/index.html");
   EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/foo2/");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/foo2/");
   EXPECT_TRUE(realm2_entry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/foo2");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/foo2");
   EXPECT_TRUE(realm4_entry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/");
   EXPECT_TRUE(realm4_entry.IsEqualForTesting(*entry));
 
   // Negative tests:
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/foo3/index.html");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/foo3/index.html");
   EXPECT_FALSE(realm2_entry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, std::string());
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), std::string());
   EXPECT_FALSE(realm2_entry.IsEqualForTesting(*entry));
 
   // Confirm we find the same realm, different auth scheme by path lookup
-  HttpAuthCache::Entry* p_realm3_digest_entry = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
+  HttpAuthCache::Entry* p_realm3_digest_entry =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                   HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey());
   EXPECT_TRUE(p_realm3_digest_entry);
   HttpAuthCache::Entry realm3_digest_entry = *p_realm3_digest_entry;
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/index.html");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/baz/index.html");
   EXPECT_TRUE(realm3_digest_entry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/baz/");
   EXPECT_TRUE(realm3_digest_entry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/baz");
   EXPECT_FALSE(realm3_digest_entry.IsEqualForTesting(*entry));
 
   // Confirm we find the same realm, different auth scheme by path lookup
-  HttpAuthCache::Entry* p_realm3DigestEntry = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
+  HttpAuthCache::Entry* p_realm3DigestEntry =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                   HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey());
   EXPECT_TRUE(p_realm3DigestEntry);
   HttpAuthCache::Entry realm3DigestEntry = *p_realm3DigestEntry;
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/index.html");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/baz/index.html");
   EXPECT_TRUE(realm3DigestEntry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/baz/");
   EXPECT_TRUE(realm3DigestEntry.IsEqualForTesting(*entry));
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz");
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), "/baz");
   EXPECT_FALSE(realm3DigestEntry.IsEqualForTesting(*entry));
 
   // Lookup using empty path (may be used for proxy).
-  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, std::string());
+  entry = cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                             NetworkIsolationKey(), std::string());
   EXPECT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
   EXPECT_EQ(kRealm3, entry->realm());
@@ -219,73 +243,258 @@
   const char kServerPath[] = "/foo/bar/index.html";
 
   GURL origin("http://www.google.com");
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   HttpAuthCache::Entry* entry;
 
   // Add AUTH_SERVER entry.
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "Basic realm=Realm1", AuthCredentials(kServerUser, kServerPass),
-            kServerPath);
+            NetworkIsolationKey(), "Basic realm=Realm1",
+            AuthCredentials(kServerUser, kServerPass), kServerPath);
 
   // Make sure credentials are only accessible with AUTH_SERVER target.
   entry = cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                       HttpAuth::AUTH_SCHEME_BASIC);
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(entry->credentials().username(), kServerUser);
   EXPECT_EQ(entry->credentials().password(), kServerPass);
-  EXPECT_EQ(entry,
-            cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, kServerPath));
+  EXPECT_EQ(entry, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                      NetworkIsolationKey(), kServerPath));
   EXPECT_FALSE(cache.Lookup(origin, HttpAuth::AUTH_PROXY, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_PROXY, kServerPath));
+                            HttpAuth::AUTH_SCHEME_BASIC,
+                            NetworkIsolationKey()));
+  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_PROXY,
+                                  NetworkIsolationKey(), kServerPath));
 
   // Add AUTH_PROXY entry with same origin and realm but different credentials.
   cache.Add(origin, HttpAuth::AUTH_PROXY, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "Basic realm=Realm1", AuthCredentials(kProxyUser, kProxyPass), "/");
+            NetworkIsolationKey(), "Basic realm=Realm1",
+            AuthCredentials(kProxyUser, kProxyPass), "/");
 
   // Make sure credentials are only accessible with the corresponding target.
   entry = cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                       HttpAuth::AUTH_SCHEME_BASIC);
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(entry->credentials().username(), kServerUser);
   EXPECT_EQ(entry->credentials().password(), kServerPass);
-  EXPECT_EQ(entry,
-            cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, kServerPath));
+  EXPECT_EQ(entry, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                      NetworkIsolationKey(), kServerPath));
   entry = cache.Lookup(origin, HttpAuth::AUTH_PROXY, kRealm1,
-                       HttpAuth::AUTH_SCHEME_BASIC);
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(entry->credentials().username(), kProxyUser);
   EXPECT_EQ(entry->credentials().password(), kProxyPass);
-  EXPECT_EQ(entry, cache.LookupByPath(origin, HttpAuth::AUTH_PROXY, "/"));
+  EXPECT_EQ(entry, cache.LookupByPath(origin, HttpAuth::AUTH_PROXY,
+                                      NetworkIsolationKey(), "/"));
 
   // Remove the AUTH_SERVER entry.
   EXPECT_TRUE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                           HttpAuth::AUTH_SCHEME_BASIC,
+                           HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                            AuthCredentials(kServerUser, kServerPass)));
 
   // Verify that only the AUTH_SERVER entry was removed.
   EXPECT_FALSE(cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, kServerPath));
+                            HttpAuth::AUTH_SCHEME_BASIC,
+                            NetworkIsolationKey()));
+  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                  NetworkIsolationKey(), kServerPath));
   entry = cache.Lookup(origin, HttpAuth::AUTH_PROXY, kRealm1,
-                       HttpAuth::AUTH_SCHEME_BASIC);
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(entry->credentials().username(), kProxyUser);
   EXPECT_EQ(entry->credentials().password(), kProxyPass);
-  EXPECT_EQ(entry, cache.LookupByPath(origin, HttpAuth::AUTH_PROXY, "/"));
+  EXPECT_EQ(entry, cache.LookupByPath(origin, HttpAuth::AUTH_PROXY,
+                                      NetworkIsolationKey(), "/"));
 
   // Remove the AUTH_PROXY entry.
   EXPECT_TRUE(cache.Remove(origin, HttpAuth::AUTH_PROXY, kRealm1,
-                           HttpAuth::AUTH_SCHEME_BASIC,
+                           HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                            AuthCredentials(kProxyUser, kProxyPass)));
 
   // Verify that neither entry remains.
   EXPECT_FALSE(cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, kServerPath));
+                            HttpAuth::AUTH_SCHEME_BASIC,
+                            NetworkIsolationKey()));
+  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                  NetworkIsolationKey(), kServerPath));
   EXPECT_FALSE(cache.Lookup(origin, HttpAuth::AUTH_PROXY, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_PROXY, "/"));
+                            HttpAuth::AUTH_SCHEME_BASIC,
+                            NetworkIsolationKey()));
+  EXPECT_FALSE(cache.LookupByPath(origin, HttpAuth::AUTH_PROXY,
+                                  NetworkIsolationKey(), "/"));
+}
+
+// Make sure server credentials with different NetworkIsolationKeys are treated
+// separately if |key_entries_by_network_isolation_key| is set to true.
+TEST(HttpAuthCacheTest, SeparateServersByNetworkIsolationKey) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  GURL kPseudoOrigin("http://www.google.com");
+  const char kPath[] = "/";
+
+  const base::string16 kUser1 = ASCIIToUTF16("user1");
+  const base::string16 kPass1 = ASCIIToUTF16("pass1");
+  const base::string16 kUser2 = ASCIIToUTF16("user2");
+  const base::string16 kPass2 = ASCIIToUTF16("pass2");
+
+  for (bool key_entries_by_network_isolation_key : {false, true}) {
+    HttpAuthCache cache(key_entries_by_network_isolation_key);
+    HttpAuthCache::Entry* entry;
+
+    // Add entry for kNetworkIsolationKey1.
+    cache.Add(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+              HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1,
+              "Basic realm=Realm1", AuthCredentials(kUser1, kPass1), kPath);
+
+    entry = cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+    ASSERT_TRUE(entry);
+    EXPECT_EQ(entry->credentials().username(), kUser1);
+    EXPECT_EQ(entry->credentials().password(), kPass1);
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                        kNetworkIsolationKey1, kPath));
+    if (key_entries_by_network_isolation_key) {
+      EXPECT_FALSE(cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                                HttpAuth::AUTH_SCHEME_BASIC,
+                                kNetworkIsolationKey2));
+      EXPECT_FALSE(cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                      kNetworkIsolationKey2, kPath));
+    } else {
+      EXPECT_EQ(entry, cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                    kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
+                                    kNetworkIsolationKey2));
+      EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                          kNetworkIsolationKey2, kPath));
+    }
+
+    // Add entry for kNetworkIsolationKey2.
+    cache.Add(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+              HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2,
+              "Basic realm=Realm1", AuthCredentials(kUser2, kPass2), kPath);
+
+    entry = cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2);
+    ASSERT_TRUE(entry);
+    EXPECT_EQ(entry->credentials().username(), kUser2);
+    EXPECT_EQ(entry->credentials().password(), kPass2);
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                        kNetworkIsolationKey2, kPath));
+    entry = cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+    ASSERT_TRUE(entry);
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                        kNetworkIsolationKey1, kPath));
+    if (key_entries_by_network_isolation_key) {
+      EXPECT_EQ(entry->credentials().username(), kUser1);
+      EXPECT_EQ(entry->credentials().password(), kPass1);
+    } else {
+      EXPECT_EQ(entry->credentials().username(), kUser2);
+      EXPECT_EQ(entry->credentials().password(), kPass2);
+    }
+
+    // Remove the entry that was just added.
+    EXPECT_TRUE(cache.Remove(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                             HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2,
+                             AuthCredentials(kUser2, kPass2)));
+
+    EXPECT_FALSE(cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                              HttpAuth::AUTH_SCHEME_BASIC,
+                              kNetworkIsolationKey2));
+    EXPECT_FALSE(cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                    kNetworkIsolationKey2, kPath));
+    if (key_entries_by_network_isolation_key) {
+      entry = cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                           HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+      ASSERT_TRUE(entry);
+      EXPECT_EQ(entry->credentials().username(), kUser1);
+      EXPECT_EQ(entry->credentials().password(), kPass1);
+      EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                          kNetworkIsolationKey1, kPath));
+    } else {
+      EXPECT_FALSE(cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_SERVER, kRealm1,
+                                HttpAuth::AUTH_SCHEME_BASIC,
+                                kNetworkIsolationKey1));
+      EXPECT_FALSE(cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_SERVER,
+                                      kNetworkIsolationKey1, kPath));
+    }
+  }
+}
+
+// Make sure added proxy credentials ignore NetworkIsolationKey, even if if
+// |key_entries_by_network_isolation_key| is set to true.
+TEST(HttpAuthCacheTest, NeverSeparateProxiesByNetworkIsolationKey) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  GURL kPseudoOrigin("http://www.google.com");
+  const char kPath[] = "/";
+
+  const base::string16 kUser1 = ASCIIToUTF16("user1");
+  const base::string16 kPass1 = ASCIIToUTF16("pass1");
+  const base::string16 kUser2 = ASCIIToUTF16("user2");
+  const base::string16 kPass2 = ASCIIToUTF16("pass2");
+
+  for (bool key_entries_by_network_isolation_key : {false, true}) {
+    HttpAuthCache cache(key_entries_by_network_isolation_key);
+    HttpAuthCache::Entry* entry;
+
+    // Add entry for kNetworkIsolationKey1.
+    cache.Add(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+              HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1,
+              "Basic realm=Realm1", AuthCredentials(kUser1, kPass1), kPath);
+
+    entry = cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+    ASSERT_TRUE(entry);
+    EXPECT_EQ(entry->credentials().username(), kUser1);
+    EXPECT_EQ(entry->credentials().password(), kPass1);
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_PROXY,
+                                        kNetworkIsolationKey1, kPath));
+    EXPECT_EQ(entry,
+              cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                           HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_PROXY,
+                                        kNetworkIsolationKey2, kPath));
+
+    // Add entry for kNetworkIsolationKey2. It should overwrite the entry for
+    // kNetworkIsolationKey1.
+    cache.Add(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+              HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2,
+              "Basic realm=Realm1", AuthCredentials(kUser2, kPass2), kPath);
+
+    entry = cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2);
+    ASSERT_TRUE(entry);
+    EXPECT_EQ(entry->credentials().username(), kUser2);
+    EXPECT_EQ(entry->credentials().password(), kPass2);
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_PROXY,
+                                        kNetworkIsolationKey2, kPath));
+    EXPECT_EQ(entry,
+              cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                           HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1));
+    EXPECT_EQ(entry, cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_PROXY,
+                                        kNetworkIsolationKey1, kPath));
+
+    // Remove the entry that was just added using an empty NetworkIsolationKey.
+    EXPECT_TRUE(cache.Remove(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                             HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+                             AuthCredentials(kUser2, kPass2)));
+
+    EXPECT_FALSE(cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                              HttpAuth::AUTH_SCHEME_BASIC,
+                              kNetworkIsolationKey2));
+    EXPECT_FALSE(cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_PROXY,
+                                    kNetworkIsolationKey2, kPath));
+    EXPECT_FALSE(cache.Lookup(kPseudoOrigin, HttpAuth::AUTH_PROXY, kRealm1,
+                              HttpAuth::AUTH_SCHEME_BASIC,
+                              kNetworkIsolationKey1));
+    EXPECT_FALSE(cache.LookupByPath(kPseudoOrigin, HttpAuth::AUTH_PROXY,
+                                    kNetworkIsolationKey1, kPath));
+  }
 }
 
 TEST(HttpAuthCacheTest, AddPath) {
@@ -323,23 +532,25 @@
 // Calling Add when the realm entry already exists, should append that
 // path.
 TEST(HttpAuthCacheTest, AddToExistingEntry) {
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   GURL origin("http://www.foobar.com:70");
   const std::string kAuthChallenge = "Basic realm=MyRealm";
   const std::string kRealm = "MyRealm";
 
   HttpAuthCache::Entry* orig_entry = cache.Add(
       origin, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC,
-      kAuthChallenge, CreateASCIICredentials("user1", "password1"), "/x/y/z/");
+      NetworkIsolationKey(), kAuthChallenge,
+      CreateASCIICredentials("user1", "password1"), "/x/y/z/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC,
-            kAuthChallenge, CreateASCIICredentials("user2", "password2"),
-            "/z/y/x/");
+            NetworkIsolationKey(), kAuthChallenge,
+            CreateASCIICredentials("user2", "password2"), "/z/y/x/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC,
-            kAuthChallenge, CreateASCIICredentials("user3", "password3"),
-            "/z/y");
+            NetworkIsolationKey(), kAuthChallenge,
+            CreateASCIICredentials("user3", "password3"), "/z/y");
 
-  HttpAuthCache::Entry* entry = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm, HttpAuth::AUTH_SCHEME_BASIC);
+  HttpAuthCache::Entry* entry =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm,
+                   HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
 
   EXPECT_TRUE(entry == orig_entry);
   EXPECT_EQ(ASCIIToUTF16("user3"), entry->credentials().username());
@@ -353,70 +564,74 @@
 TEST(HttpAuthCacheTest, Remove) {
   GURL origin("http://foobar2.com");
 
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm1", AuthCredentials(kAlice, k123), "/");
+            NetworkIsolationKey(), "basic realm=Realm1",
+            AuthCredentials(kAlice, k123), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", CreateASCIICredentials("bob", "princess"),
-            "/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            CreateASCIICredentials("bob", "princess"), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm3", AuthCredentials(kAdmin, kPassword), "/");
+            NetworkIsolationKey(), "basic realm=Realm3",
+            AuthCredentials(kAdmin, kPassword), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3,
-            HttpAuth::AUTH_SCHEME_DIGEST, "digest realm=Realm3",
-            AuthCredentials(kRoot, kWileCoyote), "/");
+            HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
+            "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), "/");
 
   // Fails, because there is no realm "Realm5".
   EXPECT_FALSE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm5,
-                            HttpAuth::AUTH_SCHEME_BASIC,
+                            HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                             AuthCredentials(kAlice, k123)));
 
   // Fails because the origin is wrong.
-  EXPECT_FALSE(cache.Remove(
-      GURL("http://foobar2.com:100"), HttpAuth::AUTH_SERVER, kRealm1,
-      HttpAuth::AUTH_SCHEME_BASIC, AuthCredentials(kAlice, k123)));
+  EXPECT_FALSE(cache.Remove(GURL("http://foobar2.com:100"),
+                            HttpAuth::AUTH_SERVER, kRealm1,
+                            HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+                            AuthCredentials(kAlice, k123)));
 
   // Fails because the username is wrong.
   EXPECT_FALSE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC,
+                            HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                             AuthCredentials(kAlice2, k123)));
 
   // Fails because the password is wrong.
   EXPECT_FALSE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC,
+                            HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                             AuthCredentials(kAlice, k1234)));
 
   // Fails because the authentication type is wrong.
   EXPECT_FALSE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                            HttpAuth::AUTH_SCHEME_DIGEST,
+                            HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
                             AuthCredentials(kAlice, k123)));
 
   // Succeeds.
   EXPECT_TRUE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                           HttpAuth::AUTH_SCHEME_BASIC,
+                           HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                            AuthCredentials(kAlice, k123)));
 
   // Fails because we just deleted the entry!
   EXPECT_FALSE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                            HttpAuth::AUTH_SCHEME_BASIC,
+                            HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                             AuthCredentials(kAlice, k123)));
 
   // Succeed when there are two authentication types for the same origin,realm.
   EXPECT_TRUE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                           HttpAuth::AUTH_SCHEME_DIGEST,
+                           HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
                            AuthCredentials(kRoot, kWileCoyote)));
 
   // Succeed as above, but when entries were added in opposite order
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3,
-            HttpAuth::AUTH_SCHEME_DIGEST, "digest realm=Realm3",
-            AuthCredentials(kRoot, kWileCoyote), "/");
+            HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
+            "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), "/");
   EXPECT_TRUE(cache.Remove(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                           HttpAuth::AUTH_SCHEME_BASIC,
+                           HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
                            AuthCredentials(kAdmin, kPassword)));
 
   // Make sure that removing one entry still leaves the other available for
   // lookup.
-  HttpAuthCache::Entry* entry = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
+  HttpAuthCache::Entry* entry =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                   HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey());
   EXPECT_FALSE(nullptr == entry);
 }
 
@@ -428,50 +643,61 @@
   base::SimpleTestClock test_clock;
   test_clock.SetNow(start_time);
 
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   cache.set_clock_for_testing(&test_clock);
 
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm1", AuthCredentials(kAlice, k123), "/");
+            NetworkIsolationKey(), "basic realm=Realm1",
+            AuthCredentials(kAlice, k123), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", AuthCredentials(kRoot, kWileCoyote), "/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            AuthCredentials(kRoot, kWileCoyote), "/");
 
   test_clock.Advance(base::TimeDelta::FromSeconds(10));  // Time now 12:00:10
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm3", AuthCredentials(kAlice2, k1234), "/");
+            NetworkIsolationKey(), "basic realm=Realm3",
+            AuthCredentials(kAlice2, k1234), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm4", AuthCredentials(kUsername, kPassword), "/");
+            NetworkIsolationKey(), "basic realm=Realm4",
+            AuthCredentials(kUsername, kPassword), "/");
   // Add path to existing entry.
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            AuthCredentials(kAdmin, kPassword), "/baz/");
 
   base::Time test_time;
   ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:05", &test_time));
   cache.ClearEntriesAddedSince(test_time);
 
   // Realms 1 and 2 are older than 12:00:05 and should not be cleared
-  EXPECT_NE(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_NE(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
+  EXPECT_NE(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_NE(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
   // Creation time is set for a whole entry rather than for a particular path.
   // Path added within the requested duration isn't be removed.
-  EXPECT_NE(nullptr,
-            cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/"));
+  EXPECT_NE(nullptr, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                        NetworkIsolationKey(), "/baz/"));
 
   // Realms 3 and 4 are newer than 12:00:05 and should be cleared.
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
 
   cache.ClearEntriesAddedSince(start_time - base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
   EXPECT_EQ(nullptr,
-            cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/"));
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                        NetworkIsolationKey(), "/baz/"));
 }
 
 TEST(HttpAuthCacheTest, ClearEntriesAddedSinceWithNullTime) {
@@ -480,36 +706,45 @@
   base::SimpleTestClock test_clock;
   test_clock.SetNow(base::Time::Now());
 
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   cache.set_clock_for_testing(&test_clock);
 
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm1", AuthCredentials(kAlice, k123), "/");
+            NetworkIsolationKey(), "basic realm=Realm1",
+            AuthCredentials(kAlice, k123), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", AuthCredentials(kRoot, kWileCoyote), "/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            AuthCredentials(kRoot, kWileCoyote), "/");
 
   test_clock.Advance(base::TimeDelta::FromSeconds(10));
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm3", AuthCredentials(kAlice2, k1234), "/");
+            NetworkIsolationKey(), "basic realm=Realm3",
+            AuthCredentials(kAlice2, k1234), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm4", AuthCredentials(kUsername, kPassword), "/");
+            NetworkIsolationKey(), "basic realm=Realm4",
+            AuthCredentials(kUsername, kPassword), "/");
   // Add path to existing entry.
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            AuthCredentials(kAdmin, kPassword), "/baz/");
 
   cache.ClearEntriesAddedSince(base::Time());
 
   // All entries should be cleared.
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
   EXPECT_EQ(nullptr,
-            cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/"));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                        NetworkIsolationKey(), "/baz/"));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
 }
 
 TEST(HttpAuthCacheTest, ClearAllEntries) {
@@ -518,44 +753,54 @@
   base::SimpleTestClock test_clock;
   test_clock.SetNow(base::Time::Now());
 
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   cache.set_clock_for_testing(&test_clock);
 
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm1", AuthCredentials(kAlice, k123), "/");
+            NetworkIsolationKey(), "basic realm=Realm1",
+            AuthCredentials(kAlice, k123), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", AuthCredentials(kRoot, kWileCoyote), "/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            AuthCredentials(kRoot, kWileCoyote), "/");
 
   test_clock.Advance(base::TimeDelta::FromSeconds(10));
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm3", AuthCredentials(kAlice2, k1234), "/");
+            NetworkIsolationKey(), "basic realm=Realm3",
+            AuthCredentials(kAlice2, k1234), "/");
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm4", AuthCredentials(kUsername, kPassword), "/");
+            NetworkIsolationKey(), "basic realm=Realm4",
+            AuthCredentials(kUsername, kPassword), "/");
   // Add path to existing entry.
   cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
-            "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/");
+            NetworkIsolationKey(), "basic realm=Realm2",
+            AuthCredentials(kAdmin, kPassword), "/baz/");
 
   test_clock.Advance(base::TimeDelta::FromSeconds(55));
   cache.ClearAllEntries();
 
   // All entries should be cleared.
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
   EXPECT_EQ(nullptr,
-            cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, "/baz/"));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
-  EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
-                                  HttpAuth::AUTH_SCHEME_BASIC));
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                        NetworkIsolationKey(), "/baz/"));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
+  EXPECT_EQ(nullptr,
+            cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
+                         HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey()));
 }
 
 TEST(HttpAuthCacheTest, UpdateStaleChallenge) {
-  HttpAuthCache cache;
+  HttpAuthCache cache(false /* key_entries_by_network_isolation_key */);
   GURL origin("http://foobar2.com");
   HttpAuthCache::Entry* entry_pre = cache.Add(
       origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST,
+      NetworkIsolationKey(),
       "Digest realm=Realm1,"
       "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\"",
       CreateASCIICredentials("realm-digest-user", "realm-digest-password"),
@@ -568,6 +813,7 @@
 
   bool update_success = cache.UpdateStaleChallenge(
       origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST,
+      NetworkIsolationKey(),
       "Digest realm=Realm1,"
       "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\","
       "stale=\"true\"");
@@ -575,14 +821,16 @@
 
   // After the stale update, the entry should still exist in the cache and
   // the nonce count should be reset to 0.
-  HttpAuthCache::Entry* entry_post = cache.Lookup(
-      origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST);
+  HttpAuthCache::Entry* entry_post =
+      cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
+                   HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey());
   ASSERT_TRUE(entry_post != nullptr);
   EXPECT_EQ(2, entry_post->IncrementNonceCount());
 
   // UpdateStaleChallenge will fail if an entry doesn't exist in the cache.
   bool update_failure = cache.UpdateStaleChallenge(
       origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_DIGEST,
+      NetworkIsolationKey(),
       "Digest realm=Realm2,"
       "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\","
       "stale=\"true\"");
@@ -594,53 +842,61 @@
   std::string path("/some/path");
   std::string another_path("/another/path");
 
-  HttpAuthCache first_cache;
+  HttpAuthCache first_cache(false /* key_entries_by_network_isolation_key */);
+  ;
   HttpAuthCache::Entry* entry;
 
   first_cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                  HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm1",
-                  AuthCredentials(kAlice, k123), path);
+                  HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+                  "basic realm=Realm1", AuthCredentials(kAlice, k123), path);
   first_cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                  HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm2",
-                  AuthCredentials(kAlice2, k1234), path);
+                  HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+                  "basic realm=Realm2", AuthCredentials(kAlice2, k1234), path);
   first_cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                  HttpAuth::AUTH_SCHEME_DIGEST, "digest realm=Realm3",
-                  AuthCredentials(kRoot, kWileCoyote), path);
+                  HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
+                  "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote),
+                  path);
   entry = first_cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                          HttpAuth::AUTH_SCHEME_DIGEST, "digest realm=Realm3",
+                          HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
+                          "digest realm=Realm3",
                           AuthCredentials(kRoot, kWileCoyote), another_path);
 
   EXPECT_EQ(2, entry->IncrementNonceCount());
 
-  HttpAuthCache second_cache;
+  HttpAuthCache second_cache(false /* key_entries_by_network_isolation_key */);
+  ;
   // Will be overwritten by kRoot:kWileCoyote.
   second_cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                   HttpAuth::AUTH_SCHEME_DIGEST, "digest realm=Realm3",
-                   AuthCredentials(kAlice2, k1234), path);
+                   HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey(),
+                   "digest realm=Realm3", AuthCredentials(kAlice2, k1234),
+                   path);
   // Should be left intact.
   second_cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm4,
-                   HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm4",
-                   AuthCredentials(kAdmin, kRoot), path);
+                   HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+                   "basic realm=Realm4", AuthCredentials(kAdmin, kRoot), path);
 
   second_cache.UpdateAllFrom(first_cache);
 
   // Copied from first_cache.
-  entry = second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
-                              HttpAuth::AUTH_SCHEME_BASIC);
+  entry =
+      second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1,
+                          HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   EXPECT_TRUE(nullptr != entry);
   EXPECT_EQ(kAlice, entry->credentials().username());
   EXPECT_EQ(k123, entry->credentials().password());
 
   // Copied from first_cache.
-  entry = second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
-                              HttpAuth::AUTH_SCHEME_BASIC);
+  entry =
+      second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2,
+                          HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   EXPECT_TRUE(nullptr != entry);
   EXPECT_EQ(kAlice2, entry->credentials().username());
   EXPECT_EQ(k1234, entry->credentials().password());
 
   // Overwritten from first_cache.
-  entry = second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
-                              HttpAuth::AUTH_SCHEME_DIGEST);
+  entry =
+      second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3,
+                          HttpAuth::AUTH_SCHEME_DIGEST, NetworkIsolationKey());
   EXPECT_TRUE(nullptr != entry);
   EXPECT_EQ(kRoot, entry->credentials().username());
   EXPECT_EQ(kWileCoyote, entry->credentials().password());
@@ -648,15 +904,16 @@
   EXPECT_EQ(3, entry->IncrementNonceCount());
 
   // All paths should get copied.
-  entry =
-      second_cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, another_path);
+  entry = second_cache.LookupByPath(origin, HttpAuth::AUTH_SERVER,
+                                    NetworkIsolationKey(), another_path);
   EXPECT_TRUE(nullptr != entry);
   EXPECT_EQ(kRoot, entry->credentials().username());
   EXPECT_EQ(kWileCoyote, entry->credentials().password());
 
   // Left intact in second_cache.
-  entry = second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
-                              HttpAuth::AUTH_SCHEME_BASIC);
+  entry =
+      second_cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4,
+                          HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   EXPECT_TRUE(nullptr != entry);
   EXPECT_EQ(kAdmin, entry->credentials().username());
   EXPECT_EQ(kRoot, entry->credentials().password());
@@ -666,7 +923,9 @@
 // insertion and existence testing).
 class HttpAuthCacheEvictionTest : public testing::Test {
  protected:
-  HttpAuthCacheEvictionTest() : origin_("http://www.google.com") { }
+  HttpAuthCacheEvictionTest()
+      : origin_("http://www.google.com"),
+        cache_(false /* key_entries_by_network_isolation_key */) {}
 
   std::string GenerateRealm(int realm_i) {
     return base::StringPrintf("Realm %d", realm_i);
@@ -682,15 +941,15 @@
 
   void AddPathToRealm(int realm_i, int path_i) {
     cache_.Add(origin_, HttpAuth::AUTH_SERVER, GenerateRealm(realm_i),
-               HttpAuth::AUTH_SCHEME_BASIC, std::string(),
-               AuthCredentials(kUsername, kPassword),
+               HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+               std::string(), AuthCredentials(kUsername, kPassword),
                GeneratePath(realm_i, path_i));
   }
 
   void CheckRealmExistence(int realm_i, bool exists) {
     const HttpAuthCache::Entry* entry =
         cache_.Lookup(origin_, HttpAuth::AUTH_SERVER, GenerateRealm(realm_i),
-                      HttpAuth::AUTH_SCHEME_BASIC);
+                      HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
     if (exists) {
       EXPECT_FALSE(entry == nullptr);
       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
@@ -701,7 +960,8 @@
 
   void CheckPathExistence(int realm_i, int path_i, bool exists) {
     const HttpAuthCache::Entry* entry = cache_.LookupByPath(
-        origin_, HttpAuth::AUTH_SERVER, GeneratePath(realm_i, path_i));
+        origin_, HttpAuth::AUTH_SERVER, NetworkIsolationKey(),
+        GeneratePath(realm_i, path_i));
     if (exists) {
       EXPECT_FALSE(entry == nullptr);
       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index acc47768..0e11a5d 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -139,6 +139,7 @@
 HttpAuthController::HttpAuthController(
     HttpAuth::Target target,
     const GURL& auth_url,
+    const NetworkIsolationKey& network_isolation_key,
     HttpAuthCache* http_auth_cache,
     HttpAuthHandlerFactory* http_auth_handler_factory,
     HostResolver* host_resolver,
@@ -147,6 +148,7 @@
       auth_url_(auth_url),
       auth_origin_(auth_url.GetOrigin()),
       auth_path_(auth_url.path()),
+      network_isolation_key_(network_isolation_key),
       embedded_identity_used_(false),
       allow_default_credentials_(allow_default_credentials),
       default_credentials_used_(false),
@@ -220,8 +222,8 @@
   // is expected to be fast. LookupByPath() is fast in the common case, since
   // the number of http auth cache entries is expected to be very small.
   // (For most users in fact, it will be 0.)
-  HttpAuthCache::Entry* entry =
-      http_auth_cache_->LookupByPath(auth_origin_, target_, auth_path_);
+  HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath(
+      auth_origin_, target_, network_isolation_key_, auth_path_);
   if (!entry)
     return false;
 
@@ -294,7 +296,8 @@
       case HttpAuth::AUTHORIZATION_RESULT_STALE:
         if (http_auth_cache_->UpdateStaleChallenge(
                 auth_origin_, target_, handler_->realm(),
-                handler_->auth_scheme(), challenge_used)) {
+                handler_->auth_scheme(), network_isolation_key_,
+                challenge_used)) {
           InvalidateCurrentHandler(INVALIDATE_HANDLER);
         } else {
           // It's possible that a server could incorrectly issue a stale
@@ -420,8 +423,9 @@
       break;
     default:
       http_auth_cache_->Add(auth_origin_, target_, handler_->realm(),
-                            handler_->auth_scheme(), handler_->challenge(),
-                            identity_.credentials, auth_path_);
+                            handler_->auth_scheme(), network_isolation_key_,
+                            handler_->challenge(), identity_.credentials,
+                            auth_path_);
       break;
   }
 }
@@ -469,7 +473,8 @@
   // Note: we require the credentials to match before invalidating
   // since the entry in the cache may be newer than what we used last time.
   http_auth_cache_->Remove(auth_origin_, target_, handler_->realm(),
-                           handler_->auth_scheme(), identity_.credentials);
+                           handler_->auth_scheme(), network_isolation_key_,
+                           identity_.credentials);
 }
 
 void HttpAuthController::PrepareIdentityForReuse() {
@@ -518,8 +523,9 @@
   }
 
   // Check the auth cache for a realm entry.
-  HttpAuthCache::Entry* entry = http_auth_cache_->Lookup(
-      auth_origin_, target_, handler_->realm(), handler_->auth_scheme());
+  HttpAuthCache::Entry* entry =
+      http_auth_cache_->Lookup(auth_origin_, target_, handler_->realm(),
+                               handler_->auth_scheme(), network_isolation_key_);
 
   if (entry) {
     identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
index 414554f..27a78f2 100644
--- a/net/http/http_auth_controller.h
+++ b/net/http/http_auth_controller.h
@@ -14,6 +14,7 @@
 #include "base/threading/thread_checker.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_auth_preferences.h"
 #include "net/log/net_log_with_source.h"
@@ -59,6 +60,10 @@
   //       If |target| is PROXY, then |auth_url| should have no hierarchical
   //       part since that is meaningless.
   //
+  // * |network_isolation_key| specifies the NetworkIsolationKey associated with
+  //       the resource load. Depending on settings, credentials may be scoped
+  //       to a single NetworkIsolationKey.
+  //
   // * |http_auth_cache| specifies the credentials cache to use. During
   //       authentication if explicit (user-provided) credentials are used and
   //       they can be cached to respond to authentication challenges in the
@@ -81,6 +86,7 @@
   HttpAuthController(
       HttpAuth::Target target,
       const GURL& auth_url,
+      const NetworkIsolationKey& network_isolation_key,
       HttpAuthCache* http_auth_cache,
       HttpAuthHandlerFactory* http_auth_handler_factory,
       HostResolver* host_resolver,
@@ -197,6 +203,9 @@
   // For proxy authentication the path is empty.
   const std::string auth_path_;
 
+  // NetworkIsolationKey assocaied with the request.
+  const NetworkIsolationKey network_isolation_key_;
+
   // |handler_| encapsulates the logic for the particular auth-scheme.
   // This includes the challenge's parameters. If nullptr, then there is no
   // associated auth handler.
diff --git a/net/http/http_auth_controller_unittest.cc b/net/http/http_auth_controller_unittest.cc
index 7e654baa..d05f247 100644
--- a/net/http/http_auth_controller_unittest.cc
+++ b/net/http/http_auth_controller_unittest.cc
@@ -56,7 +56,8 @@
     int expected_controller_rv,
     SchemeState scheme_state,
     const NetLogWithSource& net_log = NetLogWithSource()) {
-  HttpAuthCache dummy_auth_cache;
+  HttpAuthCache dummy_auth_cache(
+      false /* key_server_entries_by_network_isolation_key */);
 
   HttpRequestInfo request;
   request.method = "GET";
@@ -75,10 +76,11 @@
   auth_handler_factory.set_do_init_from_challenge(true);
   auto host_resolver = std::make_unique<MockHostResolver>();
 
-  scoped_refptr<HttpAuthController> controller(new HttpAuthController(
-      HttpAuth::AUTH_PROXY, GURL("http://example.com"), &dummy_auth_cache,
-      &auth_handler_factory, host_resolver.get(),
-      HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+  scoped_refptr<HttpAuthController> controller(
+      base::MakeRefCounted<HttpAuthController>(
+          HttpAuth::AUTH_PROXY, GURL("http://example.com"),
+          NetworkIsolationKey(), &dummy_auth_cache, &auth_handler_factory,
+          host_resolver.get(), HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
   SSLInfo null_ssl_info;
   ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, null_ssl_info, false,
                                                 false, net_log));
@@ -213,7 +215,8 @@
   };
 
   NetLogWithSource dummy_log;
-  HttpAuthCache dummy_auth_cache;
+  HttpAuthCache dummy_auth_cache(
+      false /* key_server_entries_by_network_isolation_key */);
   HttpRequestInfo request;
   request.method = "GET";
   request.url = GURL("http://example.com");
@@ -258,10 +261,11 @@
 
   auto host_resolver = std::make_unique<MockHostResolver>();
 
-  scoped_refptr<HttpAuthController> controller(new HttpAuthController(
-      HttpAuth::AUTH_SERVER, GURL("http://example.com"), &dummy_auth_cache,
-      &auth_handler_factory, host_resolver.get(),
-      HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+  scoped_refptr<HttpAuthController> controller(
+      base::MakeRefCounted<HttpAuthController>(
+          HttpAuth::AUTH_SERVER, GURL("http://example.com"),
+          NetworkIsolationKey(), &dummy_auth_cache, &auth_handler_factory,
+          host_resolver.get(), HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
   SSLInfo null_ssl_info;
   ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, null_ssl_info, false,
                                                 false, dummy_log));
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 6fca8117..4da166c4 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -89,7 +89,8 @@
       enable_quic_proxies_for_https_urls(false),
       disable_idle_sockets_close_on_memory_pressure(false),
       allow_default_credentials(HttpAuthPreferences::DefaultCredentials::
-                                    DISALLOW_DEFAULT_CREDENTIALS) {
+                                    DISALLOW_DEFAULT_CREDENTIALS),
+      key_auth_cache_server_entries_by_network_isolation_key(false) {
   enable_early_data =
       base::FeatureList::IsEnabled(features::kEnableTLS13EarlyData);
 }
@@ -141,6 +142,8 @@
 #endif
       proxy_resolution_service_(context.proxy_resolution_service),
       ssl_config_service_(context.ssl_config_service),
+      http_auth_cache_(
+          params.key_auth_cache_server_entries_by_network_isolation_key),
       ssl_client_session_cache_(SSLClientSessionCache::Config()),
       ssl_client_context_(context.ssl_config_service,
                           context.cert_verifier,
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index f6a0a95b..037b629 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -150,6 +150,8 @@
     // If authentication APIs that support ambient authentication are allowed
     // to use the default credentials.
     HttpAuthPreferences::DefaultCredentials allow_default_credentials;
+
+    bool key_auth_cache_server_entries_by_network_isolation_key;
   };
 
   // Structure with pointers to the dependencies of the HttpNetworkSession.
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 66c4164..a1787e4 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -883,9 +883,10 @@
     return OK;
   HttpAuth::Target target = HttpAuth::AUTH_PROXY;
   if (!auth_controllers_[target].get())
-    auth_controllers_[target] = new HttpAuthController(
-        target, AuthURL(target), session_->http_auth_cache(),
-        session_->http_auth_handler_factory(), session_->host_resolver(),
+    auth_controllers_[target] = base::MakeRefCounted<HttpAuthController>(
+        target, AuthURL(target), request_->network_isolation_key,
+        session_->http_auth_cache(), session_->http_auth_handler_factory(),
+        session_->host_resolver(),
         session_->params().allow_default_credentials);
   return auth_controllers_[target]->MaybeGenerateAuthToken(request_,
                                                            io_callback_,
@@ -903,9 +904,10 @@
   next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE;
   HttpAuth::Target target = HttpAuth::AUTH_SERVER;
   if (!auth_controllers_[target].get()) {
-    auth_controllers_[target] = new HttpAuthController(
-        target, AuthURL(target), session_->http_auth_cache(),
-        session_->http_auth_handler_factory(), session_->host_resolver(),
+    auth_controllers_[target] = base::MakeRefCounted<HttpAuthController>(
+        target, AuthURL(target), request_->network_isolation_key,
+        session_->http_auth_cache(), session_->http_auth_handler_factory(),
+        session_->host_resolver(),
         session_->params().allow_default_credentials);
     if (request_->load_flags & LOAD_DO_NOT_USE_EMBEDDED_IDENTITY)
       auth_controllers_[target]->DisableEmbeddedIdentity();
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index a7fd28ec..4db22adf 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -3341,7 +3341,7 @@
   // Check that credentials were successfully cached, with the right target.
   HttpAuthCache::Entry* entry = session->http_auth_cache()->Lookup(
       GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
-      HttpAuth::AUTH_SCHEME_BASIC);
+      HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   ASSERT_EQ(kFoo, entry->credentials().username());
   ASSERT_EQ(kBar, entry->credentials().password());
@@ -4005,13 +4005,13 @@
       MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
                 "Host: myproxy:70\r\n"
                 "Proxy-Connection: keep-alive\r\n\r\n"),
-      // Rety with proxy auth credentials, which will result in a server auth
+      // Retry with proxy auth credentials, which will result in a server auth
       // challenge.
       MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
                 "Host: myproxy:70\r\n"
                 "Proxy-Connection: keep-alive\r\n"
                 "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
-      // Rety with proxy and server auth credentials, which gets a response.
+      // Retry with proxy and server auth credentials, which gets a response.
       MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
                 "Host: myproxy:70\r\n"
                 "Proxy-Connection: keep-alive\r\n"
@@ -4091,13 +4091,13 @@
   // Check that the credentials were cached correctly.
   HttpAuthCache::Entry* entry = session->http_auth_cache()->Lookup(
       GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
-      HttpAuth::AUTH_SCHEME_BASIC);
+      HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   ASSERT_EQ(kFoo, entry->credentials().username());
   ASSERT_EQ(kBar, entry->credentials().password());
-  entry = session->http_auth_cache()->Lookup(GURL("http://myproxy:70"),
-                                             HttpAuth::AUTH_SERVER, "MyRealm1",
-                                             HttpAuth::AUTH_SCHEME_BASIC);
+  entry = session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey());
   ASSERT_TRUE(entry);
   ASSERT_EQ(kFoo2, entry->credentials().username());
   ASSERT_EQ(kBar2, entry->credentials().password());
@@ -4121,6 +4121,538 @@
   session->CloseAllConnections();
 }
 
+// Test the no-tunnel HTTP auth case where proxy and server origins and realms
+// are the same, but the user/passwords are different, and with different
+// NetworkIsolationKeys. Sends one request with a NIK, response to both proxy
+// and auth challenges, sends another request with another NIK, expecting only
+// the proxy credentials to be cached, and thus sees only a server auth
+// challenge. Then sends a request with the original NIK, expecting cached proxy
+// and auth credentials that match the ones used in the first request.
+//
+// Serves to verify credentials are correctly separated based on
+// HttpAuth::Target and NetworkIsolationKeys, but NetworkIsolationKey only
+// affects server credentials, not proxy credentials.
+TEST_F(HttpNetworkTransactionTest,
+       BasicAuthProxyMatchesServerAuthWithNetworkIsolationKeyNoTunnel) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  // This test would need to use a single socket without this option enabled.
+  // Best to use this option when it would affect a test, as it will eventually
+  // become the default behavior.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kPartitionConnectionsByNetworkIsolationKey);
+
+  // Proxy matches request URL.
+  session_deps_.proxy_resolution_service =
+      ProxyResolutionService::CreateFixedFromPacResult(
+          "PROXY myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+  BoundTestNetLog log;
+  session_deps_.net_log = log.bound().net_log();
+  session_deps_.key_auth_cache_server_entries_by_network_isolation_key = true;
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  MockWrite data_writes[] = {
+      // Initial request gets a proxy auth challenge.
+      MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+      // Retry with proxy auth credentials, which will result in a server auth
+      // challenge.
+      MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+      // Retry with proxy and server auth credentials, which gets a response.
+      MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
+                "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"),
+      // Another request to the same server and using the same NIK should
+      // preemptively send the correct cached proxy and server
+      // auth headers.
+      MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
+                "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+      // Proxy auth challenge.
+      MockRead("HTTP/1.0 407 Proxy Authentication Required\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      // Server auth challenge.
+      MockRead("HTTP/1.0 401 Authentication Required\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      // Response.
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "Content-Length: 5\r\n\r\n"
+               "hello"),
+      // Response to second request.
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "Content-Length: 2\r\n\r\n"
+               "hi"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  MockWrite data_writes2[] = {
+      // Initial request using a different NetworkIsolationKey includes the
+      // cached proxy credentials, but not server credentials.
+      MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+      // Retry with proxy and new server auth credentials, which gets a
+      // response.
+      MockWrite("GET http://myproxy:70/ HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n"
+                "Authorization: Basic Zm9vMzpiYXIz\r\n\r\n"),
+  };
+
+  MockRead data_reads2[] = {
+      // Server auth challenge.
+      MockRead("HTTP/1.0 401 Authentication Required\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      // Response.
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "Content-Length: 9\r\n\r\n"
+               "greetings"),
+  };
+
+  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+
+  TestCompletionCallback callback;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://myproxy:70/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request.network_isolation_key = kNetworkIsolationKey1;
+
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  int rv = trans->Start(&request, callback.callback(), log.bound());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_TRUE(CheckBasicProxyAuth(response->auth_challenge));
+
+  rv = trans->RestartWithAuth(AuthCredentials(kFoo, kBar), callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(401, response->headers->response_code());
+  EXPECT_FALSE(response->auth_challenge->is_proxy);
+  EXPECT_EQ("http://myproxy:70",
+            response->auth_challenge->challenger.Serialize());
+  EXPECT_EQ("MyRealm1", response->auth_challenge->realm);
+  EXPECT_EQ(kBasicAuthScheme, response->auth_challenge->scheme);
+
+  rv = trans->RestartWithAuth(AuthCredentials(kFoo2, kBar2),
+                              callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(200, response->headers->response_code());
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge.has_value());
+  std::string response_data;
+  EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+  EXPECT_EQ("hello", response_data);
+
+  // Check that the proxy credentials were cached correctly. The should be
+  // accessible with any NetworkIsolationKey.
+  HttpAuthCache::Entry* entry = session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo, entry->credentials().username());
+  ASSERT_EQ(kBar, entry->credentials().password());
+  EXPECT_EQ(entry,
+            session->http_auth_cache()->Lookup(
+                GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+                HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+
+  // Check that the server credentials were cached correctly. The should be
+  // accessible with only kNetworkIsolationKey1.
+  entry = session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo2, entry->credentials().username());
+  ASSERT_EQ(kBar2, entry->credentials().password());
+  // Looking up the server entry with another NetworkIsolationKey should fail.
+  EXPECT_FALSE(session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+
+  // Make another request with a different NetworkIsolationKey. It should use
+  // another socket, reuse the cached proxy credentials, but result in a server
+  // auth challenge.
+  request.network_isolation_key = kNetworkIsolationKey2;
+  trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans->Start(&request, callback.callback(), log.bound());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(401, response->headers->response_code());
+  EXPECT_FALSE(response->auth_challenge->is_proxy);
+  EXPECT_EQ("http://myproxy:70",
+            response->auth_challenge->challenger.Serialize());
+  EXPECT_EQ("MyRealm1", response->auth_challenge->realm);
+  EXPECT_EQ(kBasicAuthScheme, response->auth_challenge->scheme);
+
+  rv = trans->RestartWithAuth(AuthCredentials(kFoo3, kBar3),
+                              callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(200, response->headers->response_code());
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge.has_value());
+  EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+  EXPECT_EQ("greetings", response_data);
+
+  // Check that the proxy credentials are still cached.
+  entry = session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo, entry->credentials().username());
+  ASSERT_EQ(kBar, entry->credentials().password());
+  EXPECT_EQ(entry,
+            session->http_auth_cache()->Lookup(
+                GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+                HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+
+  // Check that the correct server credentials are cached for each
+  // NetworkIsolationKey.
+  entry = session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo2, entry->credentials().username());
+  ASSERT_EQ(kBar2, entry->credentials().password());
+  entry = session->http_auth_cache()->Lookup(
+      GURL("http://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo3, entry->credentials().username());
+  ASSERT_EQ(kBar3, entry->credentials().password());
+
+  // Make a request with the original NetworkIsolationKey. It should reuse the
+  // first socket, and the proxy credentials sent on the first socket.
+  request.network_isolation_key = kNetworkIsolationKey1;
+  trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans->Start(&request, callback.callback(), log.bound());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(200, response->headers->response_code());
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge.has_value());
+  EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+  EXPECT_EQ("hi", response_data);
+
+  trans.reset();
+  session->CloseAllConnections();
+}
+
+// Much like the test above, but uses tunnelled connections.
+TEST_F(HttpNetworkTransactionTest,
+       BasicAuthProxyMatchesServerAuthWithNetworkIsolationKeyWithTunnel) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  // This test would need to use a single socket without this option enabled.
+  // Best to use this option when it would affect a test, as it will eventually
+  // become the default behavior.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kPartitionConnectionsByNetworkIsolationKey);
+
+  // Proxy matches request URL.
+  session_deps_.proxy_resolution_service =
+      ProxyResolutionService::CreateFixedFromPacResult(
+          "HTTPS myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+  BoundTestNetLog log;
+  session_deps_.net_log = log.bound().net_log();
+  session_deps_.key_auth_cache_server_entries_by_network_isolation_key = true;
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  MockWrite data_writes[] = {
+      // Initial tunnel request gets a proxy auth challenge.
+      MockWrite("CONNECT myproxy:70 HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+      // Retry with proxy auth credentials, which will result in establishing a
+      // tunnel.
+      MockWrite("CONNECT myproxy:70 HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+      // Request over the tunnel, which gets a server auth challenge.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+      // Retry with server auth credentials, which gets a response.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Connection: keep-alive\r\n"
+                "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"),
+      // Another request to the same server and using the same NIK should
+      // preemptively send the correct cached server
+      // auth header. Since a tunnel was already established, the proxy headers
+      // won't be sent again except when establishing another tunnel.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Connection: keep-alive\r\n"
+                "Authorization: Basic Zm9vMjpiYXIy\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+      // Proxy auth challenge.
+      MockRead("HTTP/1.0 407 Proxy Authentication Required\r\n"
+               "Proxy-Connection: keep-alive\r\n"
+               "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      // Tunnel success
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+      // Server auth challenge.
+      MockRead("HTTP/1.0 401 Authentication Required\r\n"
+               "Connection: keep-alive\r\n"
+               "WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      // Response.
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: keep-alive\r\n"
+               "Content-Length: 5\r\n\r\n"
+               "hello"),
+      // Response to second request.
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: keep-alive\r\n"
+               "Content-Length: 2\r\n\r\n"
+               "hi"),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+  // One for the proxy connection, one of the server connection.
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+  SSLSocketDataProvider ssl2(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2);
+
+  MockWrite data_writes2[] = {
+      // Initial request using a different NetworkIsolationKey includes the
+      // cached proxy credentials when establishing a tunnel.
+      MockWrite("CONNECT myproxy:70 HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+      // Request over the tunnel, which gets a server auth challenge. Cached
+      // credentials cannot be used, since the NIK is different.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+      // Retry with server auth credentials, which gets a response.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: myproxy:70\r\n"
+                "Connection: keep-alive\r\n"
+                "Authorization: Basic Zm9vMzpiYXIz\r\n\r\n"),
+  };
+
+  MockRead data_reads2[] = {
+      // Tunnel success
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+      // Server auth challenge.
+      MockRead("HTTP/1.0 401 Authentication Required\r\n"
+               "Connection: keep-alive\r\n"
+               "WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      // Response.
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Connection: keep-alive\r\n"
+               "Content-Length: 9\r\n\r\n"
+               "greetings"),
+  };
+
+  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  // One for the proxy connection, one of the server connection.
+  SSLSocketDataProvider ssl3(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl3);
+  SSLSocketDataProvider ssl4(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl4);
+
+  TestCompletionCallback callback;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://myproxy:70/");
+  request.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  request.network_isolation_key = kNetworkIsolationKey1;
+
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  int rv = trans->Start(&request, callback.callback(), log.bound());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ(407, response->headers->response_code());
+  EXPECT_TRUE(CheckBasicSecureProxyAuth(response->auth_challenge));
+
+  rv = trans->RestartWithAuth(AuthCredentials(kFoo, kBar), callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(401, response->headers->response_code());
+  EXPECT_FALSE(response->auth_challenge->is_proxy);
+  EXPECT_EQ("https://myproxy:70",
+            response->auth_challenge->challenger.Serialize());
+  EXPECT_EQ("MyRealm1", response->auth_challenge->realm);
+  EXPECT_EQ(kBasicAuthScheme, response->auth_challenge->scheme);
+
+  rv = trans->RestartWithAuth(AuthCredentials(kFoo2, kBar2),
+                              callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(200, response->headers->response_code());
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge.has_value());
+  std::string response_data;
+  EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+  EXPECT_EQ("hello", response_data);
+
+  // Check that the proxy credentials were cached correctly. The should be
+  // accessible with any NetworkIsolationKey.
+  HttpAuthCache::Entry* entry = session->http_auth_cache()->Lookup(
+      GURL("https://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo, entry->credentials().username());
+  ASSERT_EQ(kBar, entry->credentials().password());
+  EXPECT_EQ(entry,
+            session->http_auth_cache()->Lookup(
+                GURL("https://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+                HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+
+  // Check that the server credentials were cached correctly. The should be
+  // accessible with only kNetworkIsolationKey1.
+  entry = session->http_auth_cache()->Lookup(
+      GURL("https://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo2, entry->credentials().username());
+  ASSERT_EQ(kBar2, entry->credentials().password());
+  // Looking up the server entry with another NetworkIsolationKey should fail.
+  EXPECT_FALSE(session->http_auth_cache()->Lookup(
+      GURL("https://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+
+  // Make another request with a different NetworkIsolationKey. It should use
+  // another socket, reuse the cached proxy credentials, but result in a server
+  // auth challenge.
+  request.network_isolation_key = kNetworkIsolationKey2;
+  trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans->Start(&request, callback.callback(), log.bound());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(401, response->headers->response_code());
+  EXPECT_FALSE(response->auth_challenge->is_proxy);
+  EXPECT_EQ("https://myproxy:70",
+            response->auth_challenge->challenger.Serialize());
+  EXPECT_EQ("MyRealm1", response->auth_challenge->realm);
+  EXPECT_EQ(kBasicAuthScheme, response->auth_challenge->scheme);
+
+  rv = trans->RestartWithAuth(AuthCredentials(kFoo3, kBar3),
+                              callback.callback());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(200, response->headers->response_code());
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge.has_value());
+  EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+  EXPECT_EQ("greetings", response_data);
+
+  // Check that the proxy credentials are still cached.
+  entry = session->http_auth_cache()->Lookup(
+      GURL("https://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo, entry->credentials().username());
+  ASSERT_EQ(kBar, entry->credentials().password());
+  EXPECT_EQ(entry,
+            session->http_auth_cache()->Lookup(
+                GURL("https://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
+                HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2));
+
+  // Check that the correct server credentials are cached for each
+  // NetworkIsolationKey.
+  entry = session->http_auth_cache()->Lookup(
+      GURL("https://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey1);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo2, entry->credentials().username());
+  ASSERT_EQ(kBar2, entry->credentials().password());
+  entry = session->http_auth_cache()->Lookup(
+      GURL("https://myproxy:70"), HttpAuth::AUTH_SERVER, "MyRealm1",
+      HttpAuth::AUTH_SCHEME_BASIC, kNetworkIsolationKey2);
+  ASSERT_TRUE(entry);
+  ASSERT_EQ(kFoo3, entry->credentials().username());
+  ASSERT_EQ(kBar3, entry->credentials().password());
+
+  // Make a request with the original NetworkIsolationKey. It should reuse the
+  // first socket, and the proxy credentials sent on the first socket.
+  request.network_isolation_key = kNetworkIsolationKey1;
+  trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  rv = trans->Start(&request, callback.callback(), log.bound());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_EQ(200, response->headers->response_code());
+  // The password prompt info should not be set.
+  EXPECT_FALSE(response->auth_challenge.has_value());
+  EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+  EXPECT_EQ("hi", response_data);
+
+  trans.reset();
+  session->CloseAllConnections();
+}
+
 // Test that we don't pass extraneous headers from the proxy's response to the
 // caller when the proxy responds to CONNECT with 407.
 TEST_F(HttpNetworkTransactionTest, SanitizeProxyAuthHeaders) {
@@ -18372,8 +18904,8 @@
 
   session->http_auth_cache()->Add(
       GURL("http://myproxy:70/"), HttpAuth::AUTH_PROXY, "MyRealm1",
-      HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=MyRealm1",
-      AuthCredentials(kFoo, kBar), "/");
+      HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+      "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
 
   TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
 
diff --git a/net/http/http_proxy_client_socket_fuzzer.cc b/net/http/http_proxy_client_socket_fuzzer.cc
index f253b16f..2b416158 100644
--- a/net/http/http_proxy_client_socket_fuzzer.cc
+++ b/net/http/http_proxy_client_socket_fuzzer.cc
@@ -17,6 +17,7 @@
 #include "net/base/address_list.h"
 #include "net/base/auth.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/test_completion_callback.h"
 #include "net/http/http_auth_cache.h"
 #include "net/http/http_auth_handler_basic.h"
@@ -47,7 +48,8 @@
 
   // Create auth handler supporting basic and digest schemes.  Other schemes can
   // make system calls, which doesn't seem like a great idea.
-  net::HttpAuthCache auth_cache;
+  net::HttpAuthCache auth_cache(
+      false /* key_server_entries_by_network_isolation_key */);
   net::HttpAuthHandlerRegistryFactory auth_handler_factory;
   auth_handler_factory.RegisterSchemeFactory(
       net::kBasicAuthScheme, new net::HttpAuthHandlerBasic::Factory());
@@ -55,10 +57,10 @@
       net::kDigestAuthScheme, new net::HttpAuthHandlerDigest::Factory());
 
   scoped_refptr<net::HttpAuthController> auth_controller(
-      new net::HttpAuthController(
-          net::HttpAuth::AUTH_PROXY, GURL("http://proxy:42/"), &auth_cache,
-          &auth_handler_factory, nullptr,
-          net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
+      base::MakeRefCounted<net::HttpAuthController>(
+          net::HttpAuth::AUTH_PROXY, GURL("http://proxy:42/"),
+          net::NetworkIsolationKey(), &auth_cache, &auth_handler_factory,
+          nullptr, net::HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
   // Determine if the HttpProxyClientSocket should be told the underlying socket
   // is HTTPS.
   net::HttpProxyClientSocket socket(
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc
index 2349095..13efec42 100644
--- a/net/http/http_proxy_connect_job.cc
+++ b/net/http/http_proxy_connect_job.cc
@@ -181,10 +181,11 @@
       has_established_connection_(false),
       http_auth_controller_(
           params_->tunnel()
-              ? new HttpAuthController(
+              ? base::MakeRefCounted<HttpAuthController>(
                     HttpAuth::AUTH_PROXY,
                     GURL((params_->ssl_params() ? "https://" : "http://") +
                          GetDestination().ToString()),
+                    params_->network_isolation_key(),
                     common_connect_job_params->http_auth_cache,
                     common_connect_job_params->http_auth_handler_factory,
                     host_resolver(),
diff --git a/net/http/http_proxy_connect_job_unittest.cc b/net/http/http_proxy_connect_job_unittest.cc
index e2b39b01..ec441b7 100644
--- a/net/http/http_proxy_connect_job_unittest.cc
+++ b/net/http/http_proxy_connect_job_unittest.cc
@@ -755,7 +755,8 @@
                      : (std::string("https://") + kHttpsProxyHost));
   session_->http_auth_cache()->Add(
       proxy_url, HttpAuth::AUTH_PROXY, "MyRealm1", HttpAuth::AUTH_SCHEME_BASIC,
-      "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
+      NetworkIsolationKey(), "Basic realm=MyRealm1",
+      AuthCredentials(kFoo, kBar), "/");
 
   for (IoMode io_mode : {SYNCHRONOUS, ASYNC}) {
     SCOPED_TRACE(io_mode);
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index e7e6d24..0528ad2d 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -175,6 +175,8 @@
         user_agent_(kUserAgent),
         proxy_host_port_(kProxyHost, kProxyPort),
         endpoint_host_port_(kOriginHost, kOriginPort),
+        http_auth_cache_(
+            false /* key_server_entries_by_network_isolation_key */),
         host_resolver_(new MockCachingHostResolver()),
         http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()) {
     IPAddress ip(192, 0, 2, 33);
@@ -294,7 +296,8 @@
         endpoint_host_port_, net_log_.bound(),
         new HttpAuthController(
             HttpAuth::AUTH_PROXY,
-            GURL("https://" + proxy_host_port_.ToString()), &http_auth_cache_,
+            GURL("https://" + proxy_host_port_.ToString()),
+            NetworkIsolationKey(), &http_auth_cache_,
             http_auth_handler_factory_.get(), host_resolver_.get(),
             HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS)));
 
@@ -681,8 +684,9 @@
   const base::string16 kFoo(base::ASCIIToUTF16("foo"));
   const base::string16 kBar(base::ASCIIToUTF16("bar"));
   http_auth_cache_.Add(GURL(kProxyUrl), HttpAuth::AUTH_PROXY, "MyRealm1",
-                       HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=MyRealm1",
-                       AuthCredentials(kFoo, kBar), "/");
+                       HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+                       "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar),
+                       "/");
 
   AssertConnectSucceeds();
 
diff --git a/net/socket/ssl_connect_job_unittest.cc b/net/socket/ssl_connect_job_unittest.cc
index 8355317..3f2b444 100644
--- a/net/socket/ssl_connect_job_unittest.cc
+++ b/net/socket/ssl_connect_job_unittest.cc
@@ -131,8 +131,8 @@
     const base::string16 kBar(base::ASCIIToUTF16("bar"));
     session_->http_auth_cache()->Add(
         GURL("http://proxy:443/"), HttpAuth::AUTH_PROXY, "MyRealm1",
-        HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=MyRealm1",
-        AuthCredentials(kFoo, kBar), "/");
+        HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+        "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
   }
 
   HttpNetworkSession* CreateNetworkSession() {
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 38551881..3a666c85 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -158,10 +158,10 @@
   void AddAuthToCache() {
     const base::string16 kFoo(base::ASCIIToUTF16("foo"));
     const base::string16 kBar(base::ASCIIToUTF16("bar"));
-    session_->http_auth_cache()->Add(GURL(kProxyUrl), HttpAuth::AUTH_PROXY,
-                                     "MyRealm1", HttpAuth::AUTH_SCHEME_BASIC,
-                                     "Basic realm=MyRealm1",
-                                     AuthCredentials(kFoo, kBar), "/");
+    session_->http_auth_cache()->Add(
+        GURL(kProxyUrl), HttpAuth::AUTH_PROXY, "MyRealm1",
+        HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey(),
+        "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/");
   }
 
   void ResumeAndRun() {
@@ -269,8 +269,8 @@
       spdy_stream, user_agent_, endpoint_host_port_pair_, net_log_.bound(),
       new HttpAuthController(
           HttpAuth::AUTH_PROXY, GURL("https://" + proxy_host_port_.ToString()),
-          session_->http_auth_cache(), session_->http_auth_handler_factory(),
-          session_->host_resolver(),
+          NetworkIsolationKey(), session_->http_auth_cache(),
+          session_->http_auth_handler_factory(), session_->host_resolver(),
           HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS));
 }
 
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 379530c..791b27c1 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -344,8 +344,8 @@
       net_log(nullptr),
       disable_idle_sockets_close_on_memory_pressure(false),
       enable_early_data(false),
-      allow_default_credentials(
-          HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS) {
+      allow_default_credentials(HttpAuthPreferences::ALLOW_DEFAULT_CREDENTIALS),
+      key_auth_cache_server_entries_by_network_isolation_key(false) {
   http2_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
       kDefaultInitialWindowSize;
 }
@@ -400,6 +400,8 @@
       session_deps->disable_idle_sockets_close_on_memory_pressure;
   params.enable_early_data = session_deps->enable_early_data;
   params.allow_default_credentials = session_deps->allow_default_credentials;
+  params.key_auth_cache_server_entries_by_network_isolation_key =
+      session_deps->key_auth_cache_server_entries_by_network_isolation_key;
   return params;
 }
 
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index 304584b3..18c10087 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -238,6 +238,7 @@
   bool disable_idle_sockets_close_on_memory_pressure;
   bool enable_early_data;
   HttpAuthPreferences::DefaultCredentials allow_default_credentials;
+  bool key_auth_cache_server_entries_by_network_isolation_key;
 };
 
 class SpdyURLRequestContext : public URLRequestContext {
diff --git a/net/test/embedded_test_server/default_handlers.cc b/net/test/embedded_test_server/default_handlers.cc
index 5c63587..b07db80 100644
--- a/net/test/embedded_test_server/default_handlers.cc
+++ b/net/test/embedded_test_server/default_handlers.cc
@@ -315,6 +315,25 @@
   return http_response;
 }
 
+// /iframe?URL
+// Returns a page that iframes the specified URL.
+std::unique_ptr<HttpResponse> HandleIframe(const HttpRequest& request) {
+  GURL request_url = request.GetURL();
+
+  auto http_response = std::make_unique<BasicHttpResponse>();
+  http_response->set_content_type("text/html");
+
+  GURL iframe_url("about:blank");
+  if (request_url.has_query()) {
+    iframe_url = GURL(UnescapeBinaryURLComponent(request_url.query()));
+  }
+
+  http_response->set_content(
+      base::StringPrintf("<html><body><iframe src=\"%s\"></body></html>",
+                         iframe_url.spec().c_str()));
+  return http_response;
+}
+
 // /nocontent
 // Returns a NO_CONTENT response.
 std::unique_ptr<HttpResponse> HandleNoContent(const HttpRequest& request) {
@@ -799,6 +818,7 @@
       PREFIXED_HANDLER("/expect-and-set-cookie", &HandleExpectAndSetCookie));
   server->RegisterDefaultHandler(
       PREFIXED_HANDLER("/set-header", &HandleSetHeader));
+  server->RegisterDefaultHandler(PREFIXED_HANDLER("/iframe", &HandleIframe));
   server->RegisterDefaultHandler(
       PREFIXED_HANDLER("/nocontent", &HandleNoContent));
   server->RegisterDefaultHandler(
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 20add8e..cf407a7 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -6965,6 +6965,76 @@
   EXPECT_EQ(1, network_delegate.set_cookie_count());
 }
 
+// Tests that |key_auth_cache_by_network_isolation_key| is respected.
+TEST_F(URLRequestTestHTTP, AuthWithNetworkIsolationKey) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  ASSERT_TRUE(http_test_server()->Start());
+
+  for (bool key_auth_cache_by_network_isolation_key : {false, true}) {
+    TestURLRequestContext url_request_context(true /* delay_initialization */);
+    std::unique_ptr<HttpNetworkSession::Params> http_network_session_params =
+        std::make_unique<HttpNetworkSession::Params>();
+    http_network_session_params
+        ->key_auth_cache_server_entries_by_network_isolation_key =
+        key_auth_cache_by_network_isolation_key;
+    url_request_context.set_http_network_session_params(
+        std::move(http_network_session_params));
+    url_request_context.Init();
+
+    // Populate the auth cache using one NetworkIsolationKey.
+    {
+      TestDelegate d;
+      GURL url(base::StringPrintf(
+          "http://%s:%s@%s/auth-basic", base::UTF16ToASCII(kUser).c_str(),
+          base::UTF16ToASCII(kSecret).c_str(),
+          http_test_server()->host_port_pair().ToString().c_str()));
+
+      std::unique_ptr<URLRequest> r(url_request_context.CreateRequest(
+          url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+      r->SetLoadFlags(LOAD_BYPASS_CACHE);
+      r->set_network_isolation_key(kNetworkIsolationKey1);
+      r->Start();
+
+      d.RunUntilComplete();
+      EXPECT_THAT(d.request_status(), IsOk());
+      ASSERT_TRUE(r->response_headers());
+      EXPECT_EQ(200, r->response_headers()->response_code());
+      EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos);
+    }
+
+    // Make a request with another NetworkIsolationKey. This may or may not use
+    // the cached auth credentials, depending on whether or not the
+    // HttpAuthCache is configured to respect the NetworkIsolationKey.
+    {
+      TestDelegate d;
+
+      std::unique_ptr<URLRequest> r(url_request_context.CreateRequest(
+          http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d,
+          TRAFFIC_ANNOTATION_FOR_TESTS));
+      r->SetLoadFlags(LOAD_BYPASS_CACHE);
+      r->set_network_isolation_key(kNetworkIsolationKey2);
+      r->Start();
+
+      d.RunUntilComplete();
+
+      EXPECT_THAT(d.request_status(), IsOk());
+      ASSERT_TRUE(r->response_headers());
+      if (key_auth_cache_by_network_isolation_key) {
+        EXPECT_EQ(401, r->response_headers()->response_code());
+      } else {
+        EXPECT_EQ(200, r->response_headers()->response_code());
+      }
+
+      EXPECT_EQ(!key_auth_cache_by_network_isolation_key,
+                d.data_received().find("user/secret") != std::string::npos);
+    }
+  }
+}
+
 TEST_F(URLRequestTest, ReportCookieActivity) {
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
diff --git a/services/device/geolocation/geolocation_service_unittest.cc b/services/device/geolocation/geolocation_service_unittest.cc
index 9e41e254..8e1a16a1 100644
--- a/services/device/geolocation/geolocation_service_unittest.cc
+++ b/services/device/geolocation/geolocation_service_unittest.cc
@@ -112,12 +112,8 @@
 #endif
 
 // TODO(https://crbug.com/912057): Flaky on Chrome OS / Fails often on *San.
-#if defined(OS_CHROMEOS)
-#define MAYBE_GeolocationConfig DISABLED_GeolocationConfig
-#else
-#define MAYBE_GeolocationConfig GeolocationConfig
-#endif
-TEST_F(GeolocationServiceUnitTest, MAYBE_GeolocationConfig) {
+// TODO(https://crbug.com/999409): Also flaky on other platforms.
+TEST_F(GeolocationServiceUnitTest, DISABLED_GeolocationConfig) {
   BindGeolocationConfig();
   {
     base::RunLoop run_loop;
diff --git a/services/network/http_auth_cache_copier.cc b/services/network/http_auth_cache_copier.cc
index 7f2697a..5db1cf0 100644
--- a/services/network/http_auth_cache_copier.cc
+++ b/services/network/http_auth_cache_copier.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/http_auth_cache_copier.h"
 
+#include "base/logging.h"
 #include "net/http/http_auth_cache.h"
 
 namespace network {
@@ -14,7 +15,11 @@
 base::UnguessableToken HttpAuthCacheCopier::SaveHttpAuthCache(
     const net::HttpAuthCache& cache) {
   base::UnguessableToken key = base::UnguessableToken::Create();
-  caches_[key].UpdateAllFrom(cache);
+  auto cache_it = caches_.emplace(std::make_pair(
+      key, std::make_unique<net::HttpAuthCache>(
+               cache.key_server_entries_by_network_isolation_key())));
+  DCHECK(cache_it.second);
+  cache_it.first->second->UpdateAllFrom(cache);
   return key;
 }
 
@@ -25,7 +30,12 @@
     DLOG(ERROR) << "Unknown HttpAuthCache key: " << key;
     return;
   }
-  cache->UpdateAllFrom(it->second);
+
+  // Source and destination caches must have the same configuration.
+  DCHECK_EQ(cache->key_server_entries_by_network_isolation_key(),
+            it->second->key_server_entries_by_network_isolation_key());
+
+  cache->UpdateAllFrom(*it->second);
   caches_.erase(it);
 }
 
diff --git a/services/network/http_auth_cache_copier.h b/services/network/http_auth_cache_copier.h
index 2aa008d5..3af7be75 100644
--- a/services/network/http_auth_cache_copier.h
+++ b/services/network/http_auth_cache_copier.h
@@ -6,6 +6,7 @@
 #define SERVICES_NETWORK_HTTP_AUTH_CACHE_COPIER_H_
 
 #include <map>
+#include <memory>
 
 #include "base/macros.h"
 #include "base/unguessable_token.h"
@@ -36,7 +37,7 @@
                          net::HttpAuthCache* cache);
 
  private:
-  std::map<base::UnguessableToken, net::HttpAuthCache> caches_;
+  std::map<base::UnguessableToken, std::unique_ptr<net::HttpAuthCache>> caches_;
 
   DISALLOW_COPY_AND_ASSIGN(HttpAuthCacheCopier);
 };
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index aaeab0a2..2d553d92 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1487,9 +1487,11 @@
   std::move(callback).Run();
 }
 
-void NetworkContext::AddAuthCacheEntry(const net::AuthChallengeInfo& challenge,
-                                       const net::AuthCredentials& credentials,
-                                       AddAuthCacheEntryCallback callback) {
+void NetworkContext::AddAuthCacheEntry(
+    const net::AuthChallengeInfo& challenge,
+    const net::NetworkIsolationKey& network_isolation_key,
+    const net::AuthCredentials& credentials,
+    AddAuthCacheEntryCallback callback) {
   if (challenge.challenger.scheme() == url::kFtpScheme) {
 #if !BUILDFLAG(DISABLE_FTP_SUPPORT)
     net::FtpAuthCache* auth_cache = url_request_context_->ftp_auth_cache();
@@ -1507,7 +1509,8 @@
                                             : net::HttpAuth::AUTH_SERVER,
                          challenge.realm,
                          net::HttpAuth::StringToScheme(challenge.scheme),
-                         challenge.challenge, credentials, challenge.path);
+                         network_isolation_key, challenge.challenge,
+                         credentials, challenge.path);
   }
   std::move(callback).Run();
 }
@@ -1519,8 +1522,12 @@
       url_request_context_->http_transaction_factory()
           ->GetSession()
           ->http_auth_cache();
-  net::HttpAuthCache::Entry* entry = http_auth_cache->LookupByPath(
-      url.GetOrigin(), net::HttpAuth::AUTH_SERVER, url.path());
+  // TODO(mmenke): Make NetworkIsolationKey an argument to this method. The one
+  // consumer of it is associated with a ResourceRequest, so can have the
+  // consumer grab it from there.
+  net::HttpAuthCache::Entry* entry =
+      http_auth_cache->LookupByPath(url.GetOrigin(), net::HttpAuth::AUTH_SERVER,
+                                    net::NetworkIsolationKey(), url.path());
   if (entry && entry->scheme() == net::HttpAuth::AUTH_SCHEME_BASIC)
     std::move(callback).Run(entry->credentials());
   else
@@ -1820,6 +1827,11 @@
   session_params.disable_idle_sockets_close_on_memory_pressure =
       params_->disable_idle_sockets_close_on_memory_pressure;
 
+  if (network_service_) {
+    session_params.key_auth_cache_server_entries_by_network_isolation_key =
+        network_service_->split_auth_cache_by_network_isolation_key();
+  }
+
   builder.set_http_network_session_params(session_params);
 
   builder.SetCreateHttpTransactionFactoryCallback(
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 17be40c..4378529b 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -68,6 +68,7 @@
 class CertVerifier;
 class CertVerifyProc;
 class HostPortPair;
+class NetworkIsolationKey;
 class ReportSender;
 class StaticHttpUserAgentSettings;
 class URLRequestContext;
@@ -358,6 +359,7 @@
   void LoadHttpAuthCache(const base::UnguessableToken& cache_key,
                          LoadHttpAuthCacheCallback callback) override;
   void AddAuthCacheEntry(const net::AuthChallengeInfo& challenge,
+                         const net::NetworkIsolationKey& network_isolation_key,
                          const net::AuthCredentials& credentials,
                          AddAuthCacheEntryCallback callback) override;
   // TODO(mmenke): Rename this method and update Mojo docs to make it clear this
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 9661505..7a48882c 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -1570,19 +1570,21 @@
   base::string16 user = base::ASCIIToUTF16("user");
   base::string16 password = base::ASCIIToUTF16("pass");
   cache->Add(origin, net::HttpAuth::AUTH_SERVER, "Realm1",
-             net::HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm1",
-             net::AuthCredentials(user, password), "/");
+             net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+             "basic realm=Realm1", net::AuthCredentials(user, password), "/");
 
   test_clock.Advance(base::TimeDelta::FromHours(1));  // Time now 13:00
   cache->Add(origin, net::HttpAuth::AUTH_PROXY, "Realm2",
-             net::HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm2",
-             net::AuthCredentials(user, password), "/");
+             net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+             "basic realm=Realm2", net::AuthCredentials(user, password), "/");
 
   ASSERT_EQ(2u, cache->GetEntriesSizeForTesting());
   ASSERT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, "Realm1",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
   ASSERT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, "Realm2",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
 
   base::RunLoop run_loop;
   base::Time test_time;
@@ -1592,9 +1594,11 @@
 
   EXPECT_EQ(1u, cache->GetEntriesSizeForTesting());
   EXPECT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, "Realm1",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
   EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, "Realm2",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
 }
 
 TEST_F(NetworkContextTest, ClearAllHttpAuthCache) {
@@ -1615,19 +1619,21 @@
   base::string16 user = base::ASCIIToUTF16("user");
   base::string16 password = base::ASCIIToUTF16("pass");
   cache->Add(origin, net::HttpAuth::AUTH_SERVER, "Realm1",
-             net::HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm1",
-             net::AuthCredentials(user, password), "/");
+             net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+             "basic realm=Realm1", net::AuthCredentials(user, password), "/");
 
   test_clock.Advance(base::TimeDelta::FromHours(1));  // Time now 13:00
   cache->Add(origin, net::HttpAuth::AUTH_PROXY, "Realm2",
-             net::HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm2",
-             net::AuthCredentials(user, password), "/");
+             net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+             "basic realm=Realm2", net::AuthCredentials(user, password), "/");
 
   ASSERT_EQ(2u, cache->GetEntriesSizeForTesting());
   ASSERT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, "Realm1",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
   ASSERT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, "Realm2",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
 
   base::RunLoop run_loop;
   network_context->ClearHttpAuthCache(base::Time(), run_loop.QuitClosure());
@@ -1635,9 +1641,11 @@
 
   EXPECT_EQ(0u, cache->GetEntriesSizeForTesting());
   EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, "Realm1",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
   EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, "Realm2",
-                                   net::HttpAuth::AUTH_SCHEME_BASIC));
+                                   net::HttpAuth::AUTH_SCHEME_BASIC,
+                                   net::NetworkIsolationKey()));
 }
 
 TEST_F(NetworkContextTest, ClearEmptyHttpAuthCache) {
@@ -1672,11 +1680,11 @@
   base::string16 user = base::ASCIIToUTF16("user");
   base::string16 password = base::ASCIIToUTF16("pass");
   cache->Add(origin, net::HttpAuth::AUTH_SERVER, "Realm",
-             net::HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm",
-             net::AuthCredentials(user, password), "/");
+             net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+             "basic realm=Realm", net::AuthCredentials(user, password), "/");
   cache->Add(origin2, net::HttpAuth::AUTH_PROXY, "Realm",
-             net::HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm",
-             net::AuthCredentials(user, password), "/");
+             net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(),
+             "basic realm=Realm", net::AuthCredentials(user, password), "/");
 
   base::RunLoop run_loop1;
   base::Optional<net::AuthCredentials> result;
@@ -5726,7 +5734,7 @@
       network_context->url_request_context()->ftp_auth_cache()->Lookup(url));
   base::RunLoop run_loop;
   network_context->AddAuthCacheEntry(
-      challenge,
+      challenge, net::NetworkIsolationKey(),
       net::AuthCredentials(base::ASCIIToUTF16(kUsername),
                            base::ASCIIToUTF16(kPassword)),
       run_loop.QuitClosure());
@@ -5815,6 +5823,9 @@
                                   ->GetSession()
                                   ->http_auth_cache();
   ASSERT_TRUE(cache);
+  // |key_server_entries_by_network_isolation_key| should be disabled by
+  // default, so the passed in NetworkIsolationKeys don't matter.
+  EXPECT_FALSE(cache->key_server_entries_by_network_isolation_key());
 
   // Add an AUTH_SERVER cache entry.
   GURL url("http://example.test/");
@@ -5826,17 +5837,18 @@
   const char kUsername[] = "test_user";
   const char kPassword[] = "test_pass";
   ASSERT_FALSE(cache->Lookup(url, net::HttpAuth::AUTH_SERVER, challenge.realm,
-                             net::HttpAuth::AUTH_SCHEME_BASIC));
+                             net::HttpAuth::AUTH_SCHEME_BASIC,
+                             net::NetworkIsolationKey()));
   base::RunLoop run_loop;
   network_context->AddAuthCacheEntry(
-      challenge,
+      challenge, net::NetworkIsolationKey(),
       net::AuthCredentials(base::ASCIIToUTF16(kUsername),
                            base::ASCIIToUTF16(kPassword)),
       run_loop.QuitClosure());
   run_loop.Run();
-  net::HttpAuthCache::Entry* entry =
-      cache->Lookup(url, net::HttpAuth::AUTH_SERVER, challenge.realm,
-                    net::HttpAuth::AUTH_SCHEME_BASIC);
+  net::HttpAuthCache::Entry* entry = cache->Lookup(
+      url, net::HttpAuth::AUTH_SERVER, challenge.realm,
+      net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(url, entry->origin());
   EXPECT_EQ(challenge.realm, entry->realm());
@@ -5845,7 +5857,8 @@
   EXPECT_EQ(base::ASCIIToUTF16(kPassword), entry->credentials().password());
   // Entry should only have been added for server auth.
   EXPECT_FALSE(cache->Lookup(url, net::HttpAuth::AUTH_PROXY, challenge.realm,
-                             net::HttpAuth::AUTH_SCHEME_BASIC));
+                             net::HttpAuth::AUTH_SCHEME_BASIC,
+                             net::NetworkIsolationKey()));
 
   // Add an AUTH_PROXY cache entry.
   GURL proxy_url("http://proxy.test/");
@@ -5854,17 +5867,18 @@
   const char kProxyUsername[] = "test_proxy_user";
   const char kProxyPassword[] = "test_proxy_pass";
   ASSERT_FALSE(cache->Lookup(proxy_url, net::HttpAuth::AUTH_PROXY,
-                             challenge.realm,
-                             net::HttpAuth::AUTH_SCHEME_BASIC));
+                             challenge.realm, net::HttpAuth::AUTH_SCHEME_BASIC,
+                             net::NetworkIsolationKey()));
   base::RunLoop run_loop2;
   network_context->AddAuthCacheEntry(
-      challenge,
+      challenge, net::NetworkIsolationKey(),
       net::AuthCredentials(base::ASCIIToUTF16(kProxyUsername),
                            base::ASCIIToUTF16(kProxyPassword)),
       run_loop2.QuitClosure());
   run_loop2.Run();
   entry = cache->Lookup(proxy_url, net::HttpAuth::AUTH_PROXY, challenge.realm,
-                        net::HttpAuth::AUTH_SCHEME_BASIC);
+                        net::HttpAuth::AUTH_SCHEME_BASIC,
+                        net::NetworkIsolationKey());
   ASSERT_TRUE(entry);
   EXPECT_EQ(proxy_url, entry->origin());
   EXPECT_EQ(challenge.realm, entry->realm());
@@ -5875,8 +5889,58 @@
             entry->credentials().password());
   // Entry should only have been added for proxy auth.
   EXPECT_FALSE(cache->Lookup(proxy_url, net::HttpAuth::AUTH_SERVER,
-                             challenge.realm,
-                             net::HttpAuth::AUTH_SCHEME_BASIC));
+                             challenge.realm, net::HttpAuth::AUTH_SCHEME_BASIC,
+                             net::NetworkIsolationKey()));
+}
+
+TEST_F(NetworkContextTest, AddHttpAuthCacheEntryWithNetworkIsolationKey) {
+  network_service()->SetSplitAuthCacheByNetworkIsolationKey(true);
+
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(CreateContextParams());
+
+  net::HttpAuthCache* cache = network_context->url_request_context()
+                                  ->http_transaction_factory()
+                                  ->GetSession()
+                                  ->http_auth_cache();
+  ASSERT_TRUE(cache);
+  // If this isn't true, the rest of this test is pretty meaningless.
+  ASSERT_TRUE(cache->key_server_entries_by_network_isolation_key());
+
+  // Add an AUTH_SERVER cache entry.
+  GURL url("http://example.test/");
+  url::Origin origin = url::Origin::Create(url);
+  net::NetworkIsolationKey network_isolation_key(origin, origin);
+  net::AuthChallengeInfo challenge;
+  challenge.is_proxy = false;
+  challenge.challenger = origin;
+  challenge.scheme = "basic";
+  challenge.realm = "testrealm";
+  const char kUsername[] = "test_user";
+  const char kPassword[] = "test_pass";
+  ASSERT_FALSE(cache->Lookup(url, net::HttpAuth::AUTH_SERVER, challenge.realm,
+                             net::HttpAuth::AUTH_SCHEME_BASIC,
+                             network_isolation_key));
+  base::RunLoop run_loop;
+  network_context->AddAuthCacheEntry(
+      challenge, network_isolation_key,
+      net::AuthCredentials(base::ASCIIToUTF16(kUsername),
+                           base::ASCIIToUTF16(kPassword)),
+      run_loop.QuitClosure());
+  run_loop.Run();
+  net::HttpAuthCache::Entry* entry =
+      cache->Lookup(url, net::HttpAuth::AUTH_SERVER, challenge.realm,
+                    net::HttpAuth::AUTH_SCHEME_BASIC, network_isolation_key);
+  ASSERT_TRUE(entry);
+  EXPECT_EQ(url, entry->origin());
+  EXPECT_EQ(challenge.realm, entry->realm());
+  EXPECT_EQ(net::HttpAuth::StringToScheme(challenge.scheme), entry->scheme());
+  EXPECT_EQ(base::ASCIIToUTF16(kUsername), entry->credentials().username());
+  EXPECT_EQ(base::ASCIIToUTF16(kPassword), entry->credentials().password());
+  // Entry should only be accessibly when using the correct NetworkIsolationKey.
+  EXPECT_FALSE(cache->Lookup(url, net::HttpAuth::AUTH_SERVER, challenge.realm,
+                             net::HttpAuth::AUTH_SCHEME_BASIC,
+                             net::NetworkIsolationKey()));
 }
 
 TEST_F(NetworkContextTest, HSTSPolicyBypassList) {
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 3954b14..10d1e215 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -656,6 +656,13 @@
     env->SetVar(variable->name, variable->value);
 }
 
+void NetworkService::SetSplitAuthCacheByNetworkIsolationKey(
+    bool split_auth_cache_by_network_isolation_key) {
+  DCHECK(network_contexts_.empty());
+  split_auth_cache_by_network_isolation_key_ =
+      split_auth_cache_by_network_isolation_key;
+}
+
 #if defined(OS_ANDROID)
 void NetworkService::DumpWithoutCrashing(base::Time dump_request_time) {
   static base::debug::CrashKeyString* time_key =
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 1218342..ac17779e 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -166,6 +166,8 @@
 #endif
   void SetEnvironment(
       std::vector<mojom::EnvironmentVariablePtr> environment) override;
+  void SetSplitAuthCacheByNetworkIsolationKey(
+      bool split_auth_cache_by_network_isolation_key) override;
 #if defined(OS_ANDROID)
   void DumpWithoutCrashing(base::Time dump_request_time) override;
 #endif
@@ -216,6 +218,10 @@
     host_resolver_factory_ = std::move(host_resolver_factory);
   }
 
+  bool split_auth_cache_by_network_isolation_key() const {
+    return split_auth_cache_by_network_isolation_key_;
+  }
+
   static NetworkService* GetNetworkServiceForTesting();
 
  private:
@@ -314,6 +320,10 @@
   // A timer that periodically calls ReportMetrics every hour.
   base::RepeatingTimer metrics_trigger_timer_;
 
+  // Whether new NetworkContexts will be configured to partition their
+  // HttpAuthCaches by NetworkIsolationKey.
+  bool split_auth_cache_by_network_isolation_key_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkService);
 };
 
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index 71ff6555..5a92e9b 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -38,6 +38,8 @@
 #include "net/dns/public/dns_protocol.h"
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_auth_scheme.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_transaction_factory.h"
 #include "net/net_buildflags.h"
 #include "net/proxy_resolution/proxy_config.h"
 #include "net/socket/client_socket_pool_manager.h"
@@ -1636,6 +1638,32 @@
   }
 }
 
+TEST_F(NetworkServiceTest, SplitAuthCacheByNetworkIsolationKey) {
+  service()->SetSplitAuthCacheByNetworkIsolationKey(true);
+  mojo::Remote<mojom::NetworkContext> network_context_remote;
+  NetworkContext network_context(
+      service(), network_context_remote.BindNewPipeAndPassReceiver(),
+      CreateContextParams());
+  EXPECT_TRUE(network_context.url_request_context()
+                  ->http_transaction_factory()
+                  ->GetSession()
+                  ->params()
+                  .key_auth_cache_server_entries_by_network_isolation_key);
+}
+
+TEST_F(NetworkServiceTest, NoSplitAuthCacheByNetworkIsolationKey) {
+  service()->SetSplitAuthCacheByNetworkIsolationKey(false);
+  mojo::Remote<mojom::NetworkContext> network_context_remote;
+  NetworkContext network_context(
+      service(), network_context_remote.BindNewPipeAndPassReceiver(),
+      CreateContextParams());
+  EXPECT_FALSE(network_context.url_request_context()
+                   ->http_transaction_factory()
+                   ->GetSession()
+                   ->params()
+                   .key_auth_cache_server_entries_by_network_isolation_key);
+}
+
 }  // namespace
 
 }  // namespace network
diff --git a/services/network/proxy_resolving_client_socket_unittest.cc b/services/network/proxy_resolving_client_socket_unittest.cc
index 5570943..fc1fe29 100644
--- a/services/network/proxy_resolving_client_socket_unittest.cc
+++ b/services/network/proxy_resolving_client_socket_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/proxy_resolution/mock_proxy_resolver.h"
@@ -467,14 +468,14 @@
 
   auth_cache->Add(GURL("http://bad:99"), net::HttpAuth::AUTH_PROXY,
                   "test_realm", net::HttpAuth::AUTH_SCHEME_BASIC,
-                  "Basic realm=\"test_realm\"",
+                  net::NetworkIsolationKey(), "Basic realm=\"test_realm\"",
                   net::AuthCredentials(base::ASCIIToUTF16("user"),
                                        base::ASCIIToUTF16("password")),
                   std::string());
 
   auth_cache->Add(GURL("http://bad:99"), net::HttpAuth::AUTH_PROXY,
                   "test_realm2", net::HttpAuth::AUTH_SCHEME_BASIC,
-                  "Basic realm=\"test_realm2\"",
+                  net::NetworkIsolationKey(), "Basic realm=\"test_realm2\"",
                   net::AuthCredentials(base::ASCIIToUTF16("user2"),
                                        base::ASCIIToUTF16("password2")),
                   std::string());
@@ -531,7 +532,7 @@
   // origin + realm + scheme lookup.
   auth_cache->Add(GURL("http://bad:99"), net::HttpAuth::AUTH_PROXY,
                   "test_realm", net::HttpAuth::AUTH_SCHEME_BASIC,
-                  "Basic realm=\"test_realm\"",
+                  net::NetworkIsolationKey(), "Basic realm=\"test_realm\"",
                   net::AuthCredentials(base::ASCIIToUTF16("user"),
                                        base::ASCIIToUTF16("password")),
                   std::string());
@@ -565,7 +566,7 @@
   // origin + realm + scheme lookup.
   auth_cache->Add(GURL("http://bad:99"), net::HttpAuth::AUTH_PROXY,
                   "test_realm", net::HttpAuth::AUTH_SCHEME_BASIC,
-                  "Basic realm=\"test_realm\"",
+                  net::NetworkIsolationKey(), "Basic realm=\"test_realm\"",
                   net::AuthCredentials(base::ASCIIToUTF16("user"),
                                        base::ASCIIToUTF16("password")),
                   std::string());
@@ -629,7 +630,7 @@
 
   auth_cache->Add(GURL("http://bad:99"), net::HttpAuth::AUTH_PROXY,
                   "test_realm", net::HttpAuth::AUTH_SCHEME_BASIC,
-                  "Basic realm=\"test_realm\"",
+                  net::NetworkIsolationKey(), "Basic realm=\"test_realm\"",
                   net::AuthCredentials(base::ASCIIToUTF16("user"),
                                        base::ASCIIToUTF16("password")),
                   "/");
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index b60048c..e014b8c 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -1181,10 +1181,15 @@
   LoadHttpAuthCache(mojo_base.mojom.UnguessableToken cache_key) => ();
 
   // Adds an entry to the HttpAuthCache or FtpAuthCache (determined by whether
-  // the |challenger| field within |challenge| is an ftp:// URL). |challenge|
-  // may not necessarily contain a stateful challenge that requires a persistent
-  // connection, allowing the cache to be pre-populated.
+  // the |challenger| field within |challenge| is an ftp:// URL).
+  // |network_isolation_key| is the NetworkIsolationKey to restrict the
+  // credentials to, and is only respected for server (not proxy) HTTP auth
+  // and only when the NetworkService was configured to split the auth cache by
+  // NetworkIsolationKey. |challenge| may not necessarily contain a stateful
+  // challenge that requires a persistent connection, allowing the cache to be
+  // pre-populated.
   AddAuthCacheEntry(AuthChallengeInfo challenge,
+                    NetworkIsolationKey network_isolation_key,
                     AuthCredentials credentials) => ();
 
   // Looks up credentials in the HttpAuthCache using the origin and path from
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index c1ae348..4a1dc2e 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -338,6 +338,12 @@
   // |environment| array.
   SetEnvironment(array<EnvironmentVariable> environment);
 
+  // Sets whether the HTTP auth cache will be split the NetworkIsolationKey.
+  // Only affects server (not proxy) credentials. May only be called before any
+  // NetworkContexts have been created. Defaults to false.
+  SetSplitAuthCacheByNetworkIsolationKey(
+      bool split_auth_cache_by_network_isolation_key);
+
   // Calls base::debug::DumpWithoutCrashing for the network process.
   // TODO(http://crbug.com/934317): Remove this once done debugging renderer
   // hangs.
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 0c05d9a8..046b2df 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -235,6 +235,7 @@
   void LoadHttpAuthCache(const base::UnguessableToken& cache_key,
                          LoadHttpAuthCacheCallback callback) override {}
   void AddAuthCacheEntry(const net::AuthChallengeInfo& challenge,
+                         const net::NetworkIsolationKey& network_isolation_key,
                          const net::AuthCredentials& credentials,
                          AddAuthCacheEntryCallback callback) override {}
   void LookupBasicAuthCredentials(
diff --git a/services/network/udp_socket_unittest.cc b/services/network/udp_socket_unittest.cc
index f6cc0b9..d7a9f51 100644
--- a/services/network/udp_socket_unittest.cc
+++ b/services/network/udp_socket_unittest.cc
@@ -279,9 +279,18 @@
   EXPECT_EQ(net::ERR_UNEXPECTED, result);
 }
 
+// TODO(crbug.com/1014916): These two tests are very flaky on Fuchsia.
+#if defined(OS_FUCHSIA)
+#define MAYBE_TestReadSendTo DISABLED_TestReadSendTo
+#define MAYBE_TestUnexpectedSequences DISABLED_TestUnexpectedSequences
+#else
+#define MAYBE_TestReadSendTo TestReadSendTo
+#define MAYBE_TestUnexpectedSequences TestUnexpectedSequences
+#endif
+
 // Tests that the sequence of calling Bind()/Connect() and setters is
 // important.
-TEST_F(UDPSocketTest, TestUnexpectedSequences) {
+TEST_F(UDPSocketTest, MAYBE_TestUnexpectedSequences) {
   mojo::Remote<mojom::UDPSocket> socket_remote;
   factory()->CreateUDPSocket(socket_remote.BindNewPipeAndPassReceiver(),
                              mojo::NullRemote());
@@ -487,7 +496,7 @@
   EXPECT_EQ(msg, result.data.value());
 }
 
-TEST_F(UDPSocketTest, TestReadSendTo) {
+TEST_F(UDPSocketTest, MAYBE_TestReadSendTo) {
   // Create a server socket to send data.
   mojo::Remote<mojom::UDPSocket> server_socket;
   factory()->CreateUDPSocket(server_socket.BindNewPipeAndPassReceiver(),
diff --git a/services/tracing/perfetto/json_exporter_main.cc b/services/tracing/perfetto/json_exporter_main.cc
index 90868ec..e3c65402 100644
--- a/services/tracing/perfetto/json_exporter_main.cc
+++ b/services/tracing/perfetto/json_exporter_main.cc
@@ -42,7 +42,8 @@
   std::vector<perfetto::TracePacket> packets;
   for (auto it = decoder.packet(); !!it; ++it) {
     perfetto::TracePacket trace_packet;
-    trace_packet.AddSlice(it->data(), it->size());
+    auto const_bytes = *it;
+    trace_packet.AddSlice(const_bytes.data, const_bytes.size);
     packets.emplace_back(std::move(trace_packet));
   }
   exporter.OnTraceData(std::move(packets), false);
diff --git a/services/tracing/perfetto/privacy_filtering_check.cc b/services/tracing/perfetto/privacy_filtering_check.cc
index 4ca40f4..6dacb68 100644
--- a/services/tracing/perfetto/privacy_filtering_check.cc
+++ b/services/tracing/perfetto/privacy_filtering_check.cc
@@ -76,7 +76,7 @@
       serialized_trace_proto.size());
 
   for (auto it = trace.packet(); !!it; ++it) {
-    TracePacket::Decoder packet(it->data(), it->size());
+    TracePacket::Decoder packet(*it);
     const MessageInfo* root = &kTracePacket;
     VerifyProto(root, &packet);
 
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index a63bf2b6..97efe6d 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -512,9 +512,10 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0532",
+              "gpu": "102b:0532-6.1.7600.16385",
               "os": "Windows-2008ServerR2-SP1",
-              "pool": "chrome.tests.perf"
+              "pool": "chrome.tests.perf",
+              "synthetic_product_name": "PowerEdge R210 II (Dell Inc.)"
             }
           ],
           "expiration": 7200,
@@ -549,9 +550,10 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0532",
+              "gpu": "102b:0532-6.1.7600.16385",
               "os": "Windows-2008ServerR2-SP1",
-              "pool": "chrome.tests.perf"
+              "pool": "chrome.tests.perf",
+              "synthetic_product_name": "PowerEdge R210 II (Dell Inc.)"
             }
           ],
           "expiration": 7200,
@@ -586,9 +588,10 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0532",
+              "gpu": "102b:0532-6.1.7600.16385",
               "os": "Windows-2008ServerR2-SP1",
-              "pool": "chrome.tests.perf"
+              "pool": "chrome.tests.perf",
+              "synthetic_product_name": "PowerEdge R210 II (Dell Inc.)"
             }
           ],
           "expiration": 7200,
@@ -626,9 +629,10 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "102b:0532",
+              "gpu": "102b:0532-6.1.7600.16385",
               "os": "Windows-2008ServerR2-SP1",
-              "pool": "chrome.tests.perf"
+              "pool": "chrome.tests.perf",
+              "synthetic_product_name": "PowerEdge R210 II (Dell Inc.)"
             }
           ],
           "expiration": 7200,
diff --git a/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter b/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
index d6ef6877..ab97355 100644
--- a/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
+++ b/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
@@ -7,11 +7,20 @@
 -org.chromium.chrome.browser.FeaturesAnnotationsTest.testFeaturesSetExistingFlags
 -org.chromium.chrome.browser.FeaturesAnnotationsTest.testFeaturesDoNotRemoveExistingFlags
 
-# Not triaged yet. See https://crbug.com/1006267
-# Reproduce with --enable-features=BackForwardCache,BackForwardCacheNoTimeEviction
--org.chromium.chrome.browser.offlinepages.OfflinePageAutoFetchTest
--org.chromium.chrome.browser.offlinepages.indicator.OfflineIndicatorControllerTest
-
-# Not triaged yet. See https://crbug.com/1006267
-# This fails even without bfcache flags but keep it disabled just for now.
+# Failing on bfcache-android-debug and 4.4.4 locally. See https://crbug.com/1006267
+-org.chromium.chrome.browser.SafeBrowsingTest.interstitialPage
+-org.chromium.chrome.browser.WarmupManagerTest.testCreateAndTakeSpareRenderer
+-org.chromium.chrome.browser.customtabs.DetachedResourceRequestTest.testSafeBrowsingSubresource
+# This one is flaky
+-org.chromium.chrome.browser.customtabs.DetachedResourceRequestTest.testSafeBrowsingSubresourceBeforeNative
 -org.chromium.chrome.browser.externalnav.UrlOverridingTest.testRedirectionFromIntent
+-org.chromium.chrome.browser.incognito.IncognitoTabLauncherTest.testLaunchIncognitoNewTab
+-org.chromium.chrome.browser.incognito.IncognitoTabLauncherTest.testLaunchIncognitoNewTab_omniboxFocused
+-org.chromium.chrome.browser.infobar.InfoBarTest.testInfoBarForGeolocationDisappearsOnBack
+-org.chromium.chrome.browser.profiling_host.ProfilingProcessHostAndroidTest.testModeBrowser
+-org.chromium.chrome.browser.tabmodel.IncognitoTabModelTest.testRecreateInIncognito
+-org.chromium.chrome.browser.toolbar.top.BrandColorTest.testBrandColorInterstitial
+# These 3 are flaky
+-org.chromium.chrome.browser.offlinepages.OfflinePageAutoFetchTest
+-org.chromium.chrome.browser.notifications.NotificationPlatformBridgeIntentTest.testLaunchNotificationPreferencesForCategory
+-org.chromium.chrome.browser.notifications.NotificationPlatformBridgeIntentTest.testLaunchNotificationPreferencesForWebsite
diff --git a/testing/libfuzzer/fuzzers/dicts/date.dict b/testing/libfuzzer/fuzzers/dicts/date.dict
new file mode 100644
index 0000000..a684685
--- /dev/null
+++ b/testing/libfuzzer/fuzzers/dicts/date.dict
@@ -0,0 +1,40 @@
+# Copyright 2019 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.
+
+# Fields
+"G"
+"y"
+"u"
+"Q"
+"q"
+"M"
+"L"
+"w"
+"W"
+"d"
+"D"
+"F"
+"g"
+"E"
+"e"
+"c"
+"a"
+"h"
+"H"
+"K"
+"k"
+"m"
+"s"
+"S"
+"A"
+"z"
+"Z"
+"v"
+# Quote
+"'"
+# Separators
+"."
+":"
+","
+"\""
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index f9310e48..e334565 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -348,8 +348,9 @@
       return_code = xvfb.run_executable(
           command, env=env, stdoutfile=output_paths.logs)
     else:
-      return_code = test_env.run_command_with_output(
-          command, env=env, stdoutfile=output_paths.logs)
+      with open(output_paths.logs, 'w') as handle:
+        return_code = test_env.run_command_output_to_handle(
+            command, handle, env=env)
     expected_results_filename = os.path.join(temp_dir, 'test-results.json')
     if os.path.exists(expected_results_filename):
       shutil.move(expected_results_filename, output_paths.test_results)
diff --git a/testing/scripts/run_wpt_tests.py b/testing/scripts/run_wpt_tests.py
index 4fdf2fc9..30bf0a23 100755
--- a/testing/scripts/run_wpt_tests.py
+++ b/testing/scripts/run_wpt_tests.py
@@ -20,6 +20,10 @@
 
 import common
 
+BLINK_TOOLS_DIR = os.path.join(common.SRC_DIR, 'third_party', 'blink', 'tools')
+WPT_METADATA_DIR = "../../wpt_expectations_metadata/"
+WPT_OVERRIDE_EXPECTATIONS_PATH = (
+    "../../third_party/blink/web_tests/WPTOverrideExpectations")
 
 class WPTTestAdapter(common.BaseIsolatedScriptArgsAdapter):
 
@@ -55,7 +59,8 @@
             "--no-manifest-download",
             "--no-pause-after-test",
             "--no-fail-on-unexpected",
-            "--metadata=../../out/Release/wpt_expectations_metadata/",
+            "--metadata",
+            WPT_METADATA_DIR,
             # By specifying metadata above, WPT will try to find manifest in the
             # metadata directory. So here we point it back to the correct path
             # for the manifest.
@@ -80,18 +85,28 @@
     def clean_up_after_test_run(self):
         common.run_command([
             sys.executable,
-            os.path.join(common.SRC_DIR, 'third_party', 'blink', 'tools',
-                         'update_wpt_output.py'),
+            os.path.join(BLINK_TOOLS_DIR, 'update_wpt_output.py'),
+            '--verbose',
             '--old-json-output-file-path',
             self.options.old_json_output_file_path,
             '--new-json-output-dir', self.options.new_json_output_dir,
             '--new-json-output-filename', self.options.new_json_output_filename,
             '--additional-expectations',
-            '../../third_party/blink/web_tests/WPTOverrideExpectations',
+            WPT_OVERRIDE_EXPECTATIONS_PATH
         ])
 
 
 def main():
+    # First, generate WPT metadata files.
+    common.run_command([
+        sys.executable,
+        os.path.join(BLINK_TOOLS_DIR, 'build_wpt_metadata.py'),
+        "--metadata-output-dir",
+        WPT_METADATA_DIR,
+        "--additional-expectations",
+        WPT_OVERRIDE_EXPECTATIONS_PATH
+    ])
+
     adapter = WPTTestAdapter()
     return adapter.run_test()
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index c811611..894f5b3 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2860,30 +2860,6 @@
             ]
         }
     ],
-    "GwpAsanUnified": [
-        {
-            "platforms": [
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "AllocationSamplingRange": "16",
-                        "MaxAllocations": "70",
-                        "MaxMetadata": "255",
-                        "ProcessSamplingBoost2": "10",
-                        "ProcessSamplingProbability": "0.015"
-                    },
-                    "enable_features": [
-                        "GwpAsanMalloc",
-                        "GwpAsanPartitionAlloc"
-                    ]
-                }
-            ]
-        }
-    ],
     "HTTPReallyBadFinal": [
         {
             "platforms": [
@@ -5377,6 +5353,27 @@
             ]
         }
     ],
+    "SavePasswordIllustration": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "image": "1"
+                    },
+                    "enable_features": [
+                        "SavePasswordIllustration"
+                    ]
+                }
+            ]
+        }
+    ],
     "SchedulerConfiguration": [
         {
             "platforms": [
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 8ced46ef..a011b6a 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -162,13 +162,11 @@
     "platform/modules/mediastream/web_platform_media_stream_source.h",
     "platform/modules/mediastream/web_platform_media_stream_track.h",
     "platform/modules/mediastream/webrtc_uma_histograms.h",
-    "platform/modules/peerconnection/audio_codec_factory.h",
     "platform/modules/peerconnection/rtc_event_log_output_sink.h",
     "platform/modules/peerconnection/rtc_event_log_output_sink_proxy_util.h",
     "platform/modules/peerconnection/two_keys_adapter_map.h",
     "platform/modules/peerconnection/webrtc_audio_sink.h",
     "platform/modules/peerconnection/webrtc_util.h",
-    "platform/modules/peerconnection/webrtc_video_track_source.h",
     "platform/modules/remoteplayback/web_remote_playback_client.h",
     "platform/modules/service_worker/web_service_worker_error.h",
     "platform/modules/service_worker/web_service_worker_network_provider.h",
@@ -383,7 +381,6 @@
     "web/modules/mediastream/web_media_stream_renderer_factory.h",
     "web/modules/mediastream/web_media_stream_utils.h",
     "web/modules/mediastream/webmediaplayer_ms.h",
-    "web/modules/peerconnection/media_stream_remote_video_source.h",
     "web/modules/peerconnection/media_stream_video_webrtc_sink.h",
     "web/modules/peerconnection/peer_connection_dependency_factory.h",
     "web/modules/peerconnection/rtc_rtp_receiver_impl.h",
diff --git a/third_party/blink/public/platform/modules/peerconnection/audio_codec_factory.h b/third_party/blink/public/platform/modules/peerconnection/audio_codec_factory.h
deleted file mode 100644
index ad000a8c..0000000
--- a/third_party/blink/public/platform/modules/peerconnection/audio_codec_factory.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_AUDIO_CODEC_FACTORY_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_AUDIO_CODEC_FACTORY_H_
-
-#include "third_party/blink/public/platform/web_common.h"
-#include "third_party/webrtc/api/audio_codecs/audio_decoder_factory.h"
-#include "third_party/webrtc/api/audio_codecs/audio_encoder_factory.h"
-#include "third_party/webrtc/api/scoped_refptr.h"
-
-namespace blink {
-
-BLINK_PLATFORM_EXPORT rtc::scoped_refptr<webrtc::AudioEncoderFactory>
-CreateWebrtcAudioEncoderFactory();
-
-BLINK_PLATFORM_EXPORT rtc::scoped_refptr<webrtc::AudioDecoderFactory>
-CreateWebrtcAudioDecoderFactory();
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_AUDIO_CODEC_FACTORY_H_
diff --git a/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h b/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h
index 8451b86..ec6c7379 100644
--- a/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h
+++ b/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h
@@ -190,6 +190,7 @@
       const base::Optional<int>& requested_buffer_size,
       bool disable_local_echo,
       bool enable_automatic_output_device_selection,
+      ProcessingType processing_type,
       const AudioProcessingProperties& audio_processing_properties);
   AudioCaptureSettings(const AudioCaptureSettings& other);
   AudioCaptureSettings& operator=(const AudioCaptureSettings& other);
@@ -216,6 +217,10 @@
     DCHECK(HasValue());
     return render_to_associated_sink_;
   }
+  ProcessingType processing_type() const {
+    DCHECK(HasValue());
+    return processing_type_;
+  }
   AudioProcessingProperties audio_processing_properties() const {
     DCHECK(HasValue());
     return audio_processing_properties_;
@@ -227,6 +232,7 @@
   base::Optional<int> requested_buffer_size_;
   bool disable_local_echo_;
   bool render_to_associated_sink_;
+  ProcessingType processing_type_;
   AudioProcessingProperties audio_processing_properties_;
 };
 
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.h
index f5e599f..f28dccb 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.h
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_document.h
@@ -14,7 +14,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
-#include "third_party/blink/renderer/bindings/core/v8/usv_string_or_trusted_url.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_document.h"
 #include "third_party/blink/renderer/bindings/tests/idls/core/test_interface_document.h"
diff --git a/third_party/blink/renderer/core/css/css_value.h b/third_party/blink/renderer/core/css/css_value.h
index 8b24591..94a011c 100644
--- a/third_party/blink/renderer/core/css/css_value.h
+++ b/third_party/blink/renderer/core/css/css_value.h
@@ -33,13 +33,14 @@
 
 class CORE_EXPORT CSSValue : public GarbageCollected<CSSValue> {
  public:
+  template <typename T>
   static void* AllocateObject(size_t size) {
     ThreadState* state =
         ThreadStateFor<ThreadingTrait<CSSValue>::kAffinity>::GetState();
     const char* type_name = "blink::CSSValue";
     return state->Heap().AllocateOnArenaIndex(
         state, size, BlinkGC::kCSSValueArenaIndex,
-        GCInfoTrait<CSSValue>::Index(), type_name);
+        GCInfoTrait<GCInfoFoldedType<CSSValue>>::Index(), type_name);
   }
 
   // TODO(sashab): Remove this method and move logic to the caller.
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index d1c1b97..b79c7d6 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -163,15 +163,14 @@
     kDocumentPositionImplementationSpecific = 0x20,
   };
 
-  // Override operator new to allocate Node subtype objects onto
-  // a dedicated heap.
+  template <typename T>
   static void* AllocateObject(size_t size) {
     ThreadState* state =
         ThreadStateFor<ThreadingTrait<Node>::kAffinity>::GetState();
     const char* type_name = "blink::Node";
     return state->Heap().AllocateOnArenaIndex(
         state, size, BlinkGC::kNodeArenaIndex,
-        GCInfoTrait<EventTarget>::Index(), type_name);
+        GCInfoTrait<GCInfoFoldedType<T>>::Index(), type_name);
   }
 
   static void DumpStatistics();
diff --git a/third_party/blink/renderer/core/layout/hit_test_result.cc b/third_party/blink/renderer/core/layout/hit_test_result.cc
index c58ec5f..c13d2ddd 100644
--- a/third_party/blink/renderer/core/layout/hit_test_result.cc
+++ b/third_party/blink/renderer/core/layout/hit_test_result.cc
@@ -46,8 +46,6 @@
 
 namespace blink {
 
-using namespace html_names;
-
 HitTestResult::HitTestResult()
     : hit_test_request_(HitTestRequest::kReadOnly | HitTestRequest::kActive),
       cacheable_(true),
@@ -190,7 +188,7 @@
     return nullptr;
 
   HTMLMapElement* map = image_element->GetTreeScope().GetImageMap(
-      image_element->FastGetAttribute(kUsemapAttr));
+      image_element->FastGetAttribute(html_names::kUsemapAttr));
   if (!map)
     return nullptr;
 
@@ -293,7 +291,7 @@
     return g_null_atom;
 
   if (auto* image = ToHTMLImageElementOrNull(*inner_node_or_image_map_image))
-    return image->getAttribute(kAltAttr);
+    return image->getAttribute(html_names::kAltAttr);
 
   if (auto* input = ToHTMLInputElementOrNull(*inner_node_or_image_map_image))
     return input->Alt();
diff --git a/third_party/blink/renderer/core/layout/layout_details_marker.cc b/third_party/blink/renderer/core/layout/layout_details_marker.cc
index 190448dd..9b6e044 100644
--- a/third_party/blink/renderer/core/layout/layout_details_marker.cc
+++ b/third_party/blink/renderer/core/layout/layout_details_marker.cc
@@ -27,31 +27,22 @@
 
 namespace blink {
 
-using namespace html_names;
-
 LayoutDetailsMarker::LayoutDetailsMarker(Element* element)
     : LayoutBlockFlow(element) {}
 
 LayoutDetailsMarker::Orientation LayoutDetailsMarker::GetOrientation() const {
-  switch (StyleRef().GetWritingMode()) {
-    case WritingMode::kHorizontalTb:
-      if (StyleRef().IsLeftToRightDirection())
-        return IsOpen() ? kDown : kRight;
-      return IsOpen() ? kDown : kLeft;
-    case WritingMode::kVerticalRl:
-      if (StyleRef().IsLeftToRightDirection())
-        return IsOpen() ? kLeft : kDown;
-      return IsOpen() ? kLeft : kUp;
-    case WritingMode::kVerticalLr:
-      if (StyleRef().IsLeftToRightDirection())
-        return IsOpen() ? kRight : kDown;
-      return IsOpen() ? kRight : kUp;
-    // TODO(layout-dev): Sideways-lr and sideways-rl are not yet supported.
-    default:
-      break;
+  // TODO(layout-dev): Sideways-lr and sideways-rl are not yet supported.
+  const auto mode = StyleRef().GetWritingMode();
+  DCHECK(mode != WritingMode::kSidewaysRl && mode != WritingMode::kSidewaysLr);
+
+  if (IsOpen()) {
+    if (mode == WritingMode::kHorizontalTb)
+      return kDown;
+    return (mode == WritingMode::kVerticalRl) ? kLeft : kRight;
   }
-  NOTREACHED();
-  return kRight;
+  if (mode == WritingMode::kHorizontalTb)
+    return StyleRef().IsLeftToRightDirection() ? kRight : kLeft;
+  return StyleRef().IsLeftToRightDirection() ? kDown : kUp;
 }
 
 void LayoutDetailsMarker::Paint(const PaintInfo& paint_info) const {
@@ -61,13 +52,12 @@
 bool LayoutDetailsMarker::IsOpen() const {
   for (LayoutObject* layout_object = Parent(); layout_object;
        layout_object = layout_object->Parent()) {
-    if (!layout_object->GetNode())
+    const auto* node = layout_object->GetNode();
+    if (!node)
       continue;
-    if (IsHTMLDetailsElement(*layout_object->GetNode()))
-      return !To<Element>(layout_object->GetNode())
-                  ->getAttribute(kOpenAttr)
-                  .IsNull();
-    if (IsHTMLInputElement(*layout_object->GetNode()))
+    if (IsHTMLDetailsElement(*node))
+      return !To<Element>(node)->getAttribute(html_names::kOpenAttr).IsNull();
+    if (IsHTMLInputElement(*node))
       return true;
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 71066fd..d4dc838 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -50,8 +50,6 @@
 
 namespace blink {
 
-using namespace html_names;
-
 LayoutImage::LayoutImage(Element* element)
     : LayoutReplaced(element, LayoutSize()),
       did_increment_visually_non_empty_pixel_count_(false),
@@ -287,7 +285,8 @@
 
 HTMLMapElement* LayoutImage::ImageMap() const {
   HTMLImageElement* i = ToHTMLImageElementOrNull(GetNode());
-  return i ? i->GetTreeScope().GetImageMap(i->FastGetAttribute(kUsemapAttr))
+  return i ? i->GetTreeScope().GetImageMap(
+                 i->FastGetAttribute(html_names::kUsemapAttr))
            : nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell.cc b/third_party/blink/renderer/core/layout/layout_table_cell.cc
index ce97dd7d..fd11296 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_cell.cc
@@ -43,8 +43,6 @@
 
 namespace blink {
 
-using namespace html_names;
-
 struct SameSizeAsLayoutTableCell : public LayoutBlockFlow,
                                    public LayoutNGTableCellInterface {
   unsigned bitfields;
@@ -203,7 +201,7 @@
     // See if nowrap was set.
     Length w = StyleOrColLogicalWidth();
     const AtomicString& nowrap =
-        To<Element>(GetNode())->getAttribute(kNowrapAttr);
+        To<Element>(GetNode())->getAttribute(html_names::kNowrapAttr);
     if (!nowrap.IsNull() && w.IsFixed()) {
       // Nowrap is set, but we didn't actually use it because of the fixed width
       // set on the cell. Even so, it is a WinIE/Moz trait to make the minwidth
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
index 9369d73..8022ea04 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -16,6 +16,7 @@
       type_(kText),
       sub_type_(static_cast<unsigned>(text.TextType())),
       style_variant_(static_cast<unsigned>(text.StyleVariant())),
+      is_generated_text_(text.IsGeneratedText()),
       is_hidden_for_paint_(false),
       text_direction_(static_cast<unsigned>(text.ResolvedDirection())) {
   DCHECK_LE(text_.start_offset, text_.end_offset);
@@ -82,6 +83,13 @@
   return false;
 }
 
+bool NGFragmentItem::IsGeneratedText() const {
+  if (Type() == kText || Type() == kGeneratedText)
+    return is_generated_text_;
+  NOTREACHED();
+  return false;
+}
+
 PhysicalRect NGFragmentItem::SelfInkOverflow() const {
   // TODO(kojii): Implement.
   return LocalRect();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 4570ca0..125bdf98 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -238,6 +238,17 @@
     return StyleVariant() == NGStyleVariant::kEllipsis;
   }
 
+  // Returns true if the text is generated (from, e.g., list marker,
+  // pseudo-element, ...) instead of from a DOM text node.
+  //  * CSS content         kText
+  //  * ellipsis            kGeneratedText
+  //  * first-letter-part   kText
+  //  * list marker         kGeneratedText
+  //  * soft hyphen         kGeneratedText
+  // TODO(yosin): When we implement |kGeneratedText|, we rename this function
+  // to avoid confliction with |kGeneratedText|.
+  bool IsGeneratedText() const;
+
   bool IsSymbolMarker() const {
     return TextType() == NGTextType::kSymbolMarker;
   }
@@ -324,6 +335,10 @@
   unsigned type_ : 2;           // ItemType
   unsigned sub_type_ : 3;       // NGTextType
   unsigned style_variant_ : 2;  // NGStyleVariant
+  // TODO(yosin): We'll remove |is_generated_text_| field when we construct
+  // |NGFragmentItem| without |NGPhysicalTextFragment| because usage of this
+  // varaible, IsGeneratedText(), is not hot.
+  unsigned is_generated_text_ : 1;  // NGPhysicalTextFragment::IsGenerated()
   unsigned is_hidden_for_paint_ : 1;
   // Note: For |TextItem| and |GeneratedTextItem|, |text_direction_| equals to
   // |ShapeResult::Direction()|.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index 92d4f89f..59ca155 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h"
 
 namespace blink {
 
@@ -115,6 +116,12 @@
   return false;
 }
 
+bool NGInlineCursor::HasSoftWrapToNextLine() const {
+  DCHECK(IsLineBox());
+  const NGInlineBreakToken& break_token = CurrentInlineBreakToken();
+  return !break_token.IsFinished() && !break_token.IsForcedBreak();
+}
+
 bool NGInlineCursor::IsAtomicInline() const {
   if (current_paint_fragment_)
     return current_paint_fragment_->PhysicalFragment().IsAtomicInline();
@@ -133,6 +140,19 @@
   return false;
 }
 
+bool NGInlineCursor::IsGeneratedText() const {
+  if (current_paint_fragment_) {
+    if (auto* text_fragment = DynamicTo<NGPhysicalTextFragment>(
+            current_paint_fragment_->PhysicalFragment()))
+      return text_fragment->IsGeneratedText();
+    return false;
+  }
+  if (current_item_)
+    return current_item_->IsGeneratedText();
+  NOTREACHED();
+  return false;
+}
+
 bool NGInlineCursor::IsHiddenForPaint() const {
   if (current_paint_fragment_)
     return current_paint_fragment_->PhysicalFragment().IsHiddenForPaint();
@@ -176,6 +196,17 @@
   return false;
 }
 
+bool NGInlineCursor::IsText() const {
+  if (current_paint_fragment_)
+    return current_paint_fragment_->PhysicalFragment().IsText();
+  if (current_item_) {
+    return current_item_->Type() == NGFragmentItem::kText ||
+           current_item_->Type() == NGFragmentItem::kGeneratedText;
+  }
+  NOTREACHED();
+  return false;
+}
+
 bool NGInlineCursor::IsBeforeSoftLineBreak() const {
   if (IsLineBreak())
     return false;
@@ -248,6 +279,18 @@
   return nullptr;
 }
 
+const NGInlineBreakToken& NGInlineCursor::CurrentInlineBreakToken() const {
+  DCHECK(IsLineBox());
+  if (current_paint_fragment_) {
+    return To<NGInlineBreakToken>(
+        *To<NGPhysicalLineBoxFragment>(
+             current_paint_fragment_->PhysicalFragment())
+             .BreakToken());
+  }
+  DCHECK(current_item_);
+  return *current_item_->InlineBreakToken();
+}
+
 const LayoutObject* NGInlineCursor::CurrentLayoutObject() const {
   if (current_paint_fragment_)
     return current_paint_fragment_->GetLayoutObject();
@@ -257,6 +300,12 @@
   return nullptr;
 }
 
+Node* NGInlineCursor::CurrentNode() const {
+  if (const LayoutObject* layout_object = CurrentLayoutObject())
+    return layout_object->GetNode();
+  return nullptr;
+}
+
 const PhysicalOffset NGInlineCursor::CurrentOffset() const {
   if (current_paint_fragment_)
     return current_paint_fragment_->InlineOffsetToContainerBox();
@@ -368,6 +417,9 @@
 }
 
 void NGInlineCursor::MoveTo(const NGPaintFragment& paint_fragment) {
+  DCHECK(!fragment_items_);
+  if (!root_paint_fragment_)
+    root_paint_fragment_ = paint_fragment.Root();
   DCHECK(root_paint_fragment_);
   DCHECK(paint_fragment.IsDescendantOfNotSelf(*root_paint_fragment_))
       << paint_fragment << " " << root_paint_fragment_;
@@ -407,6 +459,21 @@
     MakeNull();
 }
 
+void NGInlineCursor::MoveToFirstLogicalLeaf() {
+  DCHECK(IsLineBox());
+  // TODO(yosin): This isn't correct for mixed Bidi. Fix it. Besides, we
+  // should compute and store it during layout.
+  // TODO(yosin): We should check direction of each container instead of line
+  // box. See also |NGPhysicalLineBoxFragment::LastLogicalLeaf()|.
+  if (IsLtr(CurrentStyle().Direction())) {
+    while (TryToMoveToFirstChild())
+      continue;
+    return;
+  }
+  while (TryToMoveToLastChild())
+    continue;
+}
+
 void NGInlineCursor::MoveToLastChild() {
   DCHECK(CanHaveChildren());
   if (!TryToMoveToLastChild())
@@ -474,6 +541,24 @@
   MoveToNextItemSkippingChildren();
 }
 
+void NGInlineCursor::MoveToPreviousLine() {
+  DCHECK(IsLineBox());
+  if (current_paint_fragment_) {
+    // TODO(yosin): We should implement |PreviousLineOf()| here.
+    if (auto* paint_fragment =
+            NGPaintFragmentTraversal::PreviousLineOf(*current_paint_fragment_))
+      return MoveTo(*paint_fragment);
+    return MakeNull();
+  }
+  if (current_item_) {
+    do {
+      MoveToPreviousItem();
+    } while (IsNotNull() && !IsLineBox());
+    return;
+  }
+  NOTREACHED();
+}
+
 bool NGInlineCursor::TryToMoveToFirstChild() {
   if (!HasChildren())
     return false;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
index d92b72a..716d77cb 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -18,8 +18,10 @@
 class LayoutObject;
 class NGFragmentItem;
 class NGFragmentItems;
+class NGInlineBreakToken;
 class NGPaintFragment;
 class NGPhysicalBoxFragment;
+class Node;
 struct PhysicalOffset;
 struct PhysicalRect;
 struct PhysicalSize;
@@ -73,6 +75,10 @@
   // True if fragment at the current position has children.
   bool HasChildren() const;
 
+  // True if current position has soft wrap to next line. It is error to call
+  // other than line.
+  bool HasSoftWrapToNextLine() const;
+
   // True if the current position is a atomic inline. It is error to call at
   // end.
   bool IsAtomicInline() const;
@@ -84,6 +90,10 @@
   // True if the current position is an ellipsis. It is error to call at end.
   bool IsEllipsis() const;
 
+  // True if the current position is a generatd text. It is error to call at
+  // end.
+  bool IsGeneratedText() const;
+
   // True if the current position is hidden for paint. It is error to call at
   // end.
   bool IsHiddenForPaint() const;
@@ -94,6 +104,9 @@
   // True if the current position is a line break. It is error to call at end.
   bool IsLineBreak() const;
 
+  // True if the current position is a text. It is error to call at end.
+  bool IsText() const;
+
   // |Current*| functions return an object for the current position.
   const NGFragmentItem* CurrentItem() const { return current_item_; }
   const NGPaintFragment* CurrentPaintFragment() const {
@@ -104,6 +117,7 @@
   TextDirection CurrentBaseDirection() const;
   const NGPhysicalBoxFragment* CurrentBoxFragment() const;
   const LayoutObject* CurrentLayoutObject() const;
+  Node* CurrentNode() const;
   // Returns text direction of current text or atomic inline. It is error to
   // call at other than text or atomic inline. Note: <span> doesn't have
   // reserved direction.
@@ -140,6 +154,10 @@
   // See also |TryToMoveToFirstChild()|.
   void MoveToFirstChild();
 
+  // Move to first logical leaf of current line box. If current line box has
+  // no children, curosr becomes null.
+  void MoveToFirstLogicalLeaf();
+
   // Move to last child of current container box. If the current position is
   // at fragment without children, this cursor points nothing.
   // See also |TryToMoveToFirstChild()|.
@@ -162,6 +180,10 @@
   // Same as |MoveToNext| except that this skips children even if they exist.
   void MoveToNextSkippingChildren();
 
+  // Move the current position to previous line. It is error to call other than
+  // line box.
+  void MoveToPreviousLine();
+
   // Returns true if the current position moves to first child.
   bool TryToMoveToFirstChild();
 
@@ -174,6 +196,9 @@
  private:
   using ItemsSpan = base::span<const std::unique_ptr<NGFragmentItem>>;
 
+  // Returns break token for line box. It is error to call other than line box.
+  const NGInlineBreakToken& CurrentInlineBreakToken() const;
+
   // True if current position is descendant or self of |layout_object|.
   // Note: This function is used for moving cursor in culled inline boxes.
   bool IsInclusiveDescendantOf(const LayoutObject& layout_object) const;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
index 1e8e176..d6c81850 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
@@ -135,6 +135,88 @@
   EXPECT_FALSE(cursor.TryToMoveToFirstChild());
 }
 
+TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInSimpleText) {
+  // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
+  InsertStyleElement("b { background: gray; }");
+  NGInlineCursor cursor =
+      SetupCursor("<div id=root><b>first</b><b>middle</b><b>last</b></div>");
+
+  NGInlineCursor first_logical_leaf(cursor);
+  first_logical_leaf.MoveToFirstLogicalLeaf();
+  EXPECT_EQ("first", ToDebugString(first_logical_leaf));
+
+  NGInlineCursor last_logical_leaf(cursor);
+  last_logical_leaf.MoveToLastLogicalLeaf();
+  EXPECT_EQ("last", ToDebugString(last_logical_leaf));
+}
+
+TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInRtlText) {
+  // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
+  InsertStyleElement("b { background: gray; }");
+  NGInlineCursor cursor = SetupCursor(
+      "<bdo id=root dir=rtl style=display:block>"
+      "<b>first</b><b>middle</b><b>last</b>"
+      "</bdo>");
+
+  NGInlineCursor first_logical_leaf(cursor);
+  first_logical_leaf.MoveToFirstLogicalLeaf();
+  EXPECT_EQ("first", ToDebugString(first_logical_leaf));
+
+  NGInlineCursor last_logical_leaf(cursor);
+  last_logical_leaf.MoveToLastLogicalLeaf();
+  EXPECT_EQ("last", ToDebugString(last_logical_leaf));
+}
+
+TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInTextAsDeepDescendants) {
+  // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
+  InsertStyleElement("b { background: gray; }");
+  NGInlineCursor cursor = SetupCursor(
+      "<div id=root>"
+      "<b><b>first</b>ABC</b>"
+      "<b>middle</b>"
+      "<b>DEF<b>last</b></b>"
+      "</div>");
+
+  NGInlineCursor first_logical_leaf(cursor);
+  first_logical_leaf.MoveToFirstLogicalLeaf();
+  EXPECT_EQ("first", ToDebugString(first_logical_leaf));
+
+  NGInlineCursor last_logical_leaf(cursor);
+  last_logical_leaf.MoveToLastLogicalLeaf();
+  EXPECT_EQ("last", ToDebugString(last_logical_leaf));
+}
+
+TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithInlineBlock) {
+  InsertStyleElement("b { display: inline-block; }");
+  NGInlineCursor cursor = SetupCursor(
+      "<div id=root>"
+      "<b id=first>first</b>middle<b id=last>last</b>"
+      "</div>");
+
+  NGInlineCursor first_logical_leaf(cursor);
+  first_logical_leaf.MoveToFirstLogicalLeaf();
+  EXPECT_EQ("#first", ToDebugString(first_logical_leaf))
+      << "stop at inline-block";
+
+  NGInlineCursor last_logical_leaf(cursor);
+  last_logical_leaf.MoveToLastLogicalLeaf();
+  EXPECT_EQ("#last", ToDebugString(last_logical_leaf))
+      << "stop at inline-block";
+}
+
+TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithImages) {
+  NGInlineCursor cursor =
+      SetupCursor("<div id=root><img id=first>middle<img id=last></div>");
+
+  NGInlineCursor first_logical_leaf(cursor);
+  first_logical_leaf.MoveToFirstLogicalLeaf();
+  EXPECT_EQ("#first", ToDebugString(first_logical_leaf));
+
+  NGInlineCursor last_logical_leaf(cursor);
+  last_logical_leaf.MoveToLastLogicalLeaf();
+  EXPECT_EQ("#last", ToDebugString(last_logical_leaf));
+}
+
 TEST_P(NGInlineCursorTest, LastChild) {
   // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
   InsertStyleElement("a, b { background: gray; }");
@@ -285,4 +367,25 @@
   EXPECT_THAT(list, ElementsAre());
 }
 
+TEST_P(NGInlineCursorTest, PreviousLine) {
+  NGInlineCursor cursor = SetupCursor("<div id=root>abc<br>xyz</div>");
+  NGInlineCursor line1(cursor);
+  while (line1 && !line1.IsLineBox())
+    line1.MoveToNext();
+  ASSERT_TRUE(line1.IsNotNull());
+  NGInlineCursor line2(line1);
+  line2.MoveToNext();
+  while (line2 && !line2.IsLineBox())
+    line2.MoveToNext();
+  ASSERT_NE(line1, line2);
+
+  NGInlineCursor should_be_null(line1);
+  should_be_null.MoveToPreviousLine();
+  EXPECT_TRUE(should_be_null.IsNull());
+
+  NGInlineCursor should_be_line1(line2);
+  should_be_line1.MoveToPreviousLine();
+  EXPECT_EQ(line1, should_be_line1);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
index 251ad126..8960f5b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
@@ -233,24 +233,8 @@
   // returned.
   bool FinalizeForFragmentation();
 
-  // Insert a fragmentainer break before the child if necessary. In that case,
-  // the previous in-flow position will be updated, we'll return |kBrokeBefore|.
-  // If we don't break inside, we'll consider the appeal of doing so anyway (and
-  // store it as the most appealing break point so far if that's the case),
-  // since we might have to go back and break here. Return |kContinue| if we're
-  // to continue laying out. If |kNeedsEarlierBreak| is returned, it means that
-  // we ran out of space, but shouldn't break before the child, but rather abort
-  // layout, and re-layout to a previously found good breakpoint. If
-  // |has_container_separation| is true, it means that we're at a valid
-  // breakpoint. We obviously prefer valid breakpoints, but sometimes we need to
-  // break at undesirable locations. Class A breakpoints occur between block
-  // siblings. Class B breakpoints between line boxes. Both these breakpoint
-  // classes imply that we're already past the first in-flow child in the
-  // container, but there's also another way of achieving container separation:
-  // class C breakpoints. Those occur if there's a positive gap between the
-  // block-start content edge of the container and the block-start margin edge
-  // of the first in-flow child. This can happen when in-flow content is pushed
-  // down by floats. https://www.w3.org/TR/css-break-3/#possible-breaks
+  // Insert a fragmentainer break before the child if necessary.
+  // See |::blink::BreakBeforeChildIfNeeded()| for more documentation.
   NGBreakStatus BreakBeforeChildIfNeeded(NGLayoutInputNode child,
                                          const NGLayoutResult&,
                                          NGPreviousInflowPosition*,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index bc035d1..d0dd84a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -200,6 +200,10 @@
     break_appeal_ = appeal;
   }
   bool HasEarlyBreak() const { return early_break_.get(); }
+  const NGEarlyBreak& EarlyBreak() const {
+    DCHECK(early_break_.get());
+    return *early_break_.get();
+  }
 
   // Set the highest break appeal found so far. This is either:
   // 1: The highest appeal of a breakpoint found by our container
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 466e4d6..1be5b7d8 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -62,6 +62,17 @@
   return broken_node.IsColumnSpanAll() && broken_node != multicol_container;
 }
 
+// Add the break token for the column content that comes after a fragmented
+// spanner, if any; otherwise, we're past all children.
+void PushNextColumnBreakToken(
+    scoped_refptr<const NGBlockBreakToken> next_column_token,
+    NGBoxFragmentBuilder* builder) {
+  if (next_column_token)
+    builder->AddBreakToken(std::move(next_column_token));
+  else
+    builder->SetHasSeenAllChildren();
+}
+
 // Add the spanner's break token, AND another break token for the column content
 // that comes after. In the next fragment we need to resume layout of the
 // spanner, and then proceed to the column content - if there's room for both.
@@ -71,10 +82,7 @@
     scoped_refptr<const NGBlockBreakToken> next_column_token,
     NGBoxFragmentBuilder* builder) {
   builder->AddBreakToken(std::move(spanner_break_token));
-  if (next_column_token)
-    builder->AddBreakToken(std::move(next_column_token));
-  else
-    builder->SetHasSeenAllChildren();
+  PushNextColumnBreakToken(std::move(next_column_token), builder);
 }
 
 }  // namespace
@@ -82,6 +90,7 @@
 NGColumnLayoutAlgorithm::NGColumnLayoutAlgorithm(
     const NGLayoutAlgorithmParams& params)
     : NGLayoutAlgorithm(params),
+      early_break_(params.early_break),
       border_padding_(params.fragment_geometry.border +
                       params.fragment_geometry.padding),
       border_scrollbar_padding_(border_padding_ +
@@ -130,7 +139,11 @@
   if (!IsResumingLayout(BreakToken()))
     intrinsic_block_size_ = border_scrollbar_padding_.block_start;
 
-  LayoutChildren();
+  if (!LayoutChildren()) {
+    // We need to discard this layout and do it again. We found an earlier break
+    // point that's more appealing than the one we ran out of space at.
+    return RelayoutAndBreakEarlier();
+  }
 
   // Figure out how much space we've already been able to process in previous
   // fragments, if this multicol container participates in an outer
@@ -212,7 +225,7 @@
   return sizes;
 }
 
-void NGColumnLayoutAlgorithm::LayoutChildren() {
+bool NGColumnLayoutAlgorithm::LayoutChildren() {
   NGMarginStrut margin_strut;
 
   // First extract incoming child break tokens.
@@ -258,20 +271,28 @@
     // The multicol container previously broke at a spanner (this may happen if
     // we're nested inside another fragmentation context), so that's where we'll
     // resume now.
-    spanner_break_token =
-        LayoutSpanner(To<NGBlockNode>(spanner_break_token->InputNode()),
-                      spanner_break_token.get(), &margin_strut);
+    NGBreakStatus break_status = LayoutSpanner(
+        To<NGBlockNode>(spanner_break_token->InputNode()),
+        spanner_break_token.get(), &margin_strut, &spanner_break_token);
 
     if (spanner_break_token) {
-      // We broke at the spanner again!
-      PushSpannerBreakTokens(std::move(spanner_break_token),
-                             std::move(next_column_token), &container_builder_);
-      return;
+      DCHECK_EQ(break_status, NGBreakStatus::kContinue);
+      if (spanner_break_token) {
+        // We broke at the spanner again!
+        PushSpannerBreakTokens(std::move(spanner_break_token),
+                               std::move(next_column_token),
+                               &container_builder_);
+        return true;
+      }
+    } else {
+      // Breaking before the first element in the fragmentainer isn't allowed,
+      // as that would give no content progress, and we'd be stuck forever.
+      DCHECK_EQ(break_status, NGBreakStatus::kContinue);
     }
   }
 
   if (BreakToken() && BreakToken()->HasSeenAllChildren() && !next_column_token)
-    return;
+    return true;
 
   // Entering the child main loop. Here we'll alternate between laying out
   // column content and column spanners, until we're either done, or until
@@ -291,18 +312,41 @@
     if (!spanner_node)
       break;
 
-    // We found a spanner. Lay it out, and then resume column layout.
-    spanner_break_token = LayoutSpanner(spanner_node, nullptr, &margin_strut);
+    if (early_break_) {
+      // If this is the child we had previously determined to break before, do
+      // so now and finish layout.
+      DCHECK_EQ(early_break_->Type(), NGEarlyBreak::kBlock);
+      if (early_break_->IsBreakBefore() &&
+          early_break_->BlockNode() == spanner_node) {
+        container_builder_.AddBreakBeforeChild(
+            spanner_node, kBreakAppealPerfect, /* is_forced_break */ false);
+        FinishAfterBreakBeforeSpanner(std::move(next_column_token));
+        return true;
+      }
+    }
 
-    if (spanner_break_token) {
-      // We broke before or inside the spanner. This may happen if we're nested
-      // inside another fragmentation context.
+    // We found a spanner. Lay it out, and then resume column layout.
+    NGBreakStatus break_status = LayoutSpanner(
+        spanner_node, nullptr, &margin_strut, &spanner_break_token);
+    if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
+      return false;
+    } else if (break_status == NGBreakStatus::kBrokeBefore) {
+      DCHECK(ConstraintSpace().HasBlockFragmentation());
+      FinishAfterBreakBeforeSpanner(std::move(next_column_token));
+      return true;
+    } else if (spanner_break_token) {
+      DCHECK_EQ(break_status, NGBreakStatus::kContinue);
+      // We broke inside the spanner. This may happen if we're nested inside
+      // another fragmentation context.
       PushSpannerBreakTokens(std::move(spanner_break_token),
                              std::move(next_column_token), &container_builder_);
-      return;
+      return true;
     }
   } while (next_column_token);
 
+  // If there's an early break set, we should have found it and returned.
+  DCHECK(!early_break_);
+
   if (next_column_token) {
     // We broke inside column content. Add a break token for where to resume
     // column layout at in the next fragment.
@@ -318,6 +362,8 @@
 
     intrinsic_block_size_ += margin_strut.Sum();
   }
+
+  return true;
 }
 
 scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
@@ -511,7 +557,7 @@
     break;
   } while (true);
 
-  bool keep_margin = false;
+  bool is_empty = false;
 
   // If there was no content inside to process, we don't want the resulting
   // empty column fragment.
@@ -521,7 +567,7 @@
 
     if (column.Children().size() == 0) {
       // No content. Keep the trailing margin from any previous column spanner.
-      keep_margin = true;
+      is_empty = true;
 
       // TODO(mstensho): It's wrong to keep the empty fragment, just so that
       // out-of-flow descendants get propagated correctly. Find some other way
@@ -533,9 +579,14 @@
 
   intrinsic_block_size_ = column_block_offset + column_size.block_size;
 
-  // We added a row. Reset the trailing margin from any previous column spanner.
-  if (!keep_margin)
+  if (!is_empty) {
+    has_processed_first_child_ = true;
+    container_builder_.SetPreviousBreakAfter(EBreakBetween::kAuto);
+
+    // We added a row. Reset the trailing margin from any previous column
+    // spanner.
     *margin_strut = NGMarginStrut();
+  }
 
   // Commit all column fragments to the fragment builder.
   for (auto column : new_columns) {
@@ -546,20 +597,23 @@
   return result;
 }
 
-scoped_refptr<const NGBlockBreakToken> NGColumnLayoutAlgorithm::LayoutSpanner(
+NGBreakStatus NGColumnLayoutAlgorithm::LayoutSpanner(
     NGBlockNode spanner_node,
     const NGBlockBreakToken* break_token,
-    NGMarginStrut* margin_strut) {
+    NGMarginStrut* margin_strut,
+    scoped_refptr<const NGBlockBreakToken>* spanner_break_token) {
+  *spanner_break_token = nullptr;
   const ComputedStyle& spanner_style = spanner_node.Style();
   NGBoxStrut margins = ComputeMarginsFor(
       spanner_style, content_box_size_.inline_size,
       ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction());
 
   if (break_token) {
-    // Truncate block-start margins at fragmentainer breaks, and also make sure
-    // that we don't repeat them at the beginning of every fragment generated
-    // from the spanner node.
-    margins.block_start = LayoutUnit();
+    // Truncate block-start margins at fragmentainer breaks (except when the
+    // break is forced), and also make sure that we don't repeat them at the
+    // beginning of every fragment generated from the spanner node.
+    if (!break_token->IsBreakBefore() || !break_token->IsForcedBreak())
+      margins.block_start = LayoutUnit();
 
     if (break_token->IsBreakBefore()) {
       // TODO(mstensho): Passing a break-before token shouldn't be a problem,
@@ -573,12 +627,42 @@
   // of an immediately preceding spanner, if any.
   margin_strut->Append(margins.block_start, /* is_quirky */ false);
 
-  // TODO(mstensho): outer fragmentainer breaks between spanners and rows (the
-  // spanner may be unbreakable inside, and we may be in a nested fragmentation
-  // context and out of space).
   LayoutUnit block_offset = intrinsic_block_size_ + margin_strut->Sum();
   auto spanner_space = CreateConstraintSpaceForSpanner(block_offset);
-  auto result = spanner_node.Layout(spanner_space, break_token);
+
+  const NGEarlyBreak* early_break_in_child = nullptr;
+  if (early_break_ && early_break_->Type() == NGEarlyBreak::kBlock &&
+      early_break_->BlockNode() == spanner_node) {
+    // We're entering a child that we know that we're going to break inside, and
+    // even where to break. Look inside, and pass the inner breakpoint to
+    // layout.
+    early_break_in_child = early_break_->BreakInside();
+    // If there's no break inside, we should already have broken before this
+    // child.
+    DCHECK(early_break_in_child);
+  }
+
+  auto result =
+      spanner_node.Layout(spanner_space, break_token, early_break_in_child);
+
+  if (ConstraintSpace().HasBlockFragmentation() && !early_break_) {
+    // We're nested inside another fragmentation context. Examine this break
+    // point, and determine whether we should break.
+
+    LayoutUnit fragmentainer_block_offset =
+        ConstraintSpace().FragmentainerOffsetAtBfc() + block_offset;
+
+    NGBreakStatus break_status = BreakBeforeChildIfNeeded(
+        ConstraintSpace(), spanner_node, *result.get(),
+        fragmentainer_block_offset, has_processed_first_child_,
+        &container_builder_);
+
+    if (break_status != NGBreakStatus::kContinue) {
+      // We need to break, either before the spanner, or even earlier.
+      return break_status;
+    }
+  }
+
   NGFragment fragment(ConstraintSpace().GetWritingMode(),
                       result->PhysicalFragment());
 
@@ -593,18 +677,16 @@
   *margin_strut = NGMarginStrut();
   margin_strut->Append(margins.block_end, /* is_quirky */ false);
 
-  // TODO(mstensho): The correct thing would be to weigh any break inside
-  // against the appeal of breaking before the spanner, like we do in
-  // BreakBeforeChildIfNeeded() for the block layout algorithm. Just setting the
-  // appeal to perfect isn't right, but we're doing it for now, so that any
-  // break inside the spanner (in case we're nested inside another fragmentation
-  // context) isn't just discarded.
-  if (ConstraintSpace().HasBlockFragmentation())
-    container_builder_.SetBreakAppeal(kBreakAppealPerfect);
-
   intrinsic_block_size_ = offset.block_offset + fragment.BlockSize();
+  has_processed_first_child_ = true;
 
-  return To<NGBlockBreakToken>(result->PhysicalFragment().BreakToken());
+  EBreakBetween break_after = JoinFragmentainerBreakValues(
+      result->FinalBreakAfter(), spanner_node.Style().BreakAfter());
+  container_builder_.SetPreviousBreakAfter(break_after);
+
+  *spanner_break_token =
+      To<NGBlockBreakToken>(result->PhysicalFragment().BreakToken());
+  return NGBreakStatus::kContinue;
 }
 
 LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
@@ -784,6 +866,39 @@
   return intrinsic_block_size_ - border_scrollbar_padding_.block_start;
 }
 
+void NGColumnLayoutAlgorithm::FinishAfterBreakBeforeSpanner(
+    scoped_refptr<const NGBlockBreakToken> next_column_token) {
+  // We broke before the spanner. We're done here. Take up the remaining space
+  // in the outer fragmentation context.
+  intrinsic_block_size_ = FragmentainerSpaceAtBfcStart(ConstraintSpace());
+
+  // A break token for the spanner has already been inserted, but we also need
+  // to add one for the column contents that follows, so that we know where to
+  // resume, once done with the spanner - or - specify that we're past
+  // everything if there's nothing to resume at (so that we don't restart from
+  // the beginning of the multicol container).
+  PushNextColumnBreakToken(std::move(next_column_token), &container_builder_);
+}
+
+scoped_refptr<const NGLayoutResult>
+NGColumnLayoutAlgorithm::RelayoutAndBreakEarlier() {
+  // Not allowed to recurse!
+  DCHECK(!early_break_);
+
+  const NGEarlyBreak& breakpoint = container_builder_.EarlyBreak();
+  NGLayoutAlgorithmParams params(Node(),
+                                 container_builder_.InitialFragmentGeometry(),
+                                 ConstraintSpace(), BreakToken(), &breakpoint);
+  NGColumnLayoutAlgorithm algorithm_with_break(params);
+  NGBoxFragmentBuilder& new_builder = algorithm_with_break.container_builder_;
+  new_builder.SetBoxType(container_builder_.BoxType());
+  // We're not going to run out of space in the next layout pass, since we're
+  // breaking earlier, so no space shortage will be detected. Repeat what we
+  // found in this pass.
+  new_builder.PropagateSpaceShortage(container_builder_.MinimalSpaceShortage());
+  return algorithm_with_break.Layout();
+}
+
 NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns(
     const LogicalSize& column_size,
     bool is_first_fragmentainer,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
index 5707f83..42b5e9d8 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
@@ -10,6 +10,7 @@
 
 namespace blink {
 
+enum class NGBreakStatus;
 class NGBlockNode;
 class NGBlockBreakToken;
 class NGConstraintSpace;
@@ -29,8 +30,10 @@
       const MinMaxSizeInput&) const override;
 
  private:
-  // Lay out as many children as we can.
-  void LayoutChildren();
+  // Lay out as many children as we can. If false is returned, it means that we
+  // ran out of space at an unappealing location, and need to relayout and break
+  // earlier (because we have a better breakpoint there).
+  bool LayoutChildren();
 
   // Lay out one row of columns. The layout result returned is for the last
   // column that was laid out. The rows themselves don't create fragments.
@@ -38,13 +41,16 @@
       const NGBlockBreakToken* next_column_token,
       NGMarginStrut*);
 
-  // Lay out a column spanner. Will return a break token if we break before or
-  // inside the spanner. If no break token is returned, it means that we can
-  // proceed to the next row of columns.
-  scoped_refptr<const NGBlockBreakToken> LayoutSpanner(
-      NGBlockNode spanner_node,
-      const NGBlockBreakToken* break_token,
-      NGMarginStrut*);
+  // Lay out a column spanner. The return value will tell whether to break
+  // before the spanner or not. If we're not to break before the spanner, but
+  // rather inside, |spanner_break_token| will be set, so that we know where to
+  // resume in the next outer fragmentainer. If |NGBreakStatus::kContinue| is
+  // returned, and no break token was set, it means that we can proceed to the
+  // next row of columns.
+  NGBreakStatus LayoutSpanner(NGBlockNode spanner_node,
+                              const NGBlockBreakToken* break_token,
+                              NGMarginStrut*,
+                              scoped_refptr<const NGBlockBreakToken>*);
 
   LayoutUnit CalculateBalancedColumnBlockSize(
       const LogicalSize& column_size,
@@ -58,6 +64,16 @@
   LayoutUnit ConstrainColumnBlockSize(LayoutUnit size) const;
   LayoutUnit CurrentContentBlockOffset() const;
 
+  // Finalize layout after breaking before a spanner.
+  void FinishAfterBreakBeforeSpanner(
+      scoped_refptr<const NGBlockBreakToken> next_column_token);
+
+  // Lay out again, this time with a predefined good breakpoint that we
+  // discovered in the first pass. This happens when we run out of space in a
+  // fragmentainer at an less-than-ideal location, due to breaking restrictions,
+  // such as break-before:avoid or break-after:avoid.
+  scoped_refptr<const NGLayoutResult> RelayoutAndBreakEarlier();
+
   NGConstraintSpace CreateConstraintSpaceForColumns(
       const LogicalSize& column_size,
       bool is_first_fragmentainer,
@@ -68,6 +84,9 @@
       LayoutUnit block_offset) const;
   NGConstraintSpace CreateConstraintSpaceForMinMax() const;
 
+  // When set, this will specify where to break before or inside.
+  const NGEarlyBreak* early_break_ = nullptr;
+
   const NGBoxStrut border_padding_;
   const NGBoxStrut border_scrollbar_padding_;
   LogicalSize content_box_size_;
@@ -76,6 +95,11 @@
   LayoutUnit column_inline_progression_;
   LayoutUnit intrinsic_block_size_;
   bool is_constrained_by_outer_fragmentation_context_ = false;
+
+  // This will be set during (outer) block fragmentation once we've processed
+  // the first piece of content of the multicol container. It is used to check
+  // if we're at a valid class A  breakpoint (between block-level siblings).
+  bool has_processed_first_child_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
index e5de35ad..07118b1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
@@ -4672,6 +4672,424 @@
   EXPECT_EQ(expectation, dump);
 }
 
+TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreakBetweenSpanners) {
+  // There are two spanners in a nested multicol. They could fit in the same
+  // outer column, but there's a forced break between them.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:40px;"></div>
+          <div style="column-span:all; break-before:column; break-inside:avoid; width:66px; height:40px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x40
+      offset:110,0 size:100x40
+        offset:0,0 size:100x40
+          offset:0,0 size:66x40
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreakBetweenSpanners2) {
+  // There are two spanners in a nested multicol. They could fit in the same
+  // outer column, but there's a forced break between them.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-after:column; break-inside:avoid; width:55px; height:40px;"></div>
+          <div style="column-span:all; break-inside:avoid; width:66px; height:40px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x40
+      offset:110,0 size:100x40
+        offset:0,0 size:100x40
+          offset:0,0 size:66x40
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreakBetweenSpanners3) {
+  // There are two spanners in a nested multicol. They could fit in the same
+  // outer column, but there's a forced break after the last child of the first
+  // spanner.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:40px;">
+            <div style="width:33px; height:10px;"></div>
+            <div style="break-after:column; width:44px; height:10px;"></div>
+          </div>
+          <div style="column-span:all; break-inside:avoid; width:66px; height:40px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x40
+            offset:0,0 size:33x10
+            offset:0,10 size:44x10
+      offset:110,0 size:100x40
+        offset:0,0 size:100x40
+          offset:0,0 size:66x40
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreakBetweenSpanners4) {
+  // There are two spanners in a nested multicol. They could fit in the same
+  // outer column, but there's a forced break before the first child of the
+  // last spanner.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:40px;"></div>
+          <div style="column-span:all; break-inside:avoid; width:66px; height:40px;">
+            <div style="break-before:column; width:33px; height:10px;"></div>
+            <div style="width:44px; height:10px;"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x40
+      offset:110,0 size:100x40
+        offset:0,0 size:100x40
+          offset:0,0 size:66x40
+            offset:0,0 size:33x10
+            offset:0,10 size:44x10
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreakBetweenSpanners5) {
+  // There are two spanners in a nested multicol. They could fit in the same
+  // outer column, but there's a forced break between them. The second spanner
+  // has a top margin, which should be retained, due to the forced break.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:40px;"></div>
+          <div style="column-span:all; break-before:column; break-inside:avoid; width:66px; height:40px; margin-top:10px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x40
+      offset:110,0 size:100x50
+        offset:0,0 size:100x50
+          offset:0,10 size:66x40
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, SoftBreakBetweenSpanners) {
+  // There are two spanners in a nested multicol. They won't fit in the same
+  // outer column, and we don't want to break inside. So we should break between
+  // them.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:60px;"></div>
+          <div style="column-span:all; break-inside:avoid; width:66px; height:60px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x60
+      offset:110,0 size:100x60
+        offset:0,0 size:100x60
+          offset:0,0 size:66x60
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, SoftBreakBetweenSpanners2) {
+  // There are two spanners in a nested multicol. They won't fit in the same
+  // outer column, and we don't want to break inside. So we should break between
+  // them. The second spanner has a top margin, but it should be truncated since
+  // it's at a soft break.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:60px;"></div>
+          <div style="column-span:all; break-inside:avoid; width:66px; height:60px; margin-top:10px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x60
+      offset:110,0 size:100x60
+        offset:0,0 size:100x60
+          offset:0,0 size:66x60
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, AvoidSoftBreakBetweenSpanners) {
+  // There are three spanners in a nested multicol. The first two could fit in
+  // the same outer column, but the third one is too tall, and we also don't
+  // want to break before that one.So we should break between the two first
+  // spanners.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; break-inside:avoid; width:55px; height:40px;"></div>
+          <div style="column-span:all; break-inside:avoid; width:66px; height:40px;"></div>
+          <div style="column-span:all; break-inside:avoid; break-before:avoid; width:77px; height:60px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:55x40
+      offset:110,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:66x40
+          offset:0,40 size:77x60
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, AvoidSoftBreakBetweenSpanners2) {
+  // There are two spanners in a nested multicol. They won't fit in the same
+  // outer column, but we don't want to break inside the second one, and also
+  // not between the spanners. The first spanner is breakable, so we should
+  // break at the most appealing breakpoint there, i.e. before its last child.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer { columns:3; height:100px; column-fill:auto; column-gap:10px; width:320px; }
+      .inner { columns:2; }
+      .content { break-inside:avoid; height:20px; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; width:11px;">
+            <div class="content" style="width:22px;"></div>
+            <div class="content" style="width:33px;"></div>
+            <div class="content" style="width:44px;"></div>
+          </div>
+          <div style="column-span:all; break-inside:avoid; break-before:avoid; width:55px; height:60px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:11x100
+            offset:0,0 size:22x20
+            offset:0,20 size:33x20
+      offset:110,0 size:100x80
+        offset:0,0 size:100x80
+          offset:0,0 size:11x20
+            offset:0,0 size:44x20
+          offset:0,20 size:55x60
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, AvoidSoftBreakBetweenSpanners3) {
+  // Violate orphans and widows requests rather than break-between avoidance
+  // requests.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer {
+        columns:3;
+        height:100px;
+        column-fill:auto;
+        column-gap:10px;
+        width:320px;
+        line-height: 20px;
+        orphans: 3;
+        widows: 3;
+      }
+      .inner { columns:2; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div style="column-span:all; width:11px;">
+            <br>
+            <br>
+            <br>
+          </div>
+          <div style="column-span:all; break-inside:avoid; break-before:avoid; width:55px; height:60px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:11x100
+            offset:0,0 size:0x20
+              offset:0,9 size:0x1
+            offset:0,20 size:0x20
+              offset:0,9 size:0x1
+      offset:110,0 size:100x80
+        offset:0,0 size:100x80
+          offset:0,0 size:11x20
+            offset:0,0 size:0x20
+              offset:0,9 size:0x1
+          offset:0,20 size:55x60
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, SoftBreakBetweenRowAndSpanner) {
+  // We have a nested multicol with some column content, followed by a
+  // spanner. Everything won't fit in the same outer column, and we don't want
+  // to break inside the spanner. Break between the row of columns and the
+  // spanner.
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .outer {
+        columns:3;
+        height:100px;
+        column-fill:auto;
+        column-gap:10px;
+        width:320px;
+      }
+      .inner { columns:2; column-gap:10px; }
+      .content { break-inside:avoid; height:20px; }
+    </style>
+    <div id="container">
+      <div class="outer">
+        <div class="inner">
+          <div class="content" style="width:11px;"></div>
+          <div class="content" style="width:22px;"></div>
+          <div class="content" style="width:33px;"></div>
+          <div style="column-span:all; break-inside:avoid; width:44px; height:70px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:100x100
+          offset:0,0 size:45x40
+            offset:0,0 size:11x20
+            offset:0,20 size:22x20
+          offset:55,0 size:45x20
+            offset:0,0 size:33x20
+      offset:110,0 size:100x70
+        offset:0,0 size:100x70
+          offset:0,0 size:44x70
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 TEST_F(NGColumnLayoutAlgorithmTest, SpannerAsMulticol) {
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index d0b3b243..e1d3a77 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -213,6 +213,44 @@
   builder->SetIntrinsicBlockSize(intrinsic_block_size);
 }
 
+NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace& space,
+                                       NGLayoutInputNode child,
+                                       const NGLayoutResult& layout_result,
+                                       LayoutUnit fragmentainer_block_offset,
+                                       bool has_container_separation,
+                                       NGBoxFragmentBuilder* builder) {
+  DCHECK(space.HasBlockFragmentation());
+
+  if (has_container_separation) {
+    EBreakBetween break_between =
+        CalculateBreakBetweenValue(child, layout_result, *builder);
+    if (IsForcedBreakValue(space, break_between)) {
+      BreakBeforeChild(space, child, layout_result, fragmentainer_block_offset,
+                       kBreakAppealPerfect, /* is_forced_break */ true,
+                       builder);
+      return NGBreakStatus::kBrokeBefore;
+    }
+  }
+
+  NGBreakAppeal appeal_before = CalculateBreakAppealBefore(
+      space, child, layout_result, *builder, has_container_separation);
+
+  // Attempt to move past the break point, and if we can do that, also assess
+  // the appeal of breaking there, even if we didn't.
+  if (MovePastBreakpoint(space, child, layout_result,
+                         fragmentainer_block_offset, appeal_before, builder))
+    return NGBreakStatus::kContinue;
+
+  // Breaking inside the child isn't appealing, and we're out of space. Figure
+  // out where to insert a soft break. It will either be before this child, or
+  // before an earlier sibling, if there's a more appealing breakpoint there.
+  if (!AttemptSoftBreak(space, child, layout_result, fragmentainer_block_offset,
+                        appeal_before, builder))
+    return NGBreakStatus::kNeedsEarlierBreak;
+
+  return NGBreakStatus::kBrokeBefore;
+}
+
 void BreakBeforeChild(const NGConstraintSpace& space,
                       NGLayoutInputNode child,
                       const NGLayoutResult& layout_result,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
index 8fb3ade..c8ac752 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
@@ -109,6 +109,30 @@
   kNeedsEarlierBreak
 };
 
+// Insert a fragmentainer break before the child if necessary. In that case, the
+// previous in-flow position will be updated, we'll return |kBrokeBefore|. If we
+// don't break inside, we'll consider the appeal of doing so anyway (and store
+// it as the most appealing break point so far if that's the case), since we
+// might have to go back and break here. Return |kContinue| if we're to continue
+// laying out. If |kNeedsEarlierBreak| is returned, it means that we ran out of
+// space, but shouldn't break before the child, but rather abort layout, and
+// re-layout to a previously found good breakpoint.  If
+// |has_container_separation| is true, it means that we're at a valid
+// breakpoint. We obviously prefer valid breakpoints, but sometimes we need to
+// break at undesirable locations. Class A breakpoints occur between block
+// siblings. Class B breakpoints between line boxes. Both these breakpoint
+// classes imply that we're already past the first in-flow child in the
+// container, but there's also another way of achieving container separation:
+// class C breakpoints. Those occur if there's a positive gap between the
+// block-start content edge of the container and the block-start margin edge of
+// the first in-flow child. https://www.w3.org/TR/css-break-3/#possible-breaks
+NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace&,
+                                       NGLayoutInputNode child,
+                                       const NGLayoutResult&,
+                                       LayoutUnit fragmentainer_block_offset,
+                                       bool has_container_separation,
+                                       NGBoxFragmentBuilder*);
+
 // Insert a break before the child, and propagate space shortage if needed.
 void BreakBeforeChild(const NGConstraintSpace&,
                       NGLayoutInputNode child,
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 32620578..a2a132c 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
@@ -43,8 +43,6 @@
 
 namespace blink {
 
-using namespace svg_names;
-
 SVGResources::SVGResources() : linked_resource_(nullptr) {}
 
 SVGResourceClient* SVGResources::GetClient(const LayoutObject& object) {
@@ -59,26 +57,28 @@
           // http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
           // "graphics elements" :
           // http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement
-          kATag.LocalName(), kCircleTag.LocalName(), kEllipseTag.LocalName(),
-          kGTag.LocalName(), kImageTag.LocalName(), kLineTag.LocalName(),
-          kMarkerTag.LocalName(), kMaskTag.LocalName(), kPathTag.LocalName(),
-          kPolygonTag.LocalName(), kPolylineTag.LocalName(),
-          kRectTag.LocalName(), kSVGTag.LocalName(), kTextTag.LocalName(),
-          kUseTag.LocalName(),
+          svg_names::kATag.LocalName(), svg_names::kCircleTag.LocalName(),
+          svg_names::kEllipseTag.LocalName(), svg_names::kGTag.LocalName(),
+          svg_names::kImageTag.LocalName(), svg_names::kLineTag.LocalName(),
+          svg_names::kMarkerTag.LocalName(), svg_names::kMaskTag.LocalName(),
+          svg_names::kPathTag.LocalName(), svg_names::kPolygonTag.LocalName(),
+          svg_names::kPolylineTag.LocalName(), svg_names::kRectTag.LocalName(),
+          svg_names::kSVGTag.LocalName(), svg_names::kTextTag.LocalName(),
+          svg_names::kUseTag.LocalName(),
           // Not listed in the definitions is the clipPath element, the SVG spec
           // says though:
           // The "clipPath" element or any of its children can specify property
           // "clip-path".
           // So we have to add kClipPathTag here, otherwhise clip-path on
           // clipPath will fail. (Already mailed SVG WG, waiting for a solution)
-          kClipPathTag.LocalName(),
+          svg_names::kClipPathTag.LocalName(),
           // Not listed in the definitions are the text content elements, though
           // filter/clipper/masker on tspan/text/.. is allowed.
           // (Already mailed SVG WG, waiting for a solution)
-          kTextPathTag.LocalName(), kTSpanTag.LocalName(),
+          svg_names::kTextPathTag.LocalName(), svg_names::kTSpanTag.LocalName(),
           // Not listed in the definitions is the foreignObject element, but
           // clip-path is a supported attribute.
-          kForeignObjectTag.LocalName(),
+          svg_names::kForeignObjectTag.LocalName(),
           // Elements that we ignore, as it doesn't make any sense.
           // defs, pattern, switch (FIXME: Mail SVG WG about these)
           // symbol (is converted to a svg element, when referenced by use, we
@@ -90,21 +90,28 @@
 bool SVGResources::SupportsMarkers(const SVGElement& element) {
   DEFINE_STATIC_LOCAL(HashSet<AtomicString>, tag_list,
                       ({
-                          kLineTag.LocalName(), kPathTag.LocalName(),
-                          kPolygonTag.LocalName(), kPolylineTag.LocalName(),
+                          svg_names::kLineTag.LocalName(),
+                          svg_names::kPathTag.LocalName(),
+                          svg_names::kPolygonTag.LocalName(),
+                          svg_names::kPolylineTag.LocalName(),
                       }));
   return tag_list.Contains(element.localName());
 }
 
 static HashSet<AtomicString>& FillAndStrokeTags() {
-  DEFINE_STATIC_LOCAL(
-      HashSet<AtomicString>, tag_list,
-      ({
-          kCircleTag.LocalName(), kEllipseTag.LocalName(), kLineTag.LocalName(),
-          kPathTag.LocalName(), kPolygonTag.LocalName(),
-          kPolylineTag.LocalName(), kRectTag.LocalName(), kTextTag.LocalName(),
-          kTextPathTag.LocalName(), kTSpanTag.LocalName(),
-      }));
+  DEFINE_STATIC_LOCAL(HashSet<AtomicString>, tag_list,
+                      ({
+                          svg_names::kCircleTag.LocalName(),
+                          svg_names::kEllipseTag.LocalName(),
+                          svg_names::kLineTag.LocalName(),
+                          svg_names::kPathTag.LocalName(),
+                          svg_names::kPolygonTag.LocalName(),
+                          svg_names::kPolylineTag.LocalName(),
+                          svg_names::kRectTag.LocalName(),
+                          svg_names::kTextTag.LocalName(),
+                          svg_names::kTextPathTag.LocalName(),
+                          svg_names::kTSpanTag.LocalName(),
+                      }));
   return tag_list;
 }
 
diff --git a/third_party/blink/renderer/core/loader/form_submission.cc b/third_party/blink/renderer/core/loader/form_submission.cc
index d5e9b38..ea1f3f75 100644
--- a/third_party/blink/renderer/core/loader/form_submission.cc
+++ b/third_party/blink/renderer/core/loader/form_submission.cc
@@ -52,8 +52,6 @@
 
 namespace blink {
 
-using namespace html_names;
-
 static int64_t GenerateFormDataIdentifier() {
   // Initialize to the current time to reduce the likelihood of generating
   // identifiers that overlap with those from past/future browser sessions.
@@ -183,16 +181,20 @@
   copied_attributes.CopyFrom(attributes);
   if (submit_button) {
     AtomicString attribute_value;
-    if (!(attribute_value = submit_button->FastGetAttribute(kFormactionAttr))
+    if (!(attribute_value =
+              submit_button->FastGetAttribute(html_names::kFormactionAttr))
              .IsNull())
       copied_attributes.ParseAction(attribute_value);
-    if (!(attribute_value = submit_button->FastGetAttribute(kFormenctypeAttr))
+    if (!(attribute_value =
+              submit_button->FastGetAttribute(html_names::kFormenctypeAttr))
              .IsNull())
       copied_attributes.UpdateEncodingType(attribute_value);
-    if (!(attribute_value = submit_button->FastGetAttribute(kFormmethodAttr))
+    if (!(attribute_value =
+              submit_button->FastGetAttribute(html_names::kFormmethodAttr))
              .IsNull())
       copied_attributes.UpdateMethodType(attribute_value);
-    if (!(attribute_value = submit_button->FastGetAttribute(kFormtargetAttr))
+    if (!(attribute_value =
+              submit_button->FastGetAttribute(html_names::kFormtargetAttr))
              .IsNull())
       copied_attributes.SetTarget(attribute_value);
   }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 8d19bd7..39ca1d2 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -123,8 +123,6 @@
 
 namespace blink {
 
-using namespace html_names;
-
 bool IsBackForwardLoadType(WebFrameLoadType type) {
   return type == WebFrameLoadType::kBackForward;
 }
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
index adeed97..7389a37 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
@@ -36,7 +36,7 @@
 
 namespace blink {
 
-using namespace html_names;
+using html_names::kStyleAttr;
 
 // NOTE: This test uses <iframe sandbox> to create cross origin iframes.
 
@@ -285,7 +285,7 @@
   // TODO(skyostil): these expectations are either wrong, or the test is
   // not exercising the code correctly. PaintClean means the entire lifecycle
   // ran.
-  frame_element->setAttribute(kWidthAttr, "50");
+  frame_element->setAttribute(html_names::kWidthAttr, "50");
   CompositeFrame();
 
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index 314bd14..3beacf21d 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -298,7 +298,6 @@
     ClearResourceAndEventBaseReferences();
     ClearConditions();
     SetTargetElement(nullptr);
-    AnimationAttributeChanged();
     time_container_ = nullptr;
   }
 
@@ -483,7 +482,6 @@
         time_container_->ScheduleIntervalUpdate();
       }
     }
-    AnimationAttributeChanged();
   } else if (name == svg_names::kEndAttr) {
     if (!conditions_.IsEmpty()) {
       ClearConditions();
@@ -499,7 +497,6 @@
         time_container_->ScheduleIntervalUpdate();
       }
     }
-    AnimationAttributeChanged();
   } else if (name == svg_names::kOnbeginAttr) {
     SetAttributeEventListener(event_type_names::kBeginEvent,
                               CreateAttributeEventListener(this, name, value));
@@ -518,34 +515,31 @@
       restart_ = kRestartAlways;
   } else if (name == svg_names::kFillAttr) {
     fill_ = value == "freeze" ? kFillFreeze : kFillRemove;
+  } else if (name == svg_names::kDurAttr) {
+    cached_dur_ = kInvalidCachedTime;
+  } else if (name == svg_names::kRepeatDurAttr) {
+    cached_repeat_dur_ = kInvalidCachedTime;
+  } else if (name == svg_names::kRepeatCountAttr) {
+    cached_repeat_count_ = SMILRepeatCount::Invalid();
+  } else if (name == svg_names::kMinAttr) {
+    cached_min_ = kInvalidCachedTime;
+  } else if (name == svg_names::kMaxAttr) {
+    cached_max_ = kInvalidCachedTime;
   } else {
     SVGElement::ParseAttribute(params);
   }
 }
 
 void SVGSMILElement::SvgAttributeChanged(const QualifiedName& attr_name) {
-  if (attr_name == svg_names::kDurAttr) {
-    cached_dur_ = kInvalidCachedTime;
-  } else if (attr_name == svg_names::kRepeatDurAttr) {
-    cached_repeat_dur_ = kInvalidCachedTime;
-  } else if (attr_name == svg_names::kRepeatCountAttr) {
-    cached_repeat_count_ = SMILRepeatCount::Invalid();
-  } else if (attr_name == svg_names::kMinAttr) {
-    cached_min_ = kInvalidCachedTime;
-  } else if (attr_name == svg_names::kMaxAttr) {
-    cached_max_ = kInvalidCachedTime;
-  } else if (attr_name.Matches(svg_names::kHrefAttr) ||
-             attr_name.Matches(xlink_names::kHrefAttr)) {
+  if (attr_name.Matches(svg_names::kHrefAttr) ||
+      attr_name.Matches(xlink_names::kHrefAttr)) {
     // TODO(fs): Could be smarter here when 'href' is specified and 'xlink:href'
     // is changed.
     SVGElement::InvalidationGuard invalidation_guard(this);
     BuildPendingResource();
-  } else {
-    SVGElement::SvgAttributeChanged(attr_name);
     return;
   }
-
-  AnimationAttributeChanged();
+  SVGElement::SvgAttributeChanged(attr_name);
 }
 
 void SVGSMILElement::ConnectConditions() {
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.h b/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
index dbf83e6..6c85013 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
@@ -56,7 +56,6 @@
   void RemovedFrom(ContainerNode&) override;
 
   virtual bool HasValidTarget() const;
-  virtual void AnimationAttributeChanged() = 0;
 
   SMILTimeContainer* TimeContainer() const { return time_container_.Get(); }
 
@@ -131,16 +130,12 @@
 
   void AddInstanceTimeAndUpdate(BeginOrEnd, SMILTime, SMILTimeOrigin);
 
-  void SetInactive() { active_state_ = kInactive; }
-
   void SetTargetElement(SVGElement*);
 
   // Sub-classes may need to take action when the target is changed.
   virtual void WillChangeAnimationTarget();
   virtual void DidChangeAnimationTarget();
 
-  virtual void StartedActiveInterval();
-
   QualifiedName attribute_name_;
 
  private:
@@ -148,6 +143,7 @@
   void ClearResourceAndEventBaseReferences();
   void ClearConditions();
 
+  void StartedActiveInterval();
   void EndedActiveInterval();
   virtual void UpdateAnimation(float percent,
                                unsigned repeat,
diff --git a/third_party/blink/renderer/core/svg/svg_animate_element.cc b/third_party/blink/renderer/core/svg/svg_animate_element.cc
index bad29363..e73be4a 100644
--- a/third_party/blink/renderer/core/svg/svg_animate_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animate_element.cc
@@ -155,12 +155,10 @@
     const AttributeModificationParams& params) {
   if (params.name == svg_names::kAttributeTypeAttr) {
     SetAttributeType(params.new_value);
-    AnimationAttributeChanged();
     return;
   }
   if (params.name == svg_names::kAttributeNameAttr) {
     SetAttributeName(ConstructQualifiedName(*this, params.new_value));
-    AnimationAttributeChanged();
     return;
   }
   SVGAnimationElement::ParseAttribute(params);
@@ -550,7 +548,9 @@
   SVGAnimationElement::WillChangeAnimationTarget();
   // Should be cleared by the above.
   DCHECK(!animated_value_);
-  ResetCachedAnimationState();
+  from_property_.Clear();
+  to_property_.Clear();
+  to_at_end_of_duration_property_.Clear();
 }
 
 void SVGAnimateElement::DidChangeAnimationTarget() {
@@ -582,14 +582,6 @@
   DidChangeAnimationTarget();
 }
 
-void SVGAnimateElement::ResetCachedAnimationState() {
-  DCHECK(!animated_value_);
-  InvalidatedValuesCache();
-  from_property_.Clear();
-  to_property_.Clear();
-  to_at_end_of_duration_property_.Clear();
-}
-
 void SVGAnimateElement::Trace(blink::Visitor* visitor) {
   visitor->Trace(from_property_);
   visitor->Trace(to_property_);
diff --git a/third_party/blink/renderer/core/svg/svg_animate_element.h b/third_party/blink/renderer/core/svg/svg_animate_element.h
index 5bd334bb..91dad7aa 100644
--- a/third_party/blink/renderer/core/svg/svg_animate_element.h
+++ b/third_party/blink/renderer/core/svg/svg_animate_element.h
@@ -90,8 +90,6 @@
                            stringsShouldNotSupportAddition);
 
  private:
-  void ResetCachedAnimationState();
-
   bool ShouldApplyAnimation(const SVGElement& target_element) const;
 
   void SetAttributeType(const AtomicString&);
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.cc b/third_party/blink/renderer/core/svg/svg_animation_element.cc
index 0040d0a..86e0627b 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.cc
@@ -38,7 +38,7 @@
 SVGAnimationElement::SVGAnimationElement(const QualifiedName& tag_name,
                                          Document& document)
     : SVGSMILElement(tag_name, document),
-      animation_valid_(false),
+      animation_valid_(AnimationValidity::kUnknown),
       calc_mode_(kCalcModeLinear),
       animation_mode_(kNoAnimation) {
   UseCounter::Count(document, WebFeature::kSVGAnimationElement);
@@ -171,6 +171,7 @@
       return;
     }
     UpdateAnimationMode();
+    AnimationAttributeChanged();
     return;
   }
 
@@ -179,6 +180,7 @@
       ReportAttributeParsingError(SVGParseStatus::kParsingFailed, name,
                                   params.new_value);
     }
+    AnimationAttributeChanged();
     return;
   }
 
@@ -191,6 +193,7 @@
                                     params.new_value);
       }
     }
+    AnimationAttributeChanged();
     return;
   }
 
@@ -199,47 +202,36 @@
       ReportAttributeParsingError(SVGParseStatus::kParsingFailed, name,
                                   params.new_value);
     }
+    AnimationAttributeChanged();
     return;
   }
 
   if (name == svg_names::kCalcModeAttr) {
     SetCalcMode(params.new_value);
+    AnimationAttributeChanged();
     return;
   }
 
   if (name == svg_names::kFromAttr || name == svg_names::kToAttr ||
       name == svg_names::kByAttr) {
     UpdateAnimationMode();
+    AnimationAttributeChanged();
     return;
   }
 
   SVGSMILElement::ParseAttribute(params);
 }
 
-void SVGAnimationElement::SvgAttributeChanged(const QualifiedName& attr_name) {
-  if (attr_name == svg_names::kValuesAttr || attr_name == svg_names::kByAttr ||
-      attr_name == svg_names::kFromAttr || attr_name == svg_names::kToAttr ||
-      attr_name == svg_names::kCalcModeAttr ||
-      attr_name == svg_names::kKeySplinesAttr ||
-      attr_name == svg_names::kKeyPointsAttr ||
-      attr_name == svg_names::kKeyTimesAttr) {
-    AnimationAttributeChanged();
-    return;
-  }
-
-  SVGSMILElement::SvgAttributeChanged(attr_name);
-}
-
-void SVGAnimationElement::InvalidatedValuesCache() {
+void SVGAnimationElement::AnimationAttributeChanged() {
+  // Assumptions may not hold after an attribute change.
+  animation_valid_ = AnimationValidity::kUnknown;
   last_values_animation_from_ = String();
   last_values_animation_to_ = String();
 }
 
-void SVGAnimationElement::AnimationAttributeChanged() {
-  // Assumptions may not hold after an attribute change.
-  animation_valid_ = false;
-  InvalidatedValuesCache();
-  SetInactive();
+void SVGAnimationElement::WillChangeAnimationTarget() {
+  SVGSMILElement::WillChangeAnimationTarget();
+  AnimationAttributeChanged();
 }
 
 float SVGAnimationElement::getStartTime(ExceptionState& exception_state) const {
@@ -472,7 +464,7 @@
     String& from,
     String& to) {
   unsigned values_count = values_.size();
-  DCHECK(animation_valid_);
+  DCHECK_EQ(animation_valid_, AnimationValidity::kValid);
   DCHECK_GE(values_count, 1u);
 
   if (percent == 1 || values_count == 1) {
@@ -528,18 +520,14 @@
   }
 }
 
-void SVGAnimationElement::StartedActiveInterval() {
-  SVGSMILElement::StartedActiveInterval();
-
-  animation_valid_ = false;
-
+bool SVGAnimationElement::CheckAnimationParameters() {
   if (!IsValid() || !HasValidTarget())
-    return;
+    return false;
 
   // These validations are appropriate for all animation modes.
   if (FastHasAttribute(svg_names::kKeyPointsAttr) &&
       key_points_.size() != KeyTimes().size())
-    return;
+    return false;
 
   AnimationMode animation_mode = GetAnimationMode();
   CalcMode calc_mode = GetCalcMode();
@@ -552,34 +540,36 @@
          values_.size() - 1 != splines_count) ||
         (FastHasAttribute(svg_names::kKeyTimesAttr) &&
          KeyTimes().size() - 1 != splines_count))
-      return;
+      return false;
   }
 
   String from = FromValue();
   String to = ToValue();
   String by = ByValue();
   if (animation_mode == kNoAnimation)
-    return;
+    return false;
   if ((animation_mode == kFromToAnimation ||
        animation_mode == kFromByAnimation || animation_mode == kToAnimation ||
        animation_mode == kByAnimation) &&
       (FastHasAttribute(svg_names::kKeyPointsAttr) &&
        FastHasAttribute(svg_names::kKeyTimesAttr) &&
        (KeyTimes().size() < 2 || KeyTimes().size() != key_points_.size())))
-    return;
-  if (animation_mode == kFromToAnimation) {
-    animation_valid_ = CalculateFromAndToValues(from, to);
-  } else if (animation_mode == kToAnimation) {
+    return false;
+  if (animation_mode == kFromToAnimation)
+    return CalculateFromAndToValues(from, to);
+  if (animation_mode == kToAnimation) {
     // For to-animations the from value is the current accumulated value from
     // lower priority animations.
     // The value is not static and is determined during the animation.
-    animation_valid_ = CalculateFromAndToValues(g_empty_string, to);
-  } else if (animation_mode == kFromByAnimation) {
-    animation_valid_ = CalculateFromAndByValues(from, by);
-  } else if (animation_mode == kByAnimation) {
-    animation_valid_ = CalculateFromAndByValues(g_empty_string, by);
-  } else if (animation_mode == kValuesAnimation) {
-    animation_valid_ =
+    return CalculateFromAndToValues(g_empty_string, to);
+  }
+  if (animation_mode == kFromByAnimation)
+    return CalculateFromAndByValues(from, by);
+  if (animation_mode == kByAnimation)
+    return CalculateFromAndByValues(g_empty_string, by);
+  if (animation_mode == kValuesAnimation) {
+    // o_O - TODO(fs): move this to a helper function.
+    bool animation_valid =
         values_.size() >= 1 &&
         (calc_mode == kCalcModePaced ||
          !FastHasAttribute(svg_names::kKeyTimesAttr) ||
@@ -593,25 +583,38 @@
           key_splines_.size() == key_points_.size() - 1)) &&
         (!FastHasAttribute(svg_names::kKeyPointsAttr) ||
          (KeyTimes().size() > 1 && KeyTimes().size() == key_points_.size()));
-    if (animation_valid_)
-      animation_valid_ = CalculateToAtEndOfDurationValue(values_.back());
-    if (calc_mode == kCalcModePaced && animation_valid_)
+    if (animation_valid)
+      animation_valid = CalculateToAtEndOfDurationValue(values_.back());
+    if (calc_mode == kCalcModePaced && animation_valid)
       CalculateKeyTimesForCalcModePaced();
-  } else if (animation_mode == kPathAnimation) {
-    animation_valid_ =
-        calc_mode == kCalcModePaced ||
-        !FastHasAttribute(svg_names::kKeyPointsAttr) ||
-        (KeyTimes().size() > 1 && KeyTimes().size() == key_points_.size());
+    return animation_valid;
   }
-
-  if (animation_valid_ && (IsAdditive() || IsAccumulated()))
-    UseCounter::Count(&GetDocument(), WebFeature::kSVGSMILAdditiveAnimation);
+  if (animation_mode == kPathAnimation) {
+    return calc_mode == kCalcModePaced ||
+           !FastHasAttribute(svg_names::kKeyPointsAttr) ||
+           (KeyTimes().size() > 1 && KeyTimes().size() == key_points_.size());
+  }
+  return false;
 }
 
 void SVGAnimationElement::UpdateAnimation(float percent,
                                           unsigned repeat_count,
                                           SVGSMILElement* result_element) {
-  if (!animation_valid_ || !targetElement())
+  if (animation_valid_ == AnimationValidity::kUnknown) {
+    if (CheckAnimationParameters()) {
+      animation_valid_ = AnimationValidity::kValid;
+
+      if (IsAdditive() || IsAccumulated()) {
+        UseCounter::Count(&GetDocument(),
+                          WebFeature::kSVGSMILAdditiveAnimation);
+      }
+    } else {
+      animation_valid_ = AnimationValidity::kInvalid;
+    }
+  }
+  DCHECK_NE(animation_valid_, AnimationValidity::kUnknown);
+
+  if (animation_valid_ != AnimationValidity::kValid || !targetElement())
     return;
 
   float effective_percent;
@@ -623,9 +626,10 @@
     CurrentValuesForValuesAnimation(percent, effective_percent, from, to);
     if (from != last_values_animation_from_ ||
         to != last_values_animation_to_) {
-      animation_valid_ = CalculateFromAndToValues(from, to);
-      if (!animation_valid_)
+      if (!CalculateFromAndToValues(from, to)) {
+        animation_valid_ = AnimationValidity::kInvalid;
         return;
+      }
       last_values_animation_from_ = from;
       last_values_animation_to_ = to;
     }
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.h b/third_party/blink/renderer/core/svg/svg_animation_element.h
index 92721a9..a434086fa 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.h
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.h
@@ -114,14 +114,12 @@
   SVGAnimationElement(const QualifiedName&, Document&);
 
   void ParseAttribute(const AttributeModificationParams&) override;
-  void SvgAttributeChanged(const QualifiedName&) override;
 
   String ToValue() const;
   String ByValue() const;
   String FromValue() const;
 
   // from SVGSMILElement
-  void StartedActiveInterval() override;
   void UpdateAnimation(float percent,
                        unsigned repeat,
                        SVGSMILElement* result_element) override;
@@ -142,12 +140,13 @@
   // http://www.w3.org/TR/SVG/animate.html#ValuesAttribute .
   static bool ParseValues(const String&, Vector<String>& result);
 
-  void InvalidatedValuesCache();
-  void AnimationAttributeChanged() override;
+  void WillChangeAnimationTarget() override;
 
  private:
   bool IsValid() const final { return SVGTests::IsValid(); }
 
+  void AnimationAttributeChanged();
+  bool CheckAnimationParameters();
   virtual bool CalculateToAtEndOfDurationValue(
       const String& to_at_end_of_duration_string) = 0;
   virtual bool CalculateFromAndToValues(const String& from_string,
@@ -186,7 +185,12 @@
 
   void SetCalcMode(const AtomicString&);
 
-  bool animation_valid_;
+  enum class AnimationValidity : unsigned char {
+    kUnknown,
+    kValid,
+    kInvalid,
+  };
+  AnimationValidity animation_valid_;
   bool use_paced_key_times_;
 
   Vector<String> values_;
diff --git a/third_party/blink/renderer/core/svg/svg_discard_element.h b/third_party/blink/renderer/core/svg/svg_discard_element.h
index dffcf52..374a308 100644
--- a/third_party/blink/renderer/core/svg/svg_discard_element.h
+++ b/third_party/blink/renderer/core/svg/svg_discard_element.h
@@ -47,7 +47,6 @@
   void ResetAnimatedType() override {}
   void ClearAnimatedType() override {}
   void ApplyResultsToTarget() override {}
-  void AnimationAttributeChanged() override {}
 
   bool OverwritesUnderlyingAnimationValue() const override { return false; }
 
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index d87913f..1ab288d5 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -136,7 +136,6 @@
         'recordCoverageWithPerformanceTracing', 'Record coverage while performance tracing');
     Root.Runtime.experiments.register('samplingHeapProfilerTimeline', 'Sampling heap profiler timeline', true);
     Root.Runtime.experiments.register('sourceDiff', 'Source diff');
-    Root.Runtime.experiments.register('splitInDrawer', 'Split in drawer', true);
     Root.Runtime.experiments.register('spotlight', 'Spotlight', true);
 
     // Timeline
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/EmulationModel.js b/third_party/blink/renderer/devtools/front_end/sdk/EmulationModel.js
index 0c23e275..724abf2b 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/EmulationModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/EmulationModel.js
@@ -279,7 +279,8 @@
     // but instead tries to make sense of the input, even for
     // weird-looking timezone IDs. There's not much point in validating
     // the input other than checking if it contains at least one slash.
-    const valid = value.includes('/');
+    // The empty string resets the override, and is accepted as well.
+    const valid = value === '' || value.includes('/');
     return {valid};
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js b/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js
index 25e37d5..bd395ae 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js
@@ -44,16 +44,6 @@
     this._drawerSplitWidget.enableShowModeSaving();
     this._drawerSplitWidget.show(this.element);
 
-    if (Root.Runtime.experiments.isEnabled('splitInDrawer')) {
-      this._innerDrawerSplitWidget = new UI.SplitWidget(true, true, 'Inspector.drawerSidebarSplitViewState', 200, 200);
-      this._drawerSplitWidget.setSidebarWidget(this._innerDrawerSplitWidget);
-      this._drawerSidebarTabbedLocation =
-          UI.viewManager.createTabbedLocation(this._showDrawer.bind(this, false), 'drawer-sidebar', true, true);
-      this._drawerSidebarTabbedPane = this._drawerSidebarTabbedLocation.tabbedPane();
-      this._drawerSidebarTabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, this._drawerTabSelected, this);
-      this._innerDrawerSplitWidget.setSidebarWidget(this._drawerSidebarTabbedPane);
-    }
-
     // Create drawer tabbed pane.
     this._drawerTabbedLocation =
         UI.viewManager.createTabbedLocation(this._showDrawer.bind(this, false), 'drawer-view', true, true);
@@ -66,14 +56,8 @@
     this._drawerSplitWidget.installResizer(this._drawerTabbedPane.headerElement());
     this._drawerTabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, this._drawerTabSelected, this);
 
-    if (this._drawerSidebarTabbedPane) {
-      this._innerDrawerSplitWidget.setMainWidget(this._drawerTabbedPane);
-      this._drawerSidebarTabbedPane.rightToolbar().appendToolbarItem(closeDrawerButton);
-      this._drawerSplitWidget.installResizer(this._drawerSidebarTabbedPane.headerElement());
-    } else {
-      this._drawerSplitWidget.setSidebarWidget(this._drawerTabbedPane);
-      this._drawerTabbedPane.rightToolbar().appendToolbarItem(closeDrawerButton);
-    }
+    this._drawerSplitWidget.setSidebarWidget(this._drawerTabbedPane);
+    this._drawerTabbedPane.rightToolbar().appendToolbarItem(closeDrawerButton);
 
     // Create main area tabbed pane.
     this._tabbedLocation = UI.viewManager.createTabbedLocation(
@@ -140,9 +124,6 @@
     if (locationName === 'panel') {
       return this._tabbedLocation;
     }
-    if (locationName === 'drawer-sidebar') {
-      return this._drawerSidebarTabbedLocation;
-    }
     return null;
   }
 
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index cb8e5c0..5f787b71 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -150,6 +150,7 @@
     "//third_party/blink/renderer/modules/webmidi",
     "//third_party/blink/renderer/modules/webshare",
     "//third_party/blink/renderer/modules/websockets",
+    "//third_party/blink/renderer/modules/webtransport",
     "//third_party/blink/renderer/modules/webusb",
     "//third_party/blink/renderer/modules/worklet",
     "//third_party/blink/renderer/modules/xr",
@@ -401,8 +402,8 @@
     "service_worker/service_worker_timeout_timer_test.cc",
     "service_worker/thread_safe_script_container_test.cc",
     "service_worker/web_embedded_worker_impl_test.cc",
+    "wake_lock/wake_lock_manager_test.cc",
     "wake_lock/wake_lock_sentinel_test.cc",
-    "wake_lock/wake_lock_state_record_test.cc",
     "wake_lock/wake_lock_test.cc",
     "wake_lock/wake_lock_test_utils.cc",
     "wake_lock/wake_lock_test_utils.h",
@@ -458,6 +459,7 @@
     "//third_party/blink/renderer/modules/gamepad:unit_tests",
     "//third_party/blink/renderer/modules/hid:unit_tests",
     "//third_party/blink/renderer/modules/storage:unit_tests",
+    "//third_party/blink/renderer/modules/webtransport:unit_tests",
     "//third_party/blink/renderer/platform",
     "//third_party/blink/renderer/platform/wtf",
     "//third_party/opus",
diff --git a/third_party/blink/renderer/modules/mediastream/DEPS b/third_party/blink/renderer/modules/mediastream/DEPS
index 59774da..03a43394 100644
--- a/third_party/blink/renderer/modules/mediastream/DEPS
+++ b/third_party/blink/renderer/modules/mediastream/DEPS
@@ -45,6 +45,7 @@
     "+third_party/blink/renderer/modules/imagecapture",
     "+third_party/blink/renderer/modules/mediastream",
     "+third_party/blink/renderer/modules/modules_export.h",
+    "+third_party/blink/renderer/modules/peerconnection",
     "+ui/gfx/geometry/size.h",
 ]
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc
index 3bf00e9..457f0f8 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc
@@ -154,12 +154,14 @@
     const base::Optional<int>& requested_buffer_size,
     bool disable_local_echo,
     bool enable_automatic_output_device_selection,
+    ProcessingType processing_type,
     const AudioProcessingProperties& audio_processing_properties)
     : failed_constraint_name_(nullptr),
       device_id_(std::move(device_id)),
       requested_buffer_size_(requested_buffer_size),
       disable_local_echo_(disable_local_echo),
       render_to_associated_sink_(enable_automatic_output_device_selection),
+      processing_type_(processing_type),
       audio_processing_properties_(audio_processing_properties) {}
 
 AudioCaptureSettings::AudioCaptureSettings(const AudioCaptureSettings& other) =
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
index 1a2a3bd..7b1c579a 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
@@ -795,6 +795,8 @@
            sample_rate_container_.IsEmpty() || latency_container_.IsEmpty();
   }
 
+  ProcessingType processing_type() const { return processing_type_; }
+
  private:
   enum BooleanContainerId {
     kGoogAudioMirroring,
@@ -996,41 +998,27 @@
     // Three variations of the processing-based container. Each variant is
     // associated to a different type of audio processing configuration, namely
     // unprocessed, processed by WebRTC, or processed by other means.
-    if (is_reconfiguration_allowed || source_info.type() == SourceType::kNone ||
-        source_info.type() == SourceType::kUnprocessed) {
+    processing_based_containers_.push_back(
+        ProcessingBasedContainer::CreateUnprocessedContainer(
+            source_info, is_device_capture, device_parameters_,
+            is_reconfiguration_allowed));
+    processing_based_containers_.push_back(
+        ProcessingBasedContainer::CreateNoApmProcessedContainer(
+            source_info, is_device_capture, device_parameters_,
+            is_reconfiguration_allowed));
+    if (media::IsWebRtcApmInAudioServiceEnabled()) {
       processing_based_containers_.push_back(
-          ProcessingBasedContainer::CreateUnprocessedContainer(
+          ProcessingBasedContainer::CreateRemoteApmProcessedContainer(
               source_info, is_device_capture, device_parameters_,
               is_reconfiguration_allowed));
-    }
-    if (is_reconfiguration_allowed || source_info.type() == SourceType::kNone ||
-        source_info.type() == SourceType::kNoApmProcessed) {
+    } else {
       processing_based_containers_.push_back(
-          ProcessingBasedContainer::CreateNoApmProcessedContainer(
+          ProcessingBasedContainer::CreateApmProcessedContainer(
               source_info, is_device_capture, device_parameters_,
               is_reconfiguration_allowed));
     }
-    if (is_reconfiguration_allowed || source_info.type() == SourceType::kNone ||
-        source_info.type() == SourceType::kApmProcessed) {
-      if (media::IsWebRtcApmInAudioServiceEnabled()) {
-        processing_based_containers_.push_back(
-            ProcessingBasedContainer::CreateRemoteApmProcessedContainer(
-                source_info, is_device_capture, device_parameters_,
-                is_reconfiguration_allowed));
-      } else {
-        processing_based_containers_.push_back(
-            ProcessingBasedContainer::CreateApmProcessedContainer(
-                source_info, is_device_capture, device_parameters_,
-                is_reconfiguration_allowed));
-      }
-    }
 
-#if DCHECK_IS_ON()
-    if (is_reconfiguration_allowed || source_info.type() == SourceType::kNone)
-      DCHECK_EQ(processing_based_containers_.size(), 3u);
-    else
-      DCHECK_EQ(processing_based_containers_.size(), 1u);
-#endif
+    DCHECK_EQ(processing_based_containers_.size(), 3u);
 
     if (source_info.type() == SourceType::kNone)
       return;
@@ -1164,7 +1152,8 @@
     return std::make_tuple(
         score, AudioCaptureSettings(
                    device_id, best_requested_buffer_size, disable_local_echo,
-                   render_to_associated_sink, best_properties));
+                   render_to_associated_sink, best_container->processing_type(),
+                   best_properties));
   }
 
   // The DeviceContainer is considered empty if at least one of the
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc
index e062b237..72327ac 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc
@@ -33,6 +33,7 @@
 using blink::AudioCaptureSettings;
 using blink::AudioProcessingProperties;
 using EchoCancellationType = AudioProcessingProperties::EchoCancellationType;
+using ProcessingType = AudioCaptureSettings::ProcessingType;
 
 namespace {
 
@@ -60,6 +61,15 @@
 using AudioPropertiesBoolMembers =
     std::vector<bool AudioProcessingProperties::*>;
 
+const AudioPropertiesBoolMembers kAudioProcessingProperties = {
+    &AudioProcessingProperties::goog_audio_mirroring,
+    &AudioProcessingProperties::goog_auto_gain_control,
+    &AudioProcessingProperties::goog_experimental_echo_cancellation,
+    &AudioProcessingProperties::goog_noise_suppression,
+    &AudioProcessingProperties::goog_experimental_noise_suppression,
+    &AudioProcessingProperties::goog_highpass_filter,
+    &AudioProcessingProperties::goog_experimental_auto_gain_control};
+
 template <typename T>
 static bool Contains(const std::vector<T>& vector, T value) {
   return base::Contains(vector, value);
@@ -303,6 +313,31 @@
     }
   }
 
+  void CheckProcessingType(const AudioCaptureSettings& result) {
+    ProcessingType expected_type = ProcessingType::kUnprocessed;
+    const auto& properties = result.audio_processing_properties();
+    bool properties_value = false;
+    // Skip audio mirroring and start directly from auto gain control.
+    for (size_t i = 1; i < kAudioProcessingProperties.size(); ++i)
+      properties_value |= properties.*kAudioProcessingProperties[i];
+
+    // If goog_audio_mirroring is true but all the other properties are false,
+    // we should be expecting kProcessed, however if any of the properties was
+    // true, we should expected kApmProcessed.
+    if (properties.goog_audio_mirroring && !properties_value)
+      expected_type = ProcessingType::kNoApmProcessed;
+    else if (properties_value)
+      expected_type = ProcessingType::kApmProcessed;
+
+    // Finally, if the chosen echo cancellation type is either AEC3 or AEC2, the
+    // only possible processing type to expect is kWebRtcProcessed.
+    if (properties.echo_cancellation_type ==
+        EchoCancellationType::kEchoCancellationAec3) {
+      expected_type = ProcessingType::kApmProcessed;
+    }
+    EXPECT_EQ(result.processing_type(), expected_type);
+  }
+
   void CheckDevice(const AudioDeviceCaptureCapability& expected_device,
                    const AudioCaptureSettings& result) {
     EXPECT_EQ(expected_device.DeviceID().Utf8(), result.device_id());
@@ -319,6 +354,7 @@
       const AudioSettingsBoolMembers& exclude_main_settings,
       const AudioPropertiesBoolMembers& exclude_audio_properties,
       const AudioCaptureSettings& result) {
+    CheckProcessingType(result);
     CheckBoolDefaults(exclude_main_settings, exclude_audio_properties, result);
     CheckEchoCancellationTypeDefault(result);
     CheckDeviceDefaults(result);
@@ -576,15 +612,6 @@
     }
   }
 
-  const AudioPropertiesBoolMembers kAudioProcessingProperties = {
-      &AudioProcessingProperties::goog_audio_mirroring,
-      &AudioProcessingProperties::goog_auto_gain_control,
-      &AudioProcessingProperties::goog_experimental_echo_cancellation,
-      &AudioProcessingProperties::goog_noise_suppression,
-      &AudioProcessingProperties::goog_experimental_noise_suppression,
-      &AudioProcessingProperties::goog_highpass_filter,
-      &AudioProcessingProperties::goog_experimental_auto_gain_control};
-
   const std::vector<
       blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
       kAudioProcessingConstraints = {
@@ -1161,6 +1188,7 @@
     CheckDeviceDefaults(result);
   else
     EXPECT_EQ(kArbitraryDeviceID, result.device_id());
+  CheckProcessingType(result);
   CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
                     result);
   CheckEchoCancellationTypeDefault(result);
@@ -1173,6 +1201,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     CheckDevice(device, result);
+    CheckProcessingType(result);
     CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
                       result);
     EchoCancellationType expected_echo_cancellation_type =
@@ -1198,6 +1227,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     CheckDevice(device, result);
+    CheckProcessingType(result);
     CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
                       result);
     EchoCancellationType expected_echo_cancellation_type =
@@ -1266,6 +1296,7 @@
         EXPECT_EQ(GetMediaStreamSource() != blink::kMediaStreamSourceDesktop,
                   result.disable_local_echo());
         EXPECT_FALSE(result.render_to_associated_sink());
+        CheckProcessingType(result);
         if (IsDeviceCapture()) {
           CheckDevice(*default_device_, result);
         } else {
@@ -1324,6 +1355,7 @@
         EXPECT_EQ(GetMediaStreamSource() != blink::kMediaStreamSourceDesktop,
                   result.disable_local_echo());
         EXPECT_FALSE(result.render_to_associated_sink());
+        CheckProcessingType(result);
         CheckDevice(*system_echo_canceller_device_, result);
       }
     }
@@ -1358,6 +1390,7 @@
                   : EchoCancellationType::kEchoCancellationDisabled;
         EXPECT_EQ(expected_echo_cancellation_type,
                   properties.echo_cancellation_type);
+        CheckProcessingType(result);
         CheckBoolDefaults(AudioSettingsBoolMembers(),
                           AudioPropertiesBoolMembers(), result);
         if (IsDeviceCapture()) {
@@ -1405,6 +1438,7 @@
                   : EchoCancellationType::kEchoCancellationDisabled;
         EXPECT_EQ(expected_echo_cancellation_type,
                   properties.echo_cancellation_type);
+        CheckProcessingType(result);
         CheckBoolDefaults(AudioSettingsBoolMembers(),
                           AudioPropertiesBoolMembers(), result);
         CheckDevice(*system_echo_canceller_device_, result);
@@ -1430,14 +1464,6 @@
 // default value set by the echoCancellation constraint.
 TEST_P(MediaStreamConstraintsUtilAudioTest,
        EchoCancellationAndSingleBoolConstraint) {
-  const AudioPropertiesBoolMembers kAudioProcessingProperties = {
-      &AudioProcessingProperties::goog_audio_mirroring,
-      &AudioProcessingProperties::goog_auto_gain_control,
-      &AudioProcessingProperties::goog_experimental_echo_cancellation,
-      &AudioProcessingProperties::goog_noise_suppression,
-      &AudioProcessingProperties::goog_experimental_noise_suppression,
-      &AudioProcessingProperties::goog_highpass_filter,
-      &AudioProcessingProperties::goog_experimental_auto_gain_control};
 
   const std::vector<
       blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
@@ -1473,6 +1499,7 @@
          set_function)(true);
         auto result = SelectSettings();
         EXPECT_TRUE(result.HasValue());
+        CheckProcessingType(result);
         EXPECT_EQ(EchoCancellationType::kEchoCancellationDisabled,
                   result.audio_processing_properties().echo_cancellation_type);
         EXPECT_TRUE(result.audio_processing_properties().*
@@ -1531,6 +1558,7 @@
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
   CheckDeviceDefaults(result);
+  CheckProcessingType(result);
   CheckBoolDefaults({&AudioCaptureSettings::render_to_associated_sink},
                     {&AudioProcessingProperties::goog_audio_mirroring}, result);
   CheckEchoCancellationTypeDefault(result);
@@ -1549,6 +1577,7 @@
   constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
+  CheckProcessingType(result);
   CheckDeviceDefaults(result);
   CheckBoolDefaults({},
                     {&AudioProcessingProperties::goog_audio_mirroring,
@@ -1566,6 +1595,7 @@
   constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
+  CheckProcessingType(result);
   CheckDeviceDefaults(result);
   CheckBoolDefaults({},
                     {&AudioProcessingProperties::goog_audio_mirroring,
@@ -1695,15 +1725,6 @@
             &blink::WebMediaTrackConstraintSet::
                 goog_experimental_auto_gain_control,
         };
-    const AudioPropertiesBoolMembers kAudioProcessingProperties = {
-        &AudioProcessingProperties::goog_audio_mirroring,
-        &AudioProcessingProperties::goog_auto_gain_control,
-        &AudioProcessingProperties::goog_experimental_echo_cancellation,
-        &AudioProcessingProperties::goog_noise_suppression,
-        &AudioProcessingProperties::goog_experimental_noise_suppression,
-        &AudioProcessingProperties::goog_highpass_filter,
-        &AudioProcessingProperties::goog_experimental_auto_gain_control};
-
     ASSERT_EQ(kAudioProcessingConstraints.size(),
               kAudioProcessingProperties.size());
 
diff --git a/third_party/blink/renderer/modules/mediastream/remote_media_stream_track_adapter.cc b/third_party/blink/renderer/modules/mediastream/remote_media_stream_track_adapter.cc
index fc133f77..9086130 100644
--- a/third_party/blink/renderer/modules/mediastream/remote_media_stream_track_adapter.cc
+++ b/third_party/blink/renderer/modules/mediastream/remote_media_stream_track_adapter.cc
@@ -9,7 +9,7 @@
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/webrtc/track_observer.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
-#include "third_party/blink/public/web/modules/peerconnection/media_stream_remote_video_source.h"
+#include "third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 0cb0b74..b319564 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -497,6 +497,7 @@
           "websockets/close_event.idl",
           "websockets/websocket.idl",
           "websockets/websocket_stream.idl",
+          "webtransport/quic_transport.idl",
           "webusb/usb.idl",
           "webusb/usb_alternate_interface.idl",
           "webusb/usb_configuration.idl",
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 32ce674d..dd58e136 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -53,6 +53,7 @@
     "call_setup_state_tracker.cc",
     "call_setup_state_tracker.h",
     "media_stream_remote_video_source.cc",
+    "media_stream_remote_video_source.h",
     "media_stream_video_webrtc_sink.cc",
     "peer_connection_dependency_factory.cc",
     "rtc_certificate.cc",
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
index 77da47c9..944a4751 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
@@ -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/public/web/modules/peerconnection/media_stream_remote_video_source.h"
+#include "third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.h"
 
 #include <stdint.h>
 #include <utility>
diff --git a/third_party/blink/public/web/modules/peerconnection/media_stream_remote_video_source.h b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.h
similarity index 79%
rename from third_party/blink/public/web/modules/peerconnection/media_stream_remote_video_source.h
rename to third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.h
index a0ee73b..9f53584 100644
--- a/third_party/blink/public/web/modules/peerconnection/media_stream_remote_video_source.h
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/webrtc/api/media_stream_interface.h"
 
 namespace blink {
@@ -22,10 +22,7 @@
 // class is to make sure there is no difference between a video track where the
 // source is a local source and a video track where the source is a remote video
 // track.
-//
-// TODO(crbug.com/787254): Move the classes below out of the Blink exposed
-// API when all users of it have been Onion souped.
-class BLINK_MODULES_EXPORT MediaStreamRemoteVideoSource
+class MODULES_EXPORT MediaStreamRemoteVideoSource
     : public MediaStreamVideoSource {
  public:
   explicit MediaStreamRemoteVideoSource(
@@ -62,4 +59,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_MEDIA_STREAM_REMOTE_VIDEO_SOURCE_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
index 2e9aa9dd..1125ad8 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source_test.cc
@@ -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/public/web/modules/peerconnection/media_stream_remote_video_source.h"
+#include "third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.h"
 
 #include <memory>
 #include <utility>
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc
index 729d72c..53b6a7e 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc
@@ -14,11 +14,11 @@
 #include "base/synchronization/lock.h"
 #include "base/timer/timer.h"
 #include "media/base/limits.h"
-#include "third_party/blink/public/platform/modules/peerconnection/webrtc_video_track_source.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/modules/mediastream/web_media_stream_utils.h"
 #include "third_party/blink/public/web/modules/peerconnection/peer_connection_dependency_factory.h"
+#include "third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/webrtc/api/video_track_source_proxy.h"
 
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
index 65ff7e67..6155156 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc
@@ -24,7 +24,6 @@
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/peerconnection/webrtc_ip_handling_policy.h"
-#include "third_party/blink/public/platform/modules/peerconnection/audio_codec_factory.h"
 #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
@@ -45,6 +44,7 @@
 #include "third_party/blink/renderer/platform/p2p/ipc_socket_factory.h"
 #include "third_party/blink/renderer/platform/p2p/mdns_responder_adapter.h"
 #include "third_party/blink/renderer/platform/p2p/socket_dispatcher.h"
+#include "third_party/blink/renderer/platform/peerconnection/audio_codec_factory.h"
 #include "third_party/blink/renderer/platform/peerconnection/stun_field_trial.h"
 #include "third_party/blink/renderer/platform/peerconnection/video_codec_factory.h"
 #include "third_party/webrtc/api/call/call_factory_interface.h"
diff --git a/third_party/blink/renderer/modules/wake_lock/BUILD.gn b/third_party/blink/renderer/modules/wake_lock/BUILD.gn
index e77edec..1a36840 100644
--- a/third_party/blink/renderer/modules/wake_lock/BUILD.gn
+++ b/third_party/blink/renderer/modules/wake_lock/BUILD.gn
@@ -10,10 +10,10 @@
     "navigator_wake_lock.h",
     "wake_lock.cc",
     "wake_lock.h",
+    "wake_lock_manager.cc",
+    "wake_lock_manager.h",
     "wake_lock_sentinel.cc",
     "wake_lock_sentinel.h",
-    "wake_lock_state_record.cc",
-    "wake_lock_state_record.h",
     "wake_lock_type.cc",
     "wake_lock_type.h",
     "worker_navigator_wake_lock.cc",
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
index 4d6da59..652e18b6 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.cc
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/modules/permissions/permission_utils.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_type.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -26,20 +26,18 @@
 WakeLock::WakeLock(Document& document)
     : ContextLifecycleObserver(&document),
       PageVisibilityObserver(document.GetPage()),
-      state_records_{
-          MakeGarbageCollected<WakeLockStateRecord>(&document,
-                                                    WakeLockType::kScreen),
-          MakeGarbageCollected<WakeLockStateRecord>(&document,
-                                                    WakeLockType::kSystem)} {}
+      managers_{MakeGarbageCollected<WakeLockManager>(&document,
+                                                      WakeLockType::kScreen),
+                MakeGarbageCollected<WakeLockManager>(&document,
+                                                      WakeLockType::kSystem)} {}
 
 WakeLock::WakeLock(DedicatedWorkerGlobalScope& worker_scope)
     : ContextLifecycleObserver(&worker_scope),
       PageVisibilityObserver(nullptr),
-      state_records_{
-          MakeGarbageCollected<WakeLockStateRecord>(&worker_scope,
-                                                    WakeLockType::kScreen),
-          MakeGarbageCollected<WakeLockStateRecord>(&worker_scope,
-                                                    WakeLockType::kSystem)} {}
+      managers_{MakeGarbageCollected<WakeLockManager>(&worker_scope,
+                                                      WakeLockType::kScreen),
+                MakeGarbageCollected<WakeLockManager>(&worker_scope,
+                                                      WakeLockType::kSystem)} {}
 
 ScriptPromise WakeLock::request(ScriptState* script_state, const String& type) {
   // https://w3c.github.io/wake-lock/#request-static-method
@@ -161,7 +159,7 @@
     return;
   }
   // https://github.com/w3c/wake-lock/issues/222: the page can become hidden
-  // between request() and WakeLockStateRecord::AcquireWakeLock(), in which case
+  // between request() and WakeLockManager::AcquireWakeLock(), in which case
   // we need to abort early.
   if (type == WakeLockType::kScreen &&
       !(GetPage() && GetPage()->IsPageVisible())) {
@@ -174,9 +172,9 @@
   // and type:
   // 6.2.1. If success is false then reject promise with a "NotAllowedError"
   //        DOMException, and abort these steps.
-  WakeLockStateRecord* state_record = state_records_[static_cast<size_t>(type)];
-  DCHECK(state_record);
-  state_record->AcquireWakeLock(resolver);
+  WakeLockManager* manager = managers_[static_cast<size_t>(type)];
+  DCHECK(manager);
+  manager->AcquireWakeLock(resolver);
 }
 
 void WakeLock::ContextDestroyed(ExecutionContext*) {
@@ -184,15 +182,15 @@
   // 1. Let document be the responsible document of the current settings object.
   // 2. Let screenRecord be the platform wake lock's state record associated
   // with document and wake lock type "screen".
-  // 3. For each lockPromise in screenRecord.[[WakeLockStateRecord]]:
+  // 3. For each lockPromise in screenRecord.[[ActiveLocks]]:
   // 3.1. Run release a wake lock with lockPromise and "screen".
   // 4. Let systemRecord be the platform wake lock's state record associated
   // with document and wake lock type "system".
-  // 5. For each lockPromise in systemRecord.[[WakeLockStateRecord]]:
+  // 5. For each lockPromise in systemRecord.[[ActiveLocks]]:
   // 5.1. Run release a wake lock with lockPromise and "system".
-  for (WakeLockStateRecord* state_record : state_records_) {
-    if (state_record)
-      state_record->ClearWakeLocks();
+  for (WakeLockManager* manager : managers_) {
+    if (manager)
+      manager->ClearWakeLocks();
   }
 }
 
@@ -204,12 +202,12 @@
     return;
   // 3. Let screenRecord be the platform wake lock's state record associated
   // with wake lock type "screen".
-  // 4. For each lockPromise in screenRecord.[[WakeLockStateRecord]]:
+  // 4. For each lockPromise in screenRecord.[[ActiveLocks]]:
   // 4.1. Run release a wake lock with lockPromise and "screen".
-  WakeLockStateRecord* state_record =
-      state_records_[static_cast<size_t>(WakeLockType::kScreen)];
-  if (state_record)
-    state_record->ClearWakeLocks();
+  WakeLockManager* manager =
+      managers_[static_cast<size_t>(WakeLockType::kScreen)];
+  if (manager)
+    manager->ClearWakeLocks();
 }
 
 void WakeLock::ObtainPermission(
@@ -258,8 +256,8 @@
 }
 
 void WakeLock::Trace(Visitor* visitor) {
-  for (WakeLockStateRecord* state_record : state_records_)
-    visitor->Trace(state_record);
+  for (WakeLockManager* manager : managers_)
+    visitor->Trace(manager);
   PageVisibilityObserver::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock.h b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
index a22e5e2..42874c4 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock.h
@@ -28,7 +28,7 @@
 
 class ExecutionContext;
 class ScriptState;
-class WakeLockStateRecord;
+class WakeLockManager;
 
 class MODULES_EXPORT WakeLock final : public ScriptWrappable,
                                       public ContextLifecycleObserver,
@@ -71,7 +71,7 @@
   // https://w3c.github.io/wake-lock/#concepts-and-state-record
   // Each platform wake lock (one per wake lock type) has an associated state
   // record per responsible document [...] internal slots.
-  Member<WakeLockStateRecord> state_records_[kWakeLockTypeCount];
+  Member<WakeLockManager> managers_[kWakeLockTypeCount];
 
   FRIEND_TEST_ALL_PREFIXES(WakeLockTest, RequestWakeLockGranted);
   FRIEND_TEST_ALL_PREFIXES(WakeLockTest, RequestWakeLockDenied);
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.cc
similarity index 80%
rename from third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
rename to third_party/blink/renderer/modules/wake_lock/wake_lock_manager.cc
index c5194fb2de..dade239 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_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/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h"
 
 #include "base/logging.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
@@ -15,13 +15,13 @@
 
 namespace blink {
 
-WakeLockStateRecord::WakeLockStateRecord(ExecutionContext* execution_context,
-                                         WakeLockType type)
+WakeLockManager::WakeLockManager(ExecutionContext* execution_context,
+                                 WakeLockType type)
     : wake_lock_type_(type), execution_context_(execution_context) {
   DCHECK_NE(execution_context, nullptr);
 }
 
-void WakeLockStateRecord::AcquireWakeLock(ScriptPromiseResolver* resolver) {
+void WakeLockManager::AcquireWakeLock(ScriptPromiseResolver* resolver) {
   // https://w3c.github.io/wake-lock/#acquire-wake-lock-algorithm
   // 1. If the wake lock for type type is not applicable, return false.
   // 2. Set active to true if the platform wake lock has an active wake lock for
@@ -34,7 +34,7 @@
   //      object.
   // 4.2. Let record be the platform wake lock's state record associated with
   //      document and type.
-  // 4.3. Add lockPromise to record.[[WakeLockStateRecord]].
+  // 4.3. Add lockPromise to record.[[ActiveLocks]].
   // 5. Return active.
   if (!wake_lock_) {
     mojo::Remote<mojom::blink::WakeLockService> wake_lock_service;
@@ -45,9 +45,8 @@
                                    device::mojom::blink::WakeLockReason::kOther,
                                    "Blink Wake Lock",
                                    wake_lock_.BindNewPipeAndPassReceiver());
-    wake_lock_.set_disconnect_handler(
-        WTF::Bind(&WakeLockStateRecord::OnWakeLockConnectionError,
-                  WrapWeakPersistent(this)));
+    wake_lock_.set_disconnect_handler(WTF::Bind(
+        &WakeLockManager::OnWakeLockConnectionError, WrapWeakPersistent(this)));
     wake_lock_->RequestWakeLock();
   }
   auto* sentinel = MakeGarbageCollected<WakeLockSentinel>(
@@ -56,7 +55,7 @@
   resolver->Resolve(sentinel);
 }
 
-void WakeLockStateRecord::UnregisterSentinel(WakeLockSentinel* sentinel) {
+void WakeLockManager::UnregisterSentinel(WakeLockSentinel* sentinel) {
   auto iterator = wake_lock_sentinels_.find(sentinel);
   DCHECK(iterator != wake_lock_sentinels_.end());
   wake_lock_sentinels_.erase(iterator);
@@ -67,17 +66,17 @@
   }
 }
 
-void WakeLockStateRecord::ClearWakeLocks() {
+void WakeLockManager::ClearWakeLocks() {
   while (!wake_lock_sentinels_.IsEmpty())
     (*wake_lock_sentinels_.begin())->DoRelease();
 }
 
-void WakeLockStateRecord::OnWakeLockConnectionError() {
+void WakeLockManager::OnWakeLockConnectionError() {
   wake_lock_.reset();
   ClearWakeLocks();
 }
 
-void WakeLockStateRecord::Trace(blink::Visitor* visitor) {
+void WakeLockManager::Trace(blink::Visitor* visitor) {
   visitor->Trace(execution_context_);
   visitor->Trace(wake_lock_sentinels_);
 }
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h
similarity index 69%
rename from third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h
rename to third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h
index c40a504..32efa02 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_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_WAKE_LOCK_WAKE_LOCK_STATE_RECORD_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_STATE_RECORD_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_MANAGER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_MANAGER_H_
 
 #include "base/gtest_prod_util.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -20,10 +20,10 @@
 
 // https://w3c.github.io/wake-lock/#concepts-and-state-record
 // Per-document and per-wake lock type internal data.
-class MODULES_EXPORT WakeLockStateRecord final
-    : public GarbageCollected<WakeLockStateRecord> {
+class MODULES_EXPORT WakeLockManager final
+    : public GarbageCollected<WakeLockManager> {
  public:
-  WakeLockStateRecord(ExecutionContext*, WakeLockType);
+  WakeLockManager(ExecutionContext*, WakeLockType);
 
   void AcquireWakeLock(ScriptPromiseResolver*);
   void ClearWakeLocks();
@@ -48,13 +48,13 @@
   // ExecutionContext from which we will connect to |wake_lock_service_|.
   Member<ExecutionContext> execution_context_;
 
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, AcquireWakeLock);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ReleaseAllWakeLocks);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ReleaseOneWakeLock);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, ClearWakeLocks);
-  FRIEND_TEST_ALL_PREFIXES(WakeLockStateRecordTest, WakeLockConnectionError);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, AcquireWakeLock);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, ReleaseAllWakeLocks);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, ReleaseOneWakeLock);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, ClearWakeLocks);
+  FRIEND_TEST_ALL_PREFIXES(WakeLockManagerTest, WakeLockConnectionError);
 };
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_STATE_RECORD_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WAKE_LOCK_WAKE_LOCK_MANAGER_H_
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager_test.cc
similarity index 65%
rename from third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
rename to third_party/blink/renderer/modules/wake_lock/wake_lock_manager_test.cc
index ed2828c..d3ebd99 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_manager_test.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/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -18,22 +18,22 @@
 
 namespace {
 
-WakeLockStateRecord* MakeStateRecord(WakeLockTestingContext& context,
-                                     WakeLockType type) {
-  return MakeGarbageCollected<WakeLockStateRecord>(context.GetDocument(), type);
+WakeLockManager* MakeManager(WakeLockTestingContext& context,
+                             WakeLockType type) {
+  return MakeGarbageCollected<WakeLockManager>(context.GetDocument(), type);
 }
 
 }  // namespace
 
-TEST(WakeLockStateRecordTest, AcquireWakeLock) {
+TEST(WakeLockManagerTest, AcquireWakeLock) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kScreen);
+  auto* manager = MakeManager(context, WakeLockType::kScreen);
 
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
   EXPECT_FALSE(screen_lock.is_acquired());
-  EXPECT_FALSE(state_record->wake_lock_.is_bound());
+  EXPECT_FALSE(manager->wake_lock_.is_bound());
 
   auto* resolver1 =
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
@@ -42,8 +42,8 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise2 = resolver2->Promise();
 
-  state_record->AcquireWakeLock(resolver1);
-  state_record->AcquireWakeLock(resolver2);
+  manager->AcquireWakeLock(resolver1);
+  manager->AcquireWakeLock(resolver2);
   screen_lock.WaitForRequest();
 
   context.WaitForPromiseFulfillment(promise1);
@@ -54,17 +54,17 @@
   auto* sentinel2 =
       ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise2);
 
-  EXPECT_TRUE(state_record->wake_lock_sentinels_.Contains(sentinel1));
-  EXPECT_TRUE(state_record->wake_lock_sentinels_.Contains(sentinel2));
-  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
+  EXPECT_TRUE(manager->wake_lock_sentinels_.Contains(sentinel1));
+  EXPECT_TRUE(manager->wake_lock_sentinels_.Contains(sentinel2));
+  EXPECT_EQ(2U, manager->wake_lock_sentinels_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
-  EXPECT_TRUE(state_record->wake_lock_.is_bound());
+  EXPECT_TRUE(manager->wake_lock_.is_bound());
 }
 
-TEST(WakeLockStateRecordTest, ReleaseAllWakeLocks) {
+TEST(WakeLockManagerTest, ReleaseAllWakeLocks) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kScreen);
+  auto* manager = MakeManager(context, WakeLockType::kScreen);
 
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
@@ -73,28 +73,28 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise = resolver->Promise();
 
-  state_record->AcquireWakeLock(resolver);
+  manager->AcquireWakeLock(resolver);
   screen_lock.WaitForRequest();
   context.WaitForPromiseFulfillment(promise);
 
-  EXPECT_EQ(1U, state_record->wake_lock_sentinels_.size());
+  EXPECT_EQ(1U, manager->wake_lock_sentinels_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
 
   auto* sentinel =
       ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise);
 
-  state_record->UnregisterSentinel(sentinel);
+  manager->UnregisterSentinel(sentinel);
   screen_lock.WaitForCancelation();
 
-  EXPECT_EQ(0U, state_record->wake_lock_sentinels_.size());
+  EXPECT_EQ(0U, manager->wake_lock_sentinels_.size());
   EXPECT_FALSE(screen_lock.is_acquired());
-  EXPECT_FALSE(state_record->wake_lock_.is_bound());
+  EXPECT_FALSE(manager->wake_lock_.is_bound());
 }
 
-TEST(WakeLockStateRecordTest, ReleaseOneWakeLock) {
+TEST(WakeLockManagerTest, ReleaseOneWakeLock) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kScreen);
+  auto* manager = MakeManager(context, WakeLockType::kScreen);
 
   MockWakeLock& screen_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kScreen);
@@ -106,46 +106,46 @@
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise2 = resolver2->Promise();
 
-  state_record->AcquireWakeLock(resolver1);
-  state_record->AcquireWakeLock(resolver2);
+  manager->AcquireWakeLock(resolver1);
+  manager->AcquireWakeLock(resolver2);
   screen_lock.WaitForRequest();
 
   context.WaitForPromiseFulfillment(promise1);
   context.WaitForPromiseFulfillment(promise2);
 
   EXPECT_TRUE(screen_lock.is_acquired());
-  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
+  EXPECT_EQ(2U, manager->wake_lock_sentinels_.size());
 
   auto* sentinel1 =
       ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise1);
-  EXPECT_TRUE(state_record->wake_lock_sentinels_.Contains(sentinel1));
+  EXPECT_TRUE(manager->wake_lock_sentinels_.Contains(sentinel1));
 
-  state_record->UnregisterSentinel(sentinel1);
-  EXPECT_FALSE(state_record->wake_lock_sentinels_.Contains(sentinel1));
-  EXPECT_TRUE(state_record->wake_lock_.is_bound());
-  EXPECT_EQ(1U, state_record->wake_lock_sentinels_.size());
+  manager->UnregisterSentinel(sentinel1);
+  EXPECT_FALSE(manager->wake_lock_sentinels_.Contains(sentinel1));
+  EXPECT_TRUE(manager->wake_lock_.is_bound());
+  EXPECT_EQ(1U, manager->wake_lock_sentinels_.size());
   EXPECT_TRUE(screen_lock.is_acquired());
 }
 
-TEST(WakeLockStateRecordTest, ClearEmptyWakeLockSentinelList) {
+TEST(WakeLockManagerTest, ClearEmptyWakeLockSentinelList) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kSystem);
+  auto* manager = MakeManager(context, WakeLockType::kSystem);
 
   MockWakeLock& system_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kSystem);
   EXPECT_FALSE(system_lock.is_acquired());
 
-  state_record->ClearWakeLocks();
+  manager->ClearWakeLocks();
   test::RunPendingTasks();
 
   EXPECT_FALSE(system_lock.is_acquired());
 }
 
-TEST(WakeLockStateRecordTest, ClearWakeLocks) {
+TEST(WakeLockManagerTest, ClearWakeLocks) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kSystem);
+  auto* manager = MakeManager(context, WakeLockType::kSystem);
 
   auto* resolver1 =
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
@@ -157,25 +157,25 @@
   MockWakeLock& system_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kSystem);
 
-  state_record->AcquireWakeLock(resolver1);
-  state_record->AcquireWakeLock(resolver2);
+  manager->AcquireWakeLock(resolver1);
+  manager->AcquireWakeLock(resolver2);
   system_lock.WaitForRequest();
   context.WaitForPromiseFulfillment(promise1);
   context.WaitForPromiseFulfillment(promise2);
 
-  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
+  EXPECT_EQ(2U, manager->wake_lock_sentinels_.size());
 
-  state_record->ClearWakeLocks();
+  manager->ClearWakeLocks();
   system_lock.WaitForCancelation();
 
-  EXPECT_EQ(0U, state_record->wake_lock_sentinels_.size());
+  EXPECT_EQ(0U, manager->wake_lock_sentinels_.size());
   EXPECT_FALSE(system_lock.is_acquired());
 }
 
-TEST(WakeLockStateRecordTest, WakeLockConnectionError) {
+TEST(WakeLockManagerTest, WakeLockConnectionError) {
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
-  auto* state_record = MakeStateRecord(context, WakeLockType::kSystem);
+  auto* manager = MakeManager(context, WakeLockType::kSystem);
 
   auto* resolver1 =
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
@@ -187,21 +187,21 @@
   MockWakeLock& system_lock =
       wake_lock_service.get_wake_lock(WakeLockType::kSystem);
 
-  state_record->AcquireWakeLock(resolver1);
-  state_record->AcquireWakeLock(resolver2);
+  manager->AcquireWakeLock(resolver1);
+  manager->AcquireWakeLock(resolver2);
   system_lock.WaitForRequest();
   context.WaitForPromiseFulfillment(promise1);
   context.WaitForPromiseFulfillment(promise2);
 
-  EXPECT_EQ(2U, state_record->wake_lock_sentinels_.size());
+  EXPECT_EQ(2U, manager->wake_lock_sentinels_.size());
 
   // Unbind and wait for the disconnection to reach |wake_lock_|'s
   // disconnection handler.
   system_lock.Unbind();
-  state_record->wake_lock_.FlushForTesting();
+  manager->wake_lock_.FlushForTesting();
 
-  EXPECT_EQ(0U, state_record->wake_lock_sentinels_.size());
-  EXPECT_FALSE(state_record->wake_lock_);
+  EXPECT_EQ(0U, manager->wake_lock_sentinels_.size());
+  EXPECT_FALSE(manager->wake_lock_);
   EXPECT_FALSE(system_lock.is_acquired());
 }
 
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc
index cbdc8be..7c9de8e 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.cc
@@ -8,14 +8,14 @@
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/event_target_modules_names.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
 
 WakeLockSentinel::WakeLockSentinel(ScriptState* script_state,
                                    WakeLockType type,
-                                   WakeLockStateRecord* manager)
+                                   WakeLockManager* manager)
     : ContextLifecycleObserver(ExecutionContext::From(script_state)),
       manager_(manager),
       type_(type) {}
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h
index 30d954a..1acbfb4d 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel.h
@@ -19,7 +19,7 @@
 
 class ExecutionContext;
 class ScriptState;
-class WakeLockStateRecord;
+class WakeLockManager;
 
 class MODULES_EXPORT WakeLockSentinel final
     : public EventTargetWithInlineData,
@@ -31,7 +31,7 @@
  public:
   WakeLockSentinel(ScriptState* script_state,
                    WakeLockType type,
-                   WakeLockStateRecord* manager);
+                   WakeLockManager* manager);
   ~WakeLockSentinel() override;
 
   // Web-exposed interfaces
@@ -51,7 +51,7 @@
   void ContextDestroyed(ExecutionContext*) override;
 
  private:
-  friend class WakeLockStateRecord;
+  friend class WakeLockManager;
 
   // This function, which only has any effect once, detaches this sentinel from
   // its |manager_|, and fires a "release" event.
@@ -61,7 +61,7 @@
   // where |script_state_|'s context is no longer valid.
   void DoRelease();
 
-  Member<WakeLockStateRecord> manager_;
+  Member<WakeLockManager> manager_;
   const WakeLockType type_;
 
   FRIEND_TEST_ALL_PREFIXES(WakeLockSentinelTest, MultipleReleaseCalls);
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
index 70cf9dd5..370f6e3 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_sentinel_test.cc
@@ -12,7 +12,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/modules/event_target_modules_names.h"
-#include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
+#include "third_party/blink/renderer/modules/wake_lock/wake_lock_manager.h"
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "v8/include/v8.h"
@@ -53,12 +53,12 @@
   MockWakeLockService wake_lock_service;
   WakeLockTestingContext context(&wake_lock_service);
 
-  auto* state_record = MakeGarbageCollected<WakeLockStateRecord>(
-      context.GetDocument(), WakeLockType::kScreen);
+  auto* manager = MakeGarbageCollected<WakeLockManager>(context.GetDocument(),
+                                                        WakeLockType::kScreen);
   auto* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(context.GetScriptState());
   ScriptPromise promise = resolver->Promise();
-  state_record->AcquireWakeLock(resolver);
+  manager->AcquireWakeLock(resolver);
   context.WaitForPromiseFulfillment(promise);
   auto* sentinel =
       ScriptPromiseUtils::GetPromiseResolutionAsWakeLockSentinel(promise);
@@ -87,8 +87,8 @@
 
   auto* sentinel = MakeGarbageCollected<WakeLockSentinel>(
       context.GetScriptState(), WakeLockType::kScreen,
-      MakeGarbageCollected<WakeLockStateRecord>(context.GetDocument(),
-                                                WakeLockType::kScreen));
+      MakeGarbageCollected<WakeLockManager>(context.GetDocument(),
+                                            WakeLockType::kScreen));
 
   auto* event_listener =
       MakeGarbageCollected<SyncEventListener>(WTF::Bind([]() {
diff --git a/third_party/blink/renderer/modules/webtransport/BUILD.gn b/third_party/blink/renderer/modules/webtransport/BUILD.gn
new file mode 100644
index 0000000..1e3507c
--- /dev/null
+++ b/third_party/blink/renderer/modules/webtransport/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2019 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("//third_party/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("webtransport") {
+  sources = [
+    "quic_transport.cc",
+    "quic_transport.h",
+  ]
+}
+
+jumbo_source_set("unit_tests") {
+  testonly = true
+  sources = []
+
+  configs += [
+    "//third_party/blink/renderer:config",
+    "//third_party/blink/renderer:inside_blink",
+    "//third_party/blink/renderer/core:blink_core_pch",
+  ]
+
+  deps = [
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/blink/renderer/modules",
+    "//third_party/blink/renderer/platform",
+    "//third_party/blink/renderer/platform/wtf",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/webtransport/OWNERS b/third_party/blink/renderer/modules/webtransport/OWNERS
new file mode 100644
index 0000000..e57af91f
--- /dev/null
+++ b/third_party/blink/renderer/modules/webtransport/OWNERS
@@ -0,0 +1,5 @@
+ricea@chromium.org
+yhirano@chromium.org
+
+# TEAM: blink-network-dev@chromium.org
+# COMPONENT: Blink>Network>WebTransport
diff --git a/third_party/blink/renderer/modules/webtransport/README.md b/third_party/blink/renderer/modules/webtransport/README.md
new file mode 100644
index 0000000..4014d4a
--- /dev/null
+++ b/third_party/blink/renderer/modules/webtransport/README.md
@@ -0,0 +1,8 @@
+# WebTransport API
+
+This is a directory for [Web Transport](https://wicg.github.io/web-transport/)
+implementation.
+
+## Design docs
+
+ - [QuicTransport](https://docs.google.com/document/d/1UgviRBnZkMUq4OKcsAJvIQFX6UCXeCbOtX_wMgwD_es/edit)
diff --git a/third_party/blink/renderer/modules/webtransport/quic_transport.cc b/third_party/blink/renderer/modules/webtransport/quic_transport.cc
new file mode 100644
index 0000000..b9403846
--- /dev/null
+++ b/third_party/blink/renderer/modules/webtransport/quic_transport.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webtransport/quic_transport.h"
+
+namespace blink {
+
+QuicTransport::QuicTransport(ScriptState* script_state, const String& url) {}
+
+void QuicTransport::Dispose() {}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webtransport/quic_transport.h b/third_party/blink/renderer/modules/webtransport/quic_transport.h
new file mode 100644
index 0000000..c9262d6
--- /dev/null
+++ b/third_party/blink/renderer/modules/webtransport/quic_transport.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBTRANSPORT_QUIC_TRANSPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBTRANSPORT_QUIC_TRANSPORT_H_
+
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class ScriptState;
+
+class MODULES_EXPORT QuicTransport final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_PRE_FINALIZER(QuicTransport, Dispose);
+
+ public:
+  static QuicTransport* Create(ScriptState* script_state, const String& url) {
+    return MakeGarbageCollected<QuicTransport>(script_state, url);
+  }
+
+  QuicTransport(ScriptState*, const String& url);
+  ~QuicTransport() override = default;
+
+ private:
+  void Dispose();
+};
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/blink/renderer/modules/webtransport/quic_transport.idl b/third_party/blink/renderer/modules/webtransport/quic_transport.idl
new file mode 100644
index 0000000..469fc482
--- /dev/null
+++ b/third_party/blink/renderer/modules/webtransport/quic_transport.idl
@@ -0,0 +1,16 @@
+// Copyright 2019 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://wicg.github.io/web-transport/#quic-transport
+[
+    Constructor(USVString url),
+    Exposed=(Window, Worker),
+    RuntimeEnabled=QuicTransport,
+    ConstructorCallWith=ScriptState
+] interface QuicTransport {
+  // QuicTransport is the first, and at this moment only, transport which is
+  // implemented. In the (draft) spec there are many mix-in interfaces which
+  // QuicTransport includes, but we define all their methods/attributes here
+  // for simplicity.
+};
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 1f06f078..955f89e 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1255,6 +1255,7 @@
     "p2p/socket_dispatcher.cc",
     "p2p/socket_dispatcher.h",
     "peerconnection/audio_codec_factory.cc",
+    "peerconnection/audio_codec_factory.h",
     "peerconnection/rtc_answer_options_platform.h",
     "peerconnection/rtc_dtmf_sender_handler.cc",
     "peerconnection/rtc_dtmf_sender_handler.h",
@@ -1285,6 +1286,7 @@
     "peerconnection/video_codec_factory.h",
     "peerconnection/webrtc_audio_sink.cc",
     "peerconnection/webrtc_video_track_source.cc",
+    "peerconnection/webrtc_video_track_source.h",
     "prerender.cc",
     "prerender.h",
     "prerender_client.h",
@@ -2115,6 +2117,19 @@
   ]
 }
 
+# Fuzzer for blink::DateTimeFormat.
+fuzzer_test("blink_date_time_format_fuzzer") {
+  sources = [
+    "text/date_time_format_fuzzer.cc",
+  ]
+  deps = [
+    ":blink_fuzzer_test_support",
+    ":platform",
+  ]
+  dict = "//testing/libfuzzer/fuzzers/dicts/date.dict"
+  seed_corpus = "text/date_time_format_fuzzer_seed_corpus"
+}
+
 # Fuzzer for blink::JSONParser.
 fuzzer_test("blink_json_parser_fuzzer") {
   sources = [
diff --git a/third_party/blink/renderer/platform/heap/finalizer_traits.h b/third_party/blink/renderer/platform/heap/finalizer_traits.h
index d96e356..03ff1d9 100644
--- a/third_party/blink/renderer/platform/heap/finalizer_traits.h
+++ b/third_party/blink/renderer/platform/heap/finalizer_traits.h
@@ -38,18 +38,27 @@
 struct FinalizerTraitImpl<T, true> {
  private:
   STATIC_ONLY(FinalizerTraitImpl);
-  struct CustomDispatch {
+  struct Custom {
     static void Call(void* obj) {
       static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
     }
   };
-  struct DestructorDispatch {
-    static void Call(void* obj) { static_cast<T*>(obj)->~T(); }
+  struct Destructor {
+    static void Call(void* obj) {
+// The garbage collector differs from regular C++ here as it remembers whether
+// an object's base class has a virtual destructor. In case there is no virtual
+// destructor present, the object is always finalized through its leaf type. In
+// other words: there is no finalization through a base pointer.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdelete-non-abstract-non-virtual-dtor"
+      static_cast<T*>(obj)->~T();
+#pragma GCC diagnostic pop
+    }
   };
   using FinalizeImpl =
       std::conditional_t<HasFinalizeGarbageCollectedObject<T>::value,
-                         CustomDispatch,
-                         DestructorDispatch>;
+                         Custom,
+                         Destructor>;
 
  public:
   static void Finalize(void* obj) {
diff --git a/third_party/blink/renderer/platform/heap/gc_info.h b/third_party/blink/renderer/platform/heap/gc_info.h
index fa960e46..36b53cc 100644
--- a/third_party/blink/renderer/platform/heap/gc_info.h
+++ b/third_party/blink/renderer/platform/heap/gc_info.h
@@ -93,11 +93,9 @@
   Mutex table_mutex_;
 };
 
-// GCInfoAtBaseType should be used when returning a unique 14 bit integer
-// for a given gcInfo.
 template <typename T>
-struct GCInfoAtBaseType {
-  STATIC_ONLY(GCInfoAtBaseType);
+struct GCInfoTrait {
+  STATIC_ONLY(GCInfoTrait);
   static uint32_t Index() {
     static_assert(sizeof(T), "T must be fully defined");
     static const GCInfo kGcInfo = {
@@ -117,31 +115,6 @@
   }
 };
 
-template <typename T,
-          bool = WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type,
-                                           GarbageCollected>::value>
-struct GetGarbageCollectedType;
-
-template <typename T>
-struct GetGarbageCollectedType<T, true> {
-  STATIC_ONLY(GetGarbageCollectedType);
-  using type = typename T::GarbageCollectedType;
-};
-
-template <typename T>
-struct GetGarbageCollectedType<T, false> {
-  STATIC_ONLY(GetGarbageCollectedType);
-  using type = T;
-};
-
-template <typename T>
-struct GCInfoTrait {
-  STATIC_ONLY(GCInfoTrait);
-  static uint32_t Index() {
-    return GCInfoAtBaseType<typename GetGarbageCollectedType<T>::type>::Index();
-  }
-};
-
 template <typename U>
 class GCInfoTrait<const U> : public GCInfoTrait<U> {};
 
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index af6f4b3..02abf6859 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -585,7 +585,8 @@
       reinterpret_cast<v8::EmbedderHeapTracer*>(
           thread_state_->unified_heap_controller());
   while (v8_references.Pop(&reference)) {
-    controller->RegisterEmbedderReference(reference->Get());
+    controller->RegisterEmbedderReference(
+        reference->template Cast<v8::Data>().Get());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index e5a7316a..fd05b97 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -518,22 +518,48 @@
 #endif
 
  public:
-  using GarbageCollectedType = T;
+  using ParentMostGarbageCollectedType = T;
 
   void* operator new(size_t size) = delete;  // Must use MakeGarbageCollected.
 
+  template <typename Derived>
   static void* AllocateObject(size_t size) {
     if (IsGarbageCollectedMixin<T>::value) {
       // Ban large mixin so we can use PageFromObject() on them.
       CHECK_GE(kLargeObjectSizeThreshold, size)
           << "GarbageCollectedMixin may not be a large object";
     }
-    return ThreadHeap::Allocate<T>(size);
+    return ThreadHeap::Allocate<GCInfoFoldedType<Derived>>(size);
   }
 
   void operator delete(void* p) { NOTREACHED(); }
 
  protected:
+  // This trait in theory can be moved to gc_info.h, but that would cause
+  // significant memory bloat caused by huge number of ThreadHeap::Allocate<>
+  // instantiations, which linker is not able to fold.
+  template <typename Derived>
+  class GCInfoFolded {
+    static constexpr bool is_virtual_destructor_at_base =
+        std::has_virtual_destructor<ParentMostGarbageCollectedType>::value;
+    static constexpr bool both_trivially_destructible =
+        std::is_trivially_destructible<ParentMostGarbageCollectedType>::value &&
+        std::is_trivially_destructible<Derived>::value;
+    static constexpr bool has_custom_dispatch_at_base =
+        internal::HasFinalizeGarbageCollectedObject<
+            ParentMostGarbageCollectedType>::value;
+
+   public:
+    using Type = std::conditional_t<is_virtual_destructor_at_base ||
+                                        both_trivially_destructible ||
+                                        has_custom_dispatch_at_base,
+                                    ParentMostGarbageCollectedType,
+                                    Derived>;
+  };
+
+  template <typename Derived>
+  using GCInfoFoldedType = typename GCInfoFolded<Derived>::Type;
+
   GarbageCollected() = default;
 
   DISALLOW_COPY_AND_ASSIGN(GarbageCollected);
@@ -552,7 +578,7 @@
                     internal::HasFinalizeGarbageCollectedObject<T>::value,
                 "Finalized GarbageCollected class should either have a virtual "
                 "destructor or be marked as final.");
-  void* memory = T::AllocateObject(sizeof(T));
+  void* memory = T::template AllocateObject<T>(sizeof(T));
   HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
   // Placement new as regular operator new() is deleted.
   T* object = ::new (memory) T(std::forward<Args>(args)...);
@@ -579,7 +605,8 @@
                     internal::HasFinalizeGarbageCollectedObject<T>::value,
                 "Finalized GarbageCollected class should either have a virtual "
                 "destructor or be marked as final.");
-  void* memory = T::AllocateObject(sizeof(T) + additional_bytes.value);
+  void* memory =
+      T::template AllocateObject<T>(sizeof(T) + additional_bytes.value);
   HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
   // Placement new as regular operator new() is deleted.
   T* object = ::new (memory) T(std::forward<Args>(args)...);
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.h b/third_party/blink/renderer/platform/heap/heap_allocator.h
index 351f61f..ba203d3 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator.h
+++ b/third_party/blink/renderer/platform/heap/heap_allocator.h
@@ -510,6 +510,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     return ThreadHeap::Allocate<
         HeapHashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg>>(
@@ -539,6 +540,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     return ThreadHeap::Allocate<HeapHashSet<ValueArg, HashArg, TraitsArg>>(
         size);
@@ -568,6 +570,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     return ThreadHeap::Allocate<
         HeapLinkedHashSet<ValueArg, HashArg, TraitsArg>>(size);
@@ -601,6 +604,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     return ThreadHeap::Allocate<
         HeapListHashSet<ValueArg, inlineCapacity, HashArg>>(size);
@@ -629,6 +633,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     return ThreadHeap::Allocate<
         HeapHashCountedSet<Value, HashFunctions, Traits>>(size);
@@ -655,6 +660,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     // On-heap HeapVectors generally should not have inline capacity, but it is
     // hard to avoid when using a type alias. Hence we only disallow the
@@ -706,6 +712,7 @@
   }
 
  public:
+  template <typename>
   static void* AllocateObject(size_t size) {
     // On-heap HeapDeques generally should not have inline capacity, but it is
     // hard to avoid when using a type alias. Hence we only disallow the
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index 54b950c..478fac4e 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -6100,4 +6100,24 @@
   }
 }
 
+class GCBase : public GarbageCollected<GCBase> {
+ public:
+  virtual void Trace(Visitor*) {}
+};
+
+class GCDerived final : public GCBase {
+ public:
+  static int destructor_called;
+  void Trace(Visitor*) override {}
+  ~GCDerived() { ++destructor_called; }
+};
+
+int GCDerived::destructor_called = 0;
+
+TEST_F(HeapTest, CallMostDerivedFinalizer) {
+  MakeGarbageCollected<GCDerived>();
+  PreciselyCollectGarbage();
+  EXPECT_EQ(1, GCDerived::destructor_called);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc b/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc
index 69523852..874cfa1 100644
--- a/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc
@@ -40,7 +40,8 @@
     v8_references_worklist_.Push(&v8_reference);
     return;
   }
-  controller_->RegisterEmbedderReference(v8_reference.Get());
+  controller_->RegisterEmbedderReference(
+      v8_reference.template Cast<v8::Data>().Get());
 }
 
 UnifiedHeapMarkingVisitor::UnifiedHeapMarkingVisitor(ThreadState* thread_state,
diff --git a/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.cc b/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.cc
index abbae4c11..0165a9b 100644
--- a/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.cc
+++ b/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.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/public/platform/modules/peerconnection/audio_codec_factory.h"
+#include "third_party/blink/renderer/platform/peerconnection/audio_codec_factory.h"
 
 #include <memory>
 #include <vector>
diff --git a/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.h b/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.h
new file mode 100644
index 0000000..a528ac8
--- /dev/null
+++ b/third_party/blink/renderer/platform/peerconnection/audio_codec_factory.h
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_AUDIO_CODEC_FACTORY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_AUDIO_CODEC_FACTORY_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/webrtc/api/audio_codecs/audio_decoder_factory.h"
+#include "third_party/webrtc/api/audio_codecs/audio_encoder_factory.h"
+#include "third_party/webrtc/api/scoped_refptr.h"
+
+namespace blink {
+
+PLATFORM_EXPORT rtc::scoped_refptr<webrtc::AudioEncoderFactory>
+CreateWebrtcAudioEncoderFactory();
+
+PLATFORM_EXPORT rtc::scoped_refptr<webrtc::AudioDecoderFactory>
+CreateWebrtcAudioDecoderFactory();
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_AUDIO_CODEC_FACTORY_H_
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc
index a43ed373..f7d1583 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc
@@ -181,9 +181,9 @@
 
   int32_t Decode(uint32_t timestamp) {
     webrtc::EncodedImage input_image;
-    input_image.Allocate(1);
-    input_image.set_size(1);
-    input_image.data()[0] = 0;
+    static const uint8_t data[1] = {0};
+    input_image.SetEncodedData(
+        webrtc::EncodedImageBuffer::Create(data, sizeof(data)));
     input_image._frameType = webrtc::VideoFrameType::kVideoFrameKey;
     input_image._completeFrame = true;
     input_image.SetTimestamp(timestamp);
@@ -214,7 +214,9 @@
 
   webrtc::EncodedImage GetEncodedImageWithColorSpace(uint32_t timestamp) {
     webrtc::EncodedImage input_image;
-    input_image.Allocate(1);
+    static const uint8_t data[1] = {0};
+    input_image.SetEncodedData(
+        webrtc::EncodedImageBuffer::Create(data, sizeof(data)));
     input_image.set_size(1);
     input_image.data()[0] = 0;
     input_image._completeFrame = true;
diff --git a/third_party/blink/renderer/platform/peerconnection/transmission_encoding_info_handler.cc b/third_party/blink/renderer/platform/peerconnection/transmission_encoding_info_handler.cc
index 9ce0d60..e1020d02 100644
--- a/third_party/blink/renderer/platform/peerconnection/transmission_encoding_info_handler.cc
+++ b/third_party/blink/renderer/platform/peerconnection/transmission_encoding_info_handler.cc
@@ -10,10 +10,10 @@
 #include "base/cpu.h"
 #include "base/logging.h"
 #include "base/system/sys_info.h"
-#include "third_party/blink/public/platform/modules/peerconnection/audio_codec_factory.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/media_capabilities/web_media_configuration.h"
 #include "third_party/blink/renderer/platform/media_capabilities/web_video_configuration.h"
+#include "third_party/blink/renderer/platform/peerconnection/audio_codec_factory.h"
 #include "third_party/blink/renderer/platform/peerconnection/video_codec_factory.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 #include "third_party/webrtc/api/audio_codecs/audio_encoder_factory.h"
diff --git a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc
index 1698102..9c5bc18e 100644
--- a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc
+++ b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.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/public/platform/modules/peerconnection/webrtc_video_track_source.h"
+#include "third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h"
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
diff --git a/third_party/blink/public/platform/modules/peerconnection/webrtc_video_track_source.h b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h
similarity index 84%
rename from third_party/blink/public/platform/modules/peerconnection/webrtc_video_track_source.h
rename to third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h
index 934e1cd..bb02e3e4 100644
--- a/third_party/blink/public/platform/modules/peerconnection/webrtc_video_track_source.h
+++ b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_
 
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/thread_checker.h"
 #include "media/base/video_frame_pool.h"
-#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/webrtc/media/base/adapted_video_track_source.h"
 #include "third_party/webrtc/rtc_base/timestamp_aligner.h"
 
@@ -18,10 +18,7 @@
 // the webrtc video pipeline, each received a media::VideoFrame is converted to
 // a webrtc::VideoFrame, taking any adaptation requested by downstream classes
 // into account.
-//
-// TODO(crbug.com/787254): Move this class out of the Blink exposed API when its
-// clients get Onion soup'ed.
-class BLINK_PLATFORM_EXPORT WebRtcVideoTrackSource
+class PLATFORM_EXPORT WebRtcVideoTrackSource
     : public rtc::AdaptedVideoTrackSource {
  public:
   struct FrameAdaptationParams {
@@ -88,4 +85,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_
diff --git a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
index 5d339e4..343b0ff 100644
--- a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
@@ -10,7 +10,7 @@
 #include "media/base/video_frame.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/modules/peerconnection/webrtc_video_track_source.h"
+#include "third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h"
 #include "third_party/blink/renderer/platform/testing/video_frame_utils.h"
 #include "third_party/webrtc/api/video/video_frame.h"
 #include "third_party/webrtc/rtc_base/ref_counted_object.h"
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 82e3dde7..da9b3b1 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1344,6 +1344,10 @@
       status: "experimental",
     },
     {
+      name: "QuicTransport",
+      status: "experimental",
+    },
+    {
       name: "ReducedReferrerGranularity",
     },
     {
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer.cc b/third_party/blink/renderer/platform/text/date_time_format_fuzzer.cc
new file mode 100644
index 0000000..3064f62a
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/date_time_format.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class DummyTokenHandler : public DateTimeFormat::TokenHandler {
+ public:
+  ~DummyTokenHandler() override = default;
+
+  void VisitField(DateTimeFormat::FieldType field_type, int count) override {
+    CHECK(field_type != DateTimeFormat::FieldType::kFieldTypeInvalid);
+    CHECK_GE(count, 1);
+  }
+
+  void VisitLiteral(const WTF::String& string) override {
+    CHECK_GT(string.length(), 0u);
+  }
+};
+
+}  // namespace blink
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static blink::BlinkFuzzerTestSupport test_support =
+      blink::BlinkFuzzerTestSupport();
+  blink::DummyTokenHandler handler;
+  blink::DateTimeFormat::Parse(
+      WTF::String::FromUTF8(reinterpret_cast<const char*>(data), size),
+      handler);
+  return 0;
+}
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/a b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/a
new file mode 100644
index 0000000..09f23df
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/a
@@ -0,0 +1 @@
+yyyy.MM.dd G 'at' HH:mm:ss zzz
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/b b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/b
new file mode 100644
index 0000000..a1deed7
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/b
@@ -0,0 +1 @@
+EEE, MMM d, ''yy
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/c b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/c
new file mode 100644
index 0000000..a8eeb2a
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/c
@@ -0,0 +1 @@
+h:mm a
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/d b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/d
new file mode 100644
index 0000000..53bbe75b
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/d
@@ -0,0 +1 @@
+hh 'o''clock' a, zzzz
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/e b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/e
new file mode 100644
index 0000000..1840423
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/e
@@ -0,0 +1 @@
+K:mm a, z
diff --git a/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/f b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/f
new file mode 100644
index 0000000..b26f515
--- /dev/null
+++ b/third_party/blink/renderer/platform/text/date_time_format_fuzzer_seed_corpus/f
@@ -0,0 +1 @@
+yyyyy.MMMM.dd GGG hh:mm aaa
diff --git a/third_party/blink/tools/BUILD.gn b/third_party/blink/tools/BUILD.gn
index f02b590..e59f75ef 100644
--- a/third_party/blink/tools/BUILD.gn
+++ b/third_party/blink/tools/BUILD.gn
@@ -2,51 +2,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# This action generates WPT metadata files for skipping web tests
-action("build_wpt_metadata") {
-  testonly = true
-  script = "//third_party/blink/tools/build_wpt_metadata.py"
-  args = [
-    "--metadata-output-dir",
-    rebase_path("$root_out_dir/wpt_expectations_metadata"),
-    "--additional-expectations",
-    "../../third_party/blink/web_tests/WPTOverrideExpectations",
-  ]
-  outputs = [
-    "$root_out_dir/wpt_expectations_metadata.stamp",
-  ]
-  data = [
-    # Include the blinkpy tools to access expectations data
-    "//third_party/blink/tools/blinkpy/",
-  ]
-  inputs = [
-    # Include the various Test Expectations files
-    "//third_party/blink/web_tests/ASANExpectations",
-    "//third_party/blink/web_tests/LeakExpectations",
-    "//third_party/blink/web_tests/MSANExpectations",
-    "//third_party/blink/web_tests/NeverFixTests",
-    "//third_party/blink/web_tests/SlowTests",
-    "//third_party/blink/web_tests/StaleTestExpectations",
-    "//third_party/blink/web_tests/TestExpectations",
-    "//third_party/blink/web_tests/VirtualTestSuites",
-    "//third_party/blink/web_tests/WPTOverrideExpectations",
-  ]
-}
-
 # WPT codebase for running webplatform tests
 group("wpt_tests_isolate") {
   testonly = true
-  data_deps = [
-    ":build_wpt_metadata",
-    # Note that we also depend on Chrome and Chromedriver here but specify those
-    # via the //wpt_tests_isolate target in //src/BUILD.gn
-  ]
   data = [
     "//testing/scripts/common.py",
     "//testing/scripts/run_wpt_tests.py",
     "//testing/xvfb.py",
 
-    # Include blinkpy tools for post-processing WPT output.
+    # Include blinkpy tools for setting up expectations.
+    "//third_party/blink/tools/build_wpt_metadata.py",
     "//third_party/blink/tools/update_wpt_output.py",
     "//third_party/blink/tools/blinkpy/",
 
diff --git a/third_party/blink/web_tests/ASANExpectations b/third_party/blink/web_tests/ASANExpectations
index 60b1bc0..551217e 100644
--- a/third_party/blink/web_tests/ASANExpectations
+++ b/third_party/blink/web_tests/ASANExpectations
@@ -71,3 +71,5 @@
 
 # Sheriff 2019-07-31
 crbug.com/989365 [ Linux ] external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_eventhandler_attribute.tentative.https.html [ Pass Timeout ]
+
+crbug.com/1015737 [ Linux ] external/wpt/webaudio/the-audio-api/the-delaynode-interface/no-dezippering.html [ Crash ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e9be992..d6bbb62 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2475,8 +2475,6 @@
 crbug.com/955620 http/tests/navigation/frozen-useragent.html [ Skip ]
 crbug.com/955620 virtual/stable/http/tests/navigation/frozen-useragent.html [ Skip ]
 
-crbug.com/863896 http/tests/permissions/test-query.html [ Timeout ]
-
 crbug.com/876485 fast/performance/performance-measure-null-exception.html [ Failure ]
 
 crbug.com/713587 external/wpt/css/css-ui/caret-color-006.html [ Skip ]
@@ -5769,3 +5767,6 @@
 crbug.com/1014950 [ Mac ] http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js [ Pass Timeout ]
 crbug.com/1014950 [ Mac ] virtual/threaded/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js [ Pass Timeout ]
 crbug.com/1015187 [ Linux Mac ] virtual/cross-origin-embedder-policy/external/wpt/html/cross-origin-embedder-policy/none.https.html [ Pass Failure ]
+
+# Sheriff 2019-10-18
+crbug.com/996250 external/wpt/web-nfc/NFCReader_scan_iframe.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index efb5506..c2caca7 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -747,9 +747,16 @@
              "--site-per-process"]
   },
   {
+    "prefix": "split-http-cache-not-site-per-process",
+    "base": "http/tests/devtools/isolated-code-cache",
+    "args": ["--enable-features=SplitCacheByNetworkIsolationKey",
+             "--disable-site-isolation-trials"]
+  },
+  {
     "prefix": "not-site-per-process",
     "base": "http/tests/devtools/isolated-code-cache",
-    "args": ["--disable-site-isolation-trials"]
+    "args": ["--disable-site-isolation-trials",
+             "--disable-features=SplitCacheByNetworkIsolationKey"]
   },
   {
     "prefix": "not-site-per-process",
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 2572923..93d7205f 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -155740,9 +155740,15 @@
    "html/browsers/the-window-object/garbage-collection-and-browsing-contexts/discard_iframe_history_4-expected.txt": [
     []
    ],
+   "html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html": [
+    []
+   ],
    "html/browsers/the-window-object/named-access-on-the-window-object/navigated-named-objects.window-expected.txt": [
     []
    ],
+   "html/browsers/the-window-object/named-access-on-the-window-object/prototype-expected.txt": [
+    []
+   ],
    "html/browsers/the-window-object/named-access-on-the-window-object/test.html": [
     []
    ],
@@ -199203,6 +199209,262 @@
      {}
     ]
    ],
+   "compression/compression-bad-chunks.any.js": [
+    [
+     "compression/compression-bad-chunks.any.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "compression/compression-bad-chunks.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "compression/compression-bad-chunks.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "compression/compression-bad-chunks.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ]
+   ],
+   "compression/compression-including-empty-chunk.any.js": [
+    [
+     "compression/compression-including-empty-chunk.any.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "compression/compression-including-empty-chunk.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "compression/compression-including-empty-chunk.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "compression/compression-including-empty-chunk.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ]
+   ],
+   "compression/compression-multiple-chunks.any.js": [
+    [
+     "compression/compression-multiple-chunks.any.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "compression/compression-multiple-chunks.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "compression/compression-multiple-chunks.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ],
+    [
+     "compression/compression-multiple-chunks.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ],
+       [
+        "script",
+        "pako/pako_inflate.min.js"
+       ],
+       [
+        "timeout",
+        "long"
+       ]
+      ],
+      "timeout": "long"
+     }
+    ]
+   ],
+   "compression/compression-output-length.any.js": [
+    [
+     "compression/compression-output-length.any.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "compression/compression-output-length.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "compression/compression-output-length.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "compression/compression-output-length.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "worker"
+       ]
+      ]
+     }
+    ]
+   ],
    "compression/compression-stream.any.js": [
     [
      "compression/compression-stream.any.html",
@@ -242678,6 +242940,12 @@
      {}
     ]
    ],
+   "html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html": [
+    [
+     "html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html",
+     {}
+    ]
+   ],
    "html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html": [
     [
      "html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html",
@@ -242697,6 +242965,12 @@
      }
     ]
    ],
+   "html/browsers/the-window-object/named-access-on-the-window-object/prototype.html": [
+    [
+     "html/browsers/the-window-object/named-access-on-the-window-object/prototype.html",
+     {}
+    ]
+   ],
    "html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html": [
     [
      "html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html",
@@ -340284,6 +340558,22 @@
    "1d3965fca6769c70bc02308a4c70b4e58c8990e5",
    "reftest"
   ],
+  "compression/compression-bad-chunks.any.js": [
+   "06e3be9c0fe6d5663acf07cff12c4b9903c24661",
+   "testharness"
+  ],
+  "compression/compression-including-empty-chunk.any.js": [
+   "eff928857903890e967fdbcab042a44a0731e946",
+   "testharness"
+  ],
+  "compression/compression-multiple-chunks.any.js": [
+   "ca49b9cf42766a072b57cc8eb3f083127d90f8c4",
+   "testharness"
+  ],
+  "compression/compression-output-length.any.js": [
+   "d7dafcd10092d5fd1135cbb4ca2d82d5844f6d08",
+   "testharness"
+  ],
   "compression/compression-stream.any.js": [
    "47df70f7cd3979c1a3eab45581376fd38a6b2367",
    "testharness"
@@ -384849,11 +385139,11 @@
    "visual"
   ],
   "css/css-lists/content-property/marker-text-matches-armenian-ref.html": [
-   "f21dfff69608a6a1201bd586c2a6e1e24d5fd915",
+   "de55b7381ac1e2cf1553adcaee916e24e7348c4c",
    "support"
   ],
   "css/css-lists/content-property/marker-text-matches-armenian.html": [
-   "fd0df631149a740a201510f2edff405d28f41929",
+   "58f570cd1197579aba1b646a1c970ab13c23c3e3",
    "reftest"
   ],
   "css/css-lists/content-property/marker-text-matches-circle-ref.html": [
@@ -443828,6 +444118,14 @@
    "f266dd7acb96cc473129c066e59f027c2eb067f0",
    "testharness"
   ],
+  "html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html": [
+   "2acad0734cda6a5076e836240cec107af036a72a",
+   "testharness"
+  ],
+  "html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html": [
+   "9d7b9f8a25ecc392abd74949a1362c8b13450a9d",
+   "support"
+  ],
   "html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html": [
    "d5b1789d59455603d1f5929a7a7f05d8a7218d52",
    "testharness"
@@ -443840,6 +444138,14 @@
    "59d94efc994e365301c20dbc5ff1b14409dfcaab",
    "testharness"
   ],
+  "html/browsers/the-window-object/named-access-on-the-window-object/prototype-expected.txt": [
+   "312d0a1aa5fc22cd30618476df4a5538d1b4af50",
+   "support"
+  ],
+  "html/browsers/the-window-object/named-access-on-the-window-object/prototype.html": [
+   "910374381be85e5b37e1f343b6070ce7a776fe2b",
+   "testharness"
+  ],
   "html/browsers/the-window-object/named-access-on-the-window-object/test.html": [
    "c3b3cc185255d159b0f9ff9fd97aae71170d0af6",
    "support"
@@ -449609,7 +449915,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-sharedworker.js": [
@@ -449633,7 +449939,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-domain.sub.html": [
@@ -449649,7 +449955,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html": [
@@ -449657,7 +449963,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-popup.html": [
@@ -449689,7 +449995,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html": [
@@ -449697,7 +450003,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html": [
@@ -449705,7 +450011,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html": [
@@ -449713,7 +450019,7 @@
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers": [
-   "1528bf05e6368b00600b23c23042bf0d61985917",
+   "4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-worker-success.js": [
@@ -450137,7 +450443,7 @@
    "support"
   ],
   "html/interaction/focus/the-autofocus-attribute/resources/utils.js": [
-   "0eeb5a9f0adf1d09959227241cd71fe32ebb485c",
+   "c4f38fcb209e52328429a5d425fd5b5c8f2aaae4",
    "support"
   ],
   "html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html": [
@@ -450169,7 +450475,7 @@
    "testharness"
   ],
   "html/interaction/focus/the-autofocus-attribute/update-the-rendering.html": [
-   "afaf0926f5b55e4f1925010c7262ed26a616d3be",
+   "dcee4c16a07d9f5c622e562b7807e5788102fa13",
    "testharness"
   ],
   "html/obsolete/META.yml": [
@@ -463465,7 +463771,7 @@
    "support"
   ],
   "interfaces/reporting.idl": [
-   "05d5a42458b5cd38c4a902b5a7066af524a396fc",
+   "f5370e8f1e656acf7409dbda907ee9672d5e8ed1",
    "support"
   ],
   "interfaces/requestidlecallback.idl": [
@@ -491697,7 +492003,7 @@
    "testharness"
   ],
   "reporting/idlharness.any-expected.txt": [
-   "2852fc5895a9b4e8fccf5c53619cdb79fb399587",
+   "55b27a0ceb464800e872eda9f44207f755d5beea",
    "support"
   ],
   "reporting/idlharness.any.js": [
@@ -491705,7 +492011,7 @@
    "testharness"
   ],
   "reporting/idlharness.any.worker-expected.txt": [
-   "2d07eecf67cc89acc3cf03366b39e9fb2cb3611a",
+   "ce631f117aeb43da8384f991a9305c63bf527223",
    "support"
   ],
   "reporting/idlharness.window-expected.txt": [
@@ -492505,7 +492811,7 @@
    "support"
   ],
   "resources/chromium/nfc-mock.js": [
-   "30645fb00dd924085cf2dc83325d2a579dec44ed",
+   "6ef03b390d8df7df8abd7662e037db6bdf5febce",
    "support"
   ],
   "resources/chromium/sensor.mojom.js": [
@@ -512197,7 +512503,7 @@
    "testharness"
   ],
   "web-nfc/NFCReader_scan_iframe.https.html": [
-   "1f29fe4b41d9028a2381210b8b3d9ccba598b9b2",
+   "31e79b0388aa5f407a7c9a1b591f162b3e42001f",
    "testharness"
   ],
   "web-nfc/NFCReadingEvent_constructor.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/compression/compression-bad-chunks.any.js b/third_party/blink/web_tests/external/wpt/compression/compression-bad-chunks.any.js
new file mode 100644
index 0000000..06e3be9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compression/compression-bad-chunks.any.js
@@ -0,0 +1,62 @@
+// META: global=worker
+
+'use strict';
+
+const badChunks = [
+  {
+    name: 'undefined',
+    value: undefined
+  },
+  {
+    name: 'null',
+    value: null
+  },
+  {
+    name: 'numeric',
+    value: 3.14
+  },
+  {
+    name: 'object, not BufferSource',
+    value: {}
+  },
+  {
+    name: 'array',
+    value: [65]
+  },
+  {
+    name: 'SharedArrayBuffer',
+    // Use a getter to postpone construction so that all tests don't fail where
+    // SharedArrayBuffer is not yet implemented.
+    get value() {
+      return new SharedArrayBuffer();
+    }
+  },
+  {
+    name: 'shared Uint8Array',
+    get value() {
+      return new Uint8Array(new SharedArrayBuffer())
+    }
+  },
+];
+
+for (const chunk of badChunks) {
+  promise_test(async t => {
+    const cs = new CompressionStream('gzip');
+    const reader = cs.readable.getReader();
+    const writer = cs.writable.getWriter();
+    const writePromise = writer.write(chunk.value);
+    const readPromise = reader.read();
+    await promise_rejects(t, new TypeError(), writePromise, 'write should reject');
+    await promise_rejects(t, new TypeError(), readPromise, 'read should reject');
+  }, `chunk of type ${chunk.name} should error the stream for gzip`);
+
+  promise_test(async t => {
+    const cs = new CompressionStream('deflate');
+    const reader = cs.readable.getReader();
+    const writer = cs.writable.getWriter();
+    const writePromise = writer.write(chunk.value);
+    const readPromise = reader.read();
+    await promise_rejects(t, new TypeError(), writePromise, 'write should reject');
+    await promise_rejects(t, new TypeError(), readPromise, 'read should reject');
+  }, `chunk of type ${chunk.name} should error the stream for deflate`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/compression/compression-including-empty-chunk.any.js b/third_party/blink/web_tests/external/wpt/compression/compression-including-empty-chunk.any.js
new file mode 100644
index 0000000..eff9288
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compression/compression-including-empty-chunk.any.js
@@ -0,0 +1,57 @@
+// META: global=worker
+// META: script=pako/pako_inflate.min.js
+// META: timeout=long
+
+'use strict';
+
+// This test asserts that compressing '' doesn't affect the compressed data.
+// Example: compressing ['Hello', '', 'Hello'] results in 'HelloHello'
+
+async function compressChunkList(chunkList, format) {
+  const cs = new CompressionStream(format);
+  const writer = cs.writable.getWriter();
+  for (const chunk of chunkList) {
+    const chunkByte = new TextEncoder().encode(chunk);
+    writer.write(chunkByte);
+  }
+  const closePromise = writer.close();
+  const out = [];
+  const reader = cs.readable.getReader();
+  let totalSize = 0;
+  while (true) {
+    const { value, done } = await reader.read();
+    if (done)
+      break;
+    out.push(value);
+    totalSize += value.byteLength;
+  }
+  await closePromise;
+  const concatenated = new Uint8Array(totalSize);
+  let offset = 0;
+  for (const array of out) {
+    concatenated.set(array, offset);
+    offset += array.byteLength;
+  }
+  return concatenated;
+}
+
+const chunkLists = [
+  ['', 'Hello', 'Hello'],
+  ['Hello', '', 'Hello'],
+  ['Hello', 'Hello', '']
+];
+const expectedValue = new TextEncoder().encode('HelloHello');
+
+for (const chunkList of chunkLists) {
+  promise_test(async t => {
+    const compressedData = await compressChunkList(chunkList, 'deflate');
+    // decompress with pako, and check that we got the same result as our original string
+    assert_array_equals(expectedValue, pako.inflate(compressedData), 'value should match');
+  }, `the result of compressing [${chunkList}] with deflate should be 'HelloHello'`);
+
+  promise_test(async t => {
+    const compressedData = await compressChunkList(chunkList, 'gzip');
+    // decompress with pako, and check that we got the same result as our original string
+    assert_array_equals(expectedValue, pako.inflate(compressedData), 'value should match');
+  }, `the result of compressing [${chunkList}] with gzip should be 'HelloHello'`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/compression/compression-multiple-chunks.any.js b/third_party/blink/web_tests/external/wpt/compression/compression-multiple-chunks.any.js
new file mode 100644
index 0000000..ca49b9cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compression/compression-multiple-chunks.any.js
@@ -0,0 +1,60 @@
+// META: global=worker
+// META: script=pako/pako_inflate.min.js
+// META: timeout=long
+
+'use strict';
+
+// This test asserts that compressing multiple chunks should work.
+
+// Example: ('Hello', 3) => TextEncoder().encode('HelloHelloHello')
+function makeExpectedChunk(input, numberOfChunks) {
+  const expectedChunk = input.repeat(numberOfChunks);
+  return new TextEncoder().encode(expectedChunk);
+}
+
+// Example: ('Hello', 3, 'deflate') => compress ['Hello', 'Hello', Hello']
+async function compressMultipleChunks(input, numberOfChunks, format) {
+  const cs = new CompressionStream(format);
+  const writer = cs.writable.getWriter();
+  const chunk = new TextEncoder().encode(input);
+  for (let i = 0; i < numberOfChunks; ++i) {
+    writer.write(chunk);
+  }
+  const closePromise = writer.close();
+  const out = [];
+  const reader = cs.readable.getReader();
+  let totalSize = 0;
+  while (true) {
+    const { value, done } = await reader.read();
+    if (done)
+      break;
+    out.push(value);
+    totalSize += value.byteLength;
+  }
+  await closePromise;
+  const concatenated = new Uint8Array(totalSize);
+  let offset = 0;
+  for (const array of out) {
+    concatenated.set(array, offset);
+    offset += array.byteLength;
+  }
+  return concatenated;
+}
+
+const hello = 'Hello';
+
+for (let numberOfChunks = 2; numberOfChunks <= 16; ++numberOfChunks) {
+  promise_test(async t => {
+    const compressedData = await compressMultipleChunks(hello, numberOfChunks, 'deflate');
+    const expectedValue = makeExpectedChunk(hello, numberOfChunks);
+    // decompress with pako, and check that we got the same result as our original string
+    assert_array_equals(expectedValue, pako.inflate(compressedData), 'value should match');
+  }, `compressing ${numberOfChunks} chunks with deflate should work`);
+
+  promise_test(async t => {
+    const compressedData = await compressMultipleChunks(hello, numberOfChunks, 'gzip');
+    const expectedValue = makeExpectedChunk(hello, numberOfChunks);
+    // decompress with pako, and check that we got the same result as our original string
+    assert_array_equals(expectedValue, pako.inflate(compressedData), 'value should match');
+  }, `compressing ${numberOfChunks} chunks with gzip should work`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/compression/compression-output-length.any.js b/third_party/blink/web_tests/external/wpt/compression/compression-output-length.any.js
new file mode 100644
index 0000000..d7dafcd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compression/compression-output-length.any.js
@@ -0,0 +1,54 @@
+// META: global=worker
+
+'use strict';
+
+// This test asserts that compressed data length is shorter than the original
+// data length. If the input is extremely small, the compressed data may be
+// larger than the original data.
+
+const LARGE_FILE = '/media/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr.webm';
+
+async function compressArrayBuffer(input, format) {
+  const cs = new CompressionStream(format);
+  const writer = cs.writable.getWriter();
+  writer.write(input);
+  const closePromise = writer.close();
+  const out = [];
+  const reader = cs.readable.getReader();
+  let totalSize = 0;
+  while (true) {
+    const { value, done } = await reader.read();
+    if (done)
+      break;
+    out.push(value);
+    totalSize += value.byteLength;
+  }
+  await closePromise;
+  const concatenated = new Uint8Array(totalSize);
+  let offset = 0;
+  for (const array of out) {
+    concatenated.set(array, offset);
+    offset += array.byteLength;
+  }
+  return concatenated;
+}
+
+promise_test(async () => {
+  const response = await fetch(LARGE_FILE);
+  const buffer = await response.arrayBuffer();
+  const bufferView = new Uint8Array(buffer);
+  const originalLength = bufferView.length;
+  const compressedData = await compressArrayBuffer(bufferView, 'deflate');
+  const compressedLength = compressedData.length;
+  assert_less_than(compressedLength, originalLength, 'output should be smaller');
+}, 'the length of deflated data should be shorter than that of the original data');
+
+promise_test(async () => {
+  const response = await fetch(LARGE_FILE);
+  const buffer = await response.arrayBuffer();
+  const bufferView = new Uint8Array(buffer);
+  const originalLength = bufferView.length;
+  const compressedData = await compressArrayBuffer(bufferView, 'gzip');
+  const compressedLength = compressedData.length;
+  assert_less_than(compressedLength, originalLength, 'output should be smaller');
+}, 'the length of gzipped data should be shorter than that of the original data');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian-ref.html b/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian-ref.html
index f21dfff..de55b738 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian-ref.html
@@ -7,4 +7,4 @@
     padding: 0;
 }
 </style>
-<p>Ô±. Filler Text
+<p>Ô±. Filler Text<span style="display: inline-block; width: 1px; height: 50px;"></span>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian.html b/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian.html
index fd0df63..58f570c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/content-property/marker-text-matches-armenian.html
@@ -14,5 +14,5 @@
 </style>
 
 <ol>
-  <li>Filler Text</li>
+  <li>Filler Text<span style="display: inline-block; width: 1px; height: 50px;"></span></li>
 </ol>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html
new file mode 100644
index 0000000..2acad07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-npo.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access across globals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function() {
+  var iframe = document.createElement("iframe");
+  iframe.src = "cross-global-support.html";
+  document.body.appendChild(iframe);
+  iframe.onload = this.step_func_done(function() {
+    var name = "named";
+    var win = iframe.contentWindow;
+    var element = win.document.getElementById(name);
+
+    var expectedValues = [
+      // [value, is own property]
+      [element, false, "window"],
+      [element, false, "Window.prototype"],
+      [element, true, "named prototype object"],
+      [undefined, false, "EventTarget.prototype"],
+      [undefined, false, "Object.prototype"],
+    ];
+    for (var object = win; object; object = Object.getPrototypeOf(object)) {
+      var expected = expectedValues.shift();
+      assert_equals(object[name], expected[0], "[[Get]] on " + expected[2]);
+      var desc = Object.getOwnPropertyDescriptor(object, name);
+      if (expected[1]) {
+        assert_not_equals(desc, undefined, "[[GetOwnProperty]] on " + expected[2] + " should return something");
+        assert_equals(desc.value, element, "[[GetOwnProperty]] on " + expected[2]);
+      } else {
+        assert_equals(desc, undefined, "[[GetOwnProperty]] on " + expected[2]);
+      }
+    }
+  });
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html
new file mode 100644
index 0000000..9d7b9f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/cross-global-support.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access across globals: support file</title>
+<span id="named"></span>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/prototype-expected.txt b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/prototype-expected.txt
new file mode 100644
index 0000000..312d0a1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/prototype-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS Property on window.
+PASS Property on Window.prototype.
+FAIL Property on EventTarget.prototype. assert_equals: expected (undefined) undefined but got (object) object "[object Object]"
+FAIL Property on Object.prototype. assert_equals: expected (undefined) undefined but got (object) object "[object Object]"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html
new file mode 100644
index 0000000..9103743
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-window-object/named-access-on-the-window-object/prototype.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Named access with shadowing properties</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+  var name = "named1";
+  window[name] = "shadowing";
+  var element = document.createElement("span");
+  element.id = name;
+  document.body.appendChild(element);
+
+  assert_equals(window[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(window, name).value, "shadowing");
+
+  assert_equals(Window.prototype[name], element);
+  assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name), undefined);
+
+  var npo = Object.getPrototypeOf(Window.prototype);
+  assert_equals(npo[name], element);
+  assert_equals(Object.getOwnPropertyDescriptor(npo, name).value, element);
+
+  assert_equals(EventTarget.prototype[name], undefined);
+  assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name), undefined);
+}, "Property on window.");
+
+test(function() {
+  var name = "named2";
+  Window.prototype[name] = "shadowing";
+  var element = document.createElement("span");
+  element.id = name;
+  document.body.appendChild(element);
+
+  assert_equals(window[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(window, name), undefined);
+
+  assert_equals(Window.prototype[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name).value, "shadowing");
+
+  var npo = Object.getPrototypeOf(Window.prototype);
+  assert_equals(npo[name], element);
+  assert_equals(Object.getOwnPropertyDescriptor(npo, name).value, element);
+
+  assert_equals(EventTarget.prototype[name], undefined);
+  assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name), undefined);
+}, "Property on Window.prototype.");
+
+test(function() {
+  var name = "named3";
+  EventTarget.prototype[name] = "shadowing";
+  var element = document.createElement("span");
+  element.id = name;
+  document.body.appendChild(element);
+
+  assert_equals(window[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(window, name), undefined);
+
+  assert_equals(Window.prototype[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name), undefined);
+
+  var npo = Object.getPrototypeOf(Window.prototype);
+  assert_equals(npo[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(npo, name), undefined);
+
+  assert_equals(EventTarget.prototype[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name).value, "shadowing");
+}, "Property on EventTarget.prototype.");
+
+test(function() {
+  var name = "named4";
+  Object.prototype[name] = "shadowing";
+  var element = document.createElement("span");
+  element.id = name;
+  document.body.appendChild(element);
+
+  assert_equals(window[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(window, name), undefined);
+
+  assert_equals(Window.prototype[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(Window.prototype, name), undefined);
+
+  var npo = Object.getPrototypeOf(Window.prototype);
+  assert_equals(npo[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(npo, name), undefined);
+
+  assert_equals(EventTarget.prototype[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(EventTarget.prototype, name), undefined);
+
+  assert_equals(Object.prototype[name], "shadowing");
+  assert_equals(Object.getOwnPropertyDescriptor(Object.prototype, name).value, "shadowing");
+}, "Property on Object.prototype.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/broadcastchannel-iframe.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/iframe-failure.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe-messagechannel.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/incrementer-iframe.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-1.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-2.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-3.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers
index 1528bf0..4e798cd9 100644
--- a/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers
+++ b/third_party/blink/web_tests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/resources/nested-iframe-4-incrementer.html.headers
@@ -1,2 +1,2 @@
 Cross-Origin-Embedder-Policy: require-corp
-Cross-Origin-Resource-Policy: cross-site
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl b/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl
index 05d5a42..f5370e8f 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl
@@ -5,10 +5,12 @@
 
 [Exposed=(Window,Worker)]
 interface ReportBody {
+  [Default] object toJSON();
 };
 
 [Exposed=(Window,Worker)]
 interface Report {
+  [Default] object toJSON();
   readonly attribute DOMString type;
   readonly attribute DOMString url;
   readonly attribute ReportBody? body;
@@ -33,6 +35,7 @@
 
 [Exposed=(Window,Worker)]
 interface DeprecationReportBody : ReportBody {
+  [Default] object toJSON();
   readonly attribute DOMString id;
   readonly attribute Date? anticipatedRemoval;
   readonly attribute DOMString message;
@@ -43,6 +46,7 @@
 
 [Exposed=(Window,Worker)]
 interface InterventionReportBody : ReportBody {
+  [Default] object toJSON();
   readonly attribute DOMString id;
   readonly attribute DOMString message;
   readonly attribute DOMString? sourceFile;
@@ -52,6 +56,7 @@
 
 [Exposed=(Window,Worker)]
 interface CrashReportBody : ReportBody {
+  [Default] object toJSON();
   readonly attribute DOMString? reason;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/reporting/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/reporting/idlharness.any-expected.txt
index 2852fc58..55b27a0 100644
--- a/third_party/blink/web_tests/external/wpt/reporting/idlharness.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/reporting/idlharness.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 56 tests; 11 PASS, 45 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 61 tests; 11 PASS, 50 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 FAIL ReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
@@ -8,12 +8,14 @@
 FAIL ReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
 FAIL ReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
 FAIL ReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface: operation toJSON() assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
 FAIL Report interface: existence and properties of interface object assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface object length assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface object name assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: existence and properties of interface prototype object assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: operation toJSON() assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: attribute type assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: attribute url assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: attribute body assert_own_property: self does not have own property "Report" expected property "Report" missing
@@ -32,6 +34,7 @@
 FAIL DeprecationReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: operation toJSON() assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: attribute id assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: attribute anticipatedRemoval assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: attribute message assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
@@ -44,6 +47,7 @@
 FAIL InterventionReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: operation toJSON() assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: attribute id assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: attribute message assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: attribute sourceFile assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
@@ -55,6 +59,7 @@
 FAIL CrashReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 FAIL CrashReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 FAIL CrashReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface: operation toJSON() assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 FAIL CrashReportBody interface: attribute reason assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt
index 2d07eec..ce631f1 100644
--- a/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 56 tests; 2 PASS, 54 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 61 tests; 2 PASS, 59 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 FAIL ReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
@@ -8,12 +8,14 @@
 FAIL ReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
 FAIL ReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
 FAIL ReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface: operation toJSON() assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
 FAIL Report interface: existence and properties of interface object assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface object length assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface object name assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: existence and properties of interface prototype object assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: operation toJSON() assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: attribute type assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: attribute url assert_own_property: self does not have own property "Report" expected property "Report" missing
 FAIL Report interface: attribute body assert_own_property: self does not have own property "Report" expected property "Report" missing
@@ -32,6 +34,7 @@
 FAIL DeprecationReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: operation toJSON() assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: attribute id assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: attribute anticipatedRemoval assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
 FAIL DeprecationReportBody interface: attribute message assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
@@ -44,6 +47,7 @@
 FAIL InterventionReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: operation toJSON() assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: attribute id assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: attribute message assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
 FAIL InterventionReportBody interface: attribute sourceFile assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
@@ -55,6 +59,7 @@
 FAIL CrashReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 FAIL CrashReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 FAIL CrashReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface: operation toJSON() assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 FAIL CrashReportBody interface: attribute reason assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js b/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js
index 30645fb0..6ef03b3 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/nfc-mock.js
@@ -32,7 +32,7 @@
 }
 
 function toByteArray(data) {
-  // Convert JS objects to byte array
+  // Convert JS objects to byte array.
   let byteArray = new Uint8Array(0);
   let tmpData = data;
 
@@ -49,7 +49,7 @@
 
 // Compares NDEFRecords that were provided / received by the mock service.
 // TODO: Use different getters to get received record data,
-// see spec changes at https://github.com/w3c/web-nfc/pull/243
+// see spec changes at https://github.com/w3c/web-nfc/pull/243.
 function compareNDEFRecords(providedRecord, receivedRecord) {
   assert_equals(providedRecord.recordType, receivedRecord.recordType);
 
@@ -105,7 +105,7 @@
 
 // Checks whether NFCReaderOptions are matched with given message.
 function matchesWatchOptions(message, options) {
-  // Filter by Web NFC id
+  // Filter by Web NFC id.
   if (!matchesWebNfcId(message.url, options.url)) return false;
 
   // Matches any record / media type.
@@ -114,7 +114,7 @@
     return true;
   }
 
-  // Filter by mediaType and recordType
+  // Filter by mediaType and recordType.
   for (let record of message.records) {
     if (options.mediaType != null && options.mediaType !== ""
         && options.mediaType !== record.mediaType) {
@@ -175,14 +175,15 @@
       this.client_ = null;
       this.watchers_ = [];
       this.reading_messages_ = [];
+      this.operations_suspended_ = false;
     }
 
-    // NFC delegate functions
+    // NFC delegate functions.
     async push(message, options) {
       let error = this.getHWError();
       if (error)
         return error;
-      // Cancel previous pending push operation
+      // Cancel previous pending push operation.
       if (this.pending_promise_func_) {
         this.cancelPendingPushOperation();
       }
@@ -192,9 +193,12 @@
 
       return new Promise(resolve => {
         this.pending_promise_func_ = resolve;
-        if (options.timeout && options.timeout !== Infinity &&
+        // Pend push operation if NFC operation is suspended.
+        if (this.operations_suspended_) {
+          // Do nothing, pends push operation.
+        } else if (options.timeout && options.timeout !== Infinity &&
             !this.push_completed_) {
-          // Resolve with TimeoutError, else pending push operation.
+          // Resolve with TimeoutError, else pend push operation.
           if (this.push_should_timeout_) {
             resolve(
                 createNFCError(device.mojom.NFCErrorType.TIMER_EXPIRED));
@@ -226,11 +230,14 @@
       }
 
       this.watchers_.push({id: id, options: options});
-      // Triggers onWatch if the new watcher matches existing messages
-      for (let message of this.reading_messages_) {
-        if (matchesWatchOptions(message, options)) {
-          this.client_.onWatch(
-              [id], fake_tag_serial_number, toMojoNDEFMessage(message));
+      // Ignore reading if NFC operation is suspended.
+      if(!this.operations_suspended_) {
+        // Triggers onWatch if the new watcher matches existing messages.
+        for (let message of this.reading_messages_) {
+          if (matchesWatchOptions(message, options)) {
+            this.client_.onWatch(
+                [id], fake_tag_serial_number, toMojoNDEFMessage(message));
+          }
         }
       }
 
@@ -290,6 +297,7 @@
       this.push_completed_ = true;
       this.watchers_ = [];
       this.reading_messages_ = [];
+      this.operations_suspended_ = false;
       this.cancelPendingPushOperation();
       this.bindingSet_.closeAllBindings();
       this.interceptor_.stop();
@@ -311,10 +319,12 @@
     // Sets message that is used to deliver NFC reading updates.
     setReadingMessage(message) {
       this.reading_messages_.push(message);
-      // Ignore reading if NFCPushOptions.ignoreRead is true
+      // Ignore reading if NFC operation is suspended.
+      if(this.operations_suspended_) return;
+      // Ignore reading if NFCPushOptions.ignoreRead is true.
       if(this.push_options_ && this.push_options_.ignoreRead)
         return;
-      // Triggers onWatch if the new message matches existing watchers
+      // Triggers onWatch if the new message matches existing watchers.
       for (let watcher of this.watchers_) {
         if (matchesWatchOptions(message, watcher.options)) {
           this.client_.onWatch(
@@ -327,6 +337,31 @@
     setPushShouldTimeout(result) {
       this.push_should_timeout_ = result;
     }
+
+    // Suspends all pending NFC operations. Could be used when web page
+    // visibility is lost.
+    suspendNFCOperations() {
+      this.operations_suspended_ = true;
+    }
+
+    // Resumes all suspended NFC operations.
+    resumeNFCOperations() {
+      this.operations_suspended_ = false;
+      // Resumes pending NFC reading.
+      for (let watcher of this.watchers_) {
+        for (let message of this.reading_messages_) {
+          if (matchesWatchOptions(message, watcher.options)) {
+            this.client_.onWatch(
+                [watcher.id], fake_tag_serial_number,
+                toMojoNDEFMessage(message));
+          }
+        }
+      }
+      // Resumes pending push operation.
+      if (this.pending_promise_func_) {
+        this.pending_promise_func_(createNFCError(null));
+      }
+    }
   }
 
   let testInternal = {
diff --git a/third_party/blink/web_tests/external/wpt/web-nfc/NFCReader_scan_iframe.https.html b/third_party/blink/web_tests/external/wpt/web-nfc/NFCReader_scan_iframe.https.html
index 1f29fe4b..31e79b0 100644
--- a/third_party/blink/web_tests/external/wpt/web-nfc/NFCReader_scan_iframe.https.html
+++ b/third_party/blink/web_tests/external/wpt/web-nfc/NFCReader_scan_iframe.https.html
@@ -25,10 +25,13 @@
   const iframeLoadWatcher = new EventWatcher(t, iframe, 'load');
   document.body.appendChild(iframe);
   await iframeLoadWatcher.wait_for('load');
-  // Focus on iframe
+  // Focus on iframe.
   iframe.contentWindow.document.getElementById('foo').focus();
   assert_true(iframe.contentDocument.hasFocus(), 'iframe gains the focus');
-
+  // Suspend NFC operations is async in blink, use setTimeout as a workaround.
+  // TODO(wanming.lin@intel.com): find a good way to eliminate this race
+  // condition.
+  await new Promise(resolve => t.step_timeout(resolve, 0));
   mockNFC.setReadingMessage(createMessage([createTextRecord(test_text_data)]));
   await promise;
 
diff --git a/third_party/blink/web_tests/http/tests/permissions/resources/test-query.js b/third_party/blink/web_tests/http/tests/permissions/resources/test-query.js
index 0c9eff1..eb2b361 100644
--- a/third_party/blink/web_tests/http/tests/permissions/resources/test-query.js
+++ b/third_party/blink/web_tests/http/tests/permissions/resources/test-query.js
@@ -27,7 +27,7 @@
 async_test(function(test) {
     navigator.permissions.query({name:'ambient-light-sensor'}).then(function(result) {
         assert_true(result instanceof PermissionStatus);
-        assert_equals(result.state, 'granted');
+        assert_equals(result.state, 'denied');
         test.done();
     }).catch(function() {
         assert_unreached('querying ambient-light-sensor permission should not fail.')
@@ -37,7 +37,7 @@
 async_test(function(test) {
     navigator.permissions.query({name:'accelerometer'}).then(function(result) {
         assert_true(result instanceof PermissionStatus);
-        assert_equals(result.state, 'granted');
+        assert_equals(result.state, 'denied');
         test.done();
     }).catch(function() {
         assert_unreached('querying accelerometer permission should not fail.')
@@ -47,7 +47,7 @@
 async_test(function(test) {
     navigator.permissions.query({name:'gyroscope'}).then(function(result) {
         assert_true(result instanceof PermissionStatus);
-        assert_equals(result.state, 'granted');
+        assert_equals(result.state, 'denied');
         test.done();
     }).catch(function() {
         assert_unreached('querying gyroscope permission should not fail.')
@@ -57,7 +57,7 @@
 async_test(function(test) {
     navigator.permissions.query({name:'magnetometer'}).then(function(result) {
         assert_true(result instanceof PermissionStatus);
-        assert_equals(result.state, 'granted');
+        assert_equals(result.state, 'denied');
         test.done();
     }).catch(function() {
         assert_unreached('querying magnetometer permission should not fail.')
@@ -82,7 +82,7 @@
     }).catch(function() {
         assert_unreached('querying camera permission should not fail.')
     });
-}, 'Test geolocation permission in ' + get_current_scope() + ' scope.');
+}, 'Test camera permission in ' + get_current_scope() + ' scope.');
 
 async_test(function(test) {
     navigator.permissions.query({name:'microphone'}).then(function(result) {
@@ -92,7 +92,7 @@
     }).catch(function() {
         assert_unreached('querying microphone permission should not fail.')
     });
-}, 'Test geolocation permission in ' + get_current_scope() + ' scope.');
+}, 'Test microphone permission in ' + get_current_scope() + ' scope.');
 
 async_test(function(test) {
     navigator.permissions.query({name:'midi'}).then(function(result) {
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 c767408d..ead7975 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
@@ -1219,6 +1219,9 @@
     getter applicationServerKey
     getter userVisibleOnly
     method constructor
+interface QuicTransport
+    attribute @@toStringTag
+    method constructor
 interface ReadableStream
     attribute @@toStringTag
     getter locked
diff --git a/third_party/blink/web_tests/virtual/not-site-per-process/README.md b/third_party/blink/web_tests/virtual/not-site-per-process/README.md
index 2c70610a..7da7f6c6 100644
--- a/third_party/blink/web_tests/virtual/not-site-per-process/README.md
+++ b/third_party/blink/web_tests/virtual/not-site-per-process/README.md
@@ -15,7 +15,11 @@
 Tests under `virtual/not-site-per-process` are run with
 `--disable-site-isolation-trials` cmdline flag which turns off site
 isolation.  This is needed to preserve test coverage provided by around
-60 tests that fail when run with site isolation.
+60 tests that fail when run with site isolation. `isolated-code-cache` tests are
+also run with `--disable-features=SplitCacheByNetworkIsolationKey` which turns
+off HTTP cache partitioning. This is needed as a test expects cross-origin
+resources to be cached. Equivalent tests with the feature enabled can be found
+under `virtual/split-http-cache-not-site-per-process`.
 
 When modifying the list of files that behave differently with and without
 OOPIFs, please consider modifying all the locations below:
diff --git a/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
new file mode 100644
index 0000000..58f5e0e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
@@ -0,0 +1,6 @@
+# This suite runs the tests in http/tests/devtools/isolated-code-cache with
+# --enable-features=SplitCacheByNetworkIsolationKey
+# --disable-site-isolation-trials
+# This feature partitions the HTTP cache by network isolation key for improved
+# security. Tracking bug: crbug.com/910708. This test disables site isolation,
+# which would cause similar results.
diff --git a/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test-expected.txt b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test-expected.txt
new file mode 100644
index 0000000..81e758db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test-expected.txt
@@ -0,0 +1,82 @@
+Tests V8 code cache for javascript resources
+
+---First navigation - produce and consume code cache ------
+
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "script too small"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "already used streamed data"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "already used streamed data"
+        producedCacheSize : <number>
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+v8.compile Properties:
+{
+    data : {
+        cacheConsumeOptions : "code"
+        cacheRejected : false
+        columnNumber : 0
+        consumedCacheSize : <number>
+        lineNumber : 0
+        notStreamedReason : "already used streamed data"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+
+--- Second navigation - from a different origin ------
+
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "script too small"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+
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 3e128bf..aeda26e 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
@@ -1154,6 +1154,9 @@
 [Worker]     getter applicationServerKey
 [Worker]     getter userVisibleOnly
 [Worker]     method constructor
+[Worker] interface QuicTransport
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface ReadableStream
 [Worker]     attribute @@toStringTag
 [Worker]     getter locked
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 e3b3b80..3b97774 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
@@ -5965,6 +5965,9 @@
     getter applicationServerKey
     getter userVisibleOnly
     method constructor
+interface QuicTransport
+    attribute @@toStringTag
+    method constructor
 interface RTCCertificate
     attribute @@toStringTag
     getter expires
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 647debe..83990e1 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
@@ -1136,6 +1136,9 @@
 [Worker]     getter applicationServerKey
 [Worker]     getter userVisibleOnly
 [Worker]     method constructor
+[Worker] interface QuicTransport
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface ReadableStream
 [Worker]     attribute @@toStringTag
 [Worker]     getter locked
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7376eb5..823baa4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4740,22 +4740,35 @@
   <int value="1" label="By JavaScript"/>
 </enum>
 
-<enum name="BackForwardCacheEvictedReason">
-  <int value="0" label="Timeout"/>
-  <int value="1" label="Cache limit"/>
-  <int value="2" label="JavaScript execution"/>
-  <int value="3" label="Renderer process killed"/>
-  <int value="4" label="Renderer process crashed"/>
-  <int value="5" label="Dialog"/>
-  <int value="6" label="Granted media stream access"/>
-  <int value="7" label="Scheduler tracked feature used"/>
-</enum>
-
 <enum name="BackForwardCacheHistoryNavigationOutcome">
   <int value="0" label="Restored"/>
   <int value="1" label="Not restored"/>
 </enum>
 
+<enum name="BackForwardCacheNotRestoredReason">
+  <int value="0" label="Not main frame. This shouldn't be recorded."/>
+  <int value="1" label="Back forward cache disabled"/>
+  <int value="2" label="Related active contents exist"/>
+  <int value="3" label="HTTP status not OK"/>
+  <int value="4" label="Scheme not HTTP or HTTPS"/>
+  <int value="5" label="Loading"/>
+  <int value="6" label="Was granted media access"/>
+  <int value="7" label="Blocklisted features"/>
+  <int value="8" label="Disable for render frame host called"/>
+  <int value="9" label="Domain not allowed"/>
+  <int value="10" label="HTTP method not GET"/>
+  <int value="11" label="Subframe is navigating"/>
+  <int value="12" label="Timeout"/>
+  <int value="13" label="Cache limit"/>
+  <int value="14" label="JavaScript execution"/>
+  <int value="15" label="Renderer process killed"/>
+  <int value="16" label="Renderer process crashed"/>
+  <int value="17" label="Dialog"/>
+  <int value="18" label="Granted media stream access"/>
+  <int value="19" label="Scheduler tracked feature used"/>
+  <int value="20" label="Conflicting BrowsingInstance"/>
+</enum>
+
 <enum name="BackForwardNavigationType">
   <int value="0" label="Fast back navigation with WKBackForwardList"/>
   <int value="1" label="Slow back navigation"/>
@@ -10130,6 +10143,7 @@
   <int value="55" label="System wake lock"/>
   <int value="56" label="Legacy cookie access"/>
   <int value="57" label="Native file system write guard"/>
+  <int value="58" label="Installed web app metadata"/>
 </enum>
 
 <enum name="ContentTypeParseableResult">
@@ -20956,6 +20970,7 @@
   <int value="1394" label="AUTOTESTPRIVATE_GETAPPWINDOWLIST"/>
   <int value="1395" label="AUTOTESTPRIVATE_SETAPPWINDOWSTATE"/>
   <int value="1396" label="AUTOTESTPRIVATE_CLOSEAPPWINDOW"/>
+  <int value="1397" label="AUTOTESTPRIVATE_REFRESHENTERPRISEPOLICIES"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -32768,6 +32783,12 @@
   <int value="5" label="Invalid GUID"/>
 </enum>
 
+<enum name="InvalidDevicePolicyFilesStatus">
+  <int value="0" label="All valid"/>
+  <int value="1" label="Some invalid"/>
+  <int value="2" label="All invalid"/>
+</enum>
+
 <enum name="InvalidOriginReason">
   <int value="0" label="Opaque Origin"/>
   <int value="1" label="Empty URL"/>
@@ -39112,6 +39133,7 @@
   <int value="4" label="Password Generation Confirmation"/>
   <int value="5" label="Profile Chooser"/>
   <int value="6" label="Passwords Accessory Sheet"/>
+  <int value="7" label="Touch To Fill"/>
 </enum>
 
 <enum name="ManifestFetchResultType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 997e61a..5fa17af0 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13440,17 +13440,17 @@
   </summary>
 </histogram>
 
-<histogram name="BackForwardCache.HistoryNavigationOutcome.EvictedReason"
-    enum="BackForwardCacheEvictedReason" expires_after="2020-10-01">
+<histogram name="BackForwardCache.HistoryNavigationOutcome.NotRestoredReason"
+    enum="BackForwardCacheNotRestoredReason" expires_after="2020-10-01">
   <owner>bfcache-dev@chromium.org</owner>
   <owner>hajimehoshi@chromium.org</owner>
   <summary>
-    When navigating back to a page in the session history, this records the
-    reason why the page is evicted from the back-forward cache. Eviction can
-    happen when the page can no longer be held by the cache. This is a breakdown
-    metric of BackForwardCache.HistoryNavigationOutcome's 'Evicted' value.
+    When navigating back to a page in the session history, record why it wasn't
+    restored from the cache. The page might not have entered the back-forward in
+    the first place based on the features used, or it might have been evicted
+    while being in the cache.
 
-    Recording starts as of M79.
+    This recording starts as of M79.
   </summary>
 </histogram>
 
@@ -37315,6 +37315,10 @@
 
 <histogram name="Enterprise.InvalidDevicePolicyFiles" units="files"
     expires_after="2020-07-02">
+  <obsolete>
+    Deprecated 10/2019, since no code reports it anymore. Superseded by
+    Enterprise.InvalidDevicePolicyFilesStatus.
+  </obsolete>
   <owner>emaxx@chromium.org</owner>
   <owner>igorcov@chromium.org</owner>
   <summary>
@@ -37324,6 +37328,17 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.InvalidDevicePolicyFilesStatus"
+    enum="InvalidDevicePolicyFilesStatus" expires_after="M83">
+  <owner>emaxx@chromium.org</owner>
+  <owner>igorcov@chromium.org</owner>
+  <owner>vsavu@chromium.org</owner>
+  <summary>
+    Chrome OS only. Result of checking if device policy files are valid when
+    reading the device policy data.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.IOSPolicies" units="units">
   <owner>mnissler@chromium.org</owner>
   <summary>
@@ -80935,6 +80950,42 @@
   </summary>
 </histogram>
 
+<histogram name="Net.QuicSession.HeaderCompressionRatioHpackReceived" units="%"
+    expires_after="2020-10-08">
+  <owner>bnc@chromium.org</owner>
+  <owner>src/net/quic/OWNERS</owner>
+  <summary>
+    Header compression ratio as percentage for received headers using HPACK.
+  </summary>
+</histogram>
+
+<histogram name="Net.QuicSession.HeaderCompressionRatioHpackSent" units="%"
+    expires_after="2020-10-08">
+  <owner>bnc@chromium.org</owner>
+  <owner>src/net/quic/OWNERS</owner>
+  <summary>
+    Header compression ratio as percentage for sent headers using HPACK.
+  </summary>
+</histogram>
+
+<histogram name="Net.QuicSession.HeaderCompressionRatioQpackReceived" units="%"
+    expires_after="2020-10-08">
+  <owner>bnc@chromium.org</owner>
+  <owner>src/net/quic/OWNERS</owner>
+  <summary>
+    Header compression ratio as percentage for received headers using QPACK.
+  </summary>
+</histogram>
+
+<histogram name="Net.QuicSession.HeaderCompressionRatioQpackSent" units="%"
+    expires_after="2020-10-08">
+  <owner>bnc@chromium.org</owner>
+  <owner>src/net/quic/OWNERS</owner>
+  <summary>
+    Header compression ratio as percentage for sent headers using QPACK.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicSession.HeadersHOLBlockedTime" units="Milliseconds"
     expires_after="2018-05-09">
   <obsolete>
@@ -115846,42 +115897,6 @@
   </summary>
 </histogram>
 
-<histogram name="QuicSession.HeaderCompressionRatioHpackReceived" units="%"
-    expires_after="2020-10-08">
-  <owner>bnc@chromium.org</owner>
-  <owner>src/net/quic/OWNERS</owner>
-  <summary>
-    Header compression ratio as percentage for received headers using HPACK.
-  </summary>
-</histogram>
-
-<histogram name="QuicSession.HeaderCompressionRatioHpackSent" units="%"
-    expires_after="2020-10-08">
-  <owner>bnc@chromium.org</owner>
-  <owner>src/net/quic/OWNERS</owner>
-  <summary>
-    Header compression ratio as percentage for sent headers using HPACK.
-  </summary>
-</histogram>
-
-<histogram name="QuicSession.HeaderCompressionRatioQpackReceived" units="%"
-    expires_after="2020-10-08">
-  <owner>bnc@chromium.org</owner>
-  <owner>src/net/quic/OWNERS</owner>
-  <summary>
-    Header compression ratio as percentage for received headers using QPACK.
-  </summary>
-</histogram>
-
-<histogram name="QuicSession.HeaderCompressionRatioQpackSent" units="%"
-    expires_after="2020-10-08">
-  <owner>bnc@chromium.org</owner>
-  <owner>src/net/quic/OWNERS</owner>
-  <summary>
-    Header compression ratio as percentage for sent headers using QPACK.
-  </summary>
-</histogram>
-
 <histogram name="QuicSession.Qpack.HeaderListCountWhenBlockedStreamLimited"
     units="count" expires_after="2020-10-08">
   <owner>bnc@chromium.org</owner>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index da8707e..c52af632 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -630,9 +630,10 @@
     'platform': 'win',
     'target_bits': 32,
     'dimension': {
-      'pool': 'chrome.tests.perf',
-      'os': 'Windows-2008ServerR2-SP1',
-      'gpu': '102b:0532'
+        'gpu': '102b:0532-6.1.7600.16385',
+        'os': 'Windows-2008ServerR2-SP1',
+        'pool': 'chrome.tests.perf',
+        'synthetic_product_name': 'PowerEdge R210 II (Dell Inc.)',
     },
   },
   'Win 7 Nvidia GPU Perf': {
diff --git a/tools/perf/core/results_processor/command_line.py b/tools/perf/core/results_processor/command_line.py
index 5a6ece3..e5c51fb 100644
--- a/tools/perf/core/results_processor/command_line.py
+++ b/tools/perf/core/results_processor/command_line.py
@@ -20,7 +20,7 @@
 
 
 # These formats are always handled natively, and never handed over to Telemetry.
-HANDLED_NATIVELY = ['none', 'json-test-results']
+HANDLED_NATIVELY = ['none', 'json-test-results', 'histograms', 'html', 'csv']
 
 
 def ArgumentParser(standalone=False, legacy_formats=None):
@@ -131,25 +131,11 @@
   else:
     chosen_formats = ['html']
 
-  # The following is a temporary hack to switch on native handling of output
-  # formats for several benchmarks. This is an experiment to make sure
-  # that Results Processor is ready to handle output on all platforms.
-  # TODO(crbug.com/981349): Remove this after experiment is finished (i.e.
-  # all formats are considered HANDLED_NATIVELY).
-  force_native = False
-  if 'positional_args' in options.__dict__ and options.positional_args:
-    benchmark_name = options.positional_args[0]
-    if benchmark_name in ['power.desktop', 'startup.mobile', 'jetstream2',
-                          'octane', 'blink_perf.css']:
-      force_native = True
-      logging.warning('Force native handling of output formats for %s',
-                      benchmark_name)
-
   options.output_formats = []
   for output_format in chosen_formats:
     if output_format == 'none':
       continue
-    elif standalone or output_format in HANDLED_NATIVELY or force_native:
+    elif standalone or output_format in HANDLED_NATIVELY:
       options.output_formats.append(output_format)
     else:
       options.legacy_output_formats.append(output_format)
diff --git a/tools/perf/core/results_processor/command_line_unittest.py b/tools/perf/core/results_processor/command_line_unittest.py
index f67154c..5bf0ac1 100644
--- a/tools/perf/core/results_processor/command_line_unittest.py
+++ b/tools/perf/core/results_processor/command_line_unittest.py
@@ -124,10 +124,9 @@
     self.assertEqual(options.upload_bucket, 'some-special-bucket')
 
   def testDefaultOutputFormat(self):
-    self.legacy_formats = ['html']
     options = self.ParseArgs([])
-    self.assertEqual(options.output_formats, [])
-    self.assertEqual(options.legacy_output_formats, ['html'])
+    self.assertEqual(options.output_formats, ['html'])
+    self.assertEqual(options.legacy_output_formats, [])
 
   def testUnkownOutputFormatRaises(self):
     with self.assertRaises(SystemExit):
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index 5bcc620..b0ae539 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -39,8 +39,6 @@
     TYPE_HIGHLIGHTED,  // Performs an action when selected, and has a different
                        // colored background. When placed at the bottom, the
                        // background matches the menu's rounded corners.
-    TYPE_TITLE,        // Plain text that does not perform any action when
-                       // selected.
   };
 
   MenuModel();
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index aa1c2f05..0aeeb3e 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -17,10 +17,6 @@
 
 const int kSeparatorId = -1;
 
-// Text items are rendered as enabled but are non-interactive with no actions
-// and cannot be highlighted.
-const int kTitleId = -2;
-
 ////////////////////////////////////////////////////////////////////////////////
 // SimpleMenuModel::Delegate, public:
 
@@ -146,10 +142,6 @@
   AppendItem(std::move(item));
 }
 
-void SimpleMenuModel::AddTitle(const base::string16& label) {
-  AppendItem(Item(kTitleId, TYPE_TITLE, label));
-}
-
 void SimpleMenuModel::AddSeparator(MenuSeparatorType separator_type) {
   if (items_.empty()) {
     if (separator_type == NORMAL_SEPARATOR) {
@@ -468,9 +460,6 @@
 
 bool SimpleMenuModel::IsEnabledAt(int index) const {
   int command_id = GetCommandIdAt(index);
-  if (command_id == kTitleId)
-    return false;
-
   if (!delegate_ || command_id == kSeparatorId || GetButtonMenuItemAt(index))
     return items_[ValidateItemIndex(index)].enabled;
 
@@ -480,8 +469,7 @@
 
 bool SimpleMenuModel::IsVisibleAt(int index) const {
   int command_id = GetCommandIdAt(index);
-  if (!delegate_ || command_id == kSeparatorId || command_id == kTitleId ||
-      GetButtonMenuItemAt(index))
+  if (!delegate_ || command_id == kSeparatorId || GetButtonMenuItemAt(index))
     return items_[ValidateItemIndex(index)].visible;
 
   return delegate_->IsCommandIdVisible(command_id) &&
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index e51bcb4..3cc9d68 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -107,7 +107,6 @@
   void AddHighlightedItemWithIcon(int command_id,
                                   const base::string16& label,
                                   const gfx::ImageSkia& icon);
-  void AddTitle(const base::string16& label);
 
   // Adds a separator of the specified type to the model.
   // - Adding a separator after another separator is always invalid if they
diff --git a/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js b/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
index a6428db7..ca6debc 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_xf_elements_unittest.js
@@ -10,7 +10,71 @@
       '<xf-display-panel id="test-xf-display-panel"></xf-display-panel>';
 }
 
-async function testFilesDisplayPanel(done) {
+async function testDisplayPanelAttachPanel(done) {
+  // Get the host display panel container element.
+  /** @type {!DisplayPanel|!Element} */
+  const displayPanel = assert(document.querySelector('#test-xf-display-panel'));
+
+  // Test create/attach/remove sequences.
+  // Create a progress panel item.
+  let progressPanel = displayPanel.createPanelItem('testpanel');
+  progressPanel.panelType = progressPanel.panelTypeProgress;
+
+  // Attach the panel to it's host display panel.
+  displayPanel.attachPanelItem(progressPanel);
+
+  // Verify the panel is attached to the document.
+  assertTrue(!!progressPanel.isConnected);
+
+  // Remove the panel item.
+  displayPanel.removePanelItem(progressPanel);
+
+  // Verify the panel item is not attached to the document.
+  assertFalse(progressPanel.isConnected);
+
+  // Create a progress panel item.
+  progressPanel = displayPanel.createPanelItem('testpanel2');
+  progressPanel.panelType = progressPanel.panelTypeProgress;
+
+  // Remove the panel item.
+  displayPanel.removePanelItem(progressPanel);
+
+  // Try to attach the removed panel to it's host display panel.
+  displayPanel.attachPanelItem(progressPanel);
+
+  // Verify the panel is not attached to the document.
+  assertFalse(progressPanel.isConnected);
+
+  done();
+}
+
+async function testDisplayPanelChangingPanelTypes(done) {
+  // Get the host display panel container element.
+  /** @type {!DisplayPanel|!Element} */
+  const displayPanel = assert(document.querySelector('#test-xf-display-panel'));
+  const panelItem = displayPanel.addPanelItem('testpanel');
+  panelItem.panelType = panelItem.panelTypeProgress;
+
+  // Verify the panel item indicator is progress.
+  assertTrue(
+      panelItem.indicator === 'progress',
+      'Wrong panel indicator, got ' + panelItem.indicator);
+
+  // Change the panel item to an error panel.
+  panelItem.panelType = panelItem.panelTypeError;
+
+  // Verify the panel item indicator is set to error.
+  assertTrue(
+      panelItem.indicator === 'status',
+      'Wrong panel indicator, got ' + panelItem.indicator);
+  assertTrue(
+      panelItem.status === 'failure',
+      'Wrong panel status, got ' + panelItem.status);
+
+  done();
+}
+
+async function testFilesDisplayPanelSummaryPanel(done) {
   // Get the host display panel container element.
   /** @type {!DisplayPanel|!Element} */
   const displayPanel = assert(document.querySelector('#test-xf-display-panel'));
@@ -68,28 +132,5 @@
   summaryPanelItem = summaryContainer.querySelector('xf-panel-item');
   assertTrue(summaryPanelItem.panelType === summaryPanelItem.panelTypeSummary);
 
-  // Test create/attach/remove sequences.
-  // Create a progress panel item.
-  progressPanel = displayPanel.createPanelItem('testpanel2');
-  progressPanel.panelType = progressPanel.panelTypeProgress;
-  // Attach the panel to it's host display panel.
-  displayPanel.attachPanelItem(progressPanel);
-  // Verify the panel is attached to the document.
-  assertTrue(!!progressPanel.parentNode);
-  // Remove the panel item.
-  displayPanel.removePanelItem(progressPanel);
-  // Verify the panel item is not attached to the document.
-  assertTrue(progressPanel.parentNode === null);
-
-  // Create a progress panel item.
-  progressPanel = displayPanel.createPanelItem('testpanel3');
-  progressPanel.panelType = progressPanel.panelTypeProgress;
-  // Remove the panel item.
-  displayPanel.removePanelItem(progressPanel);
-  // Try to attach the removed panel to it's host display panel.
-  displayPanel.attachPanelItem(progressPanel);
-  // Verify the panel is not attached to the document.
-  assertTrue(progressPanel.parentNode === null);
-
   done();
 }
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
index 779a1ea..0c0b9a88 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
@@ -445,6 +445,13 @@
   }
 
   /**
+   *  Getter for the progress indicator.
+   */
+  get indicator() {
+    return this.getAttribute('indicator');
+  }
+
+  /**
    * Setter to set the success/failure indication.
    * @param {string} status Status value being set.
    */
@@ -453,6 +460,13 @@
   }
 
   /**
+   *  Getter for the success/failure indication.
+   */
+  get status() {
+    return this.getAttribute('status');
+  }
+
+  /**
    * Setter to set the progress property, sent to any child indicator.
    * @param {string} progress Progress value being set.
    * @public
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 2b2eac6..93bb5833 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -126,14 +126,6 @@
 
 Object.freeze(DirectoryItemTreeBaseMethods);
 
-const TREE_ITEM_INNER_HTML = '<div class="tree-row">' +
-    ' <paper-ripple fit class="recenteringTouch"></paper-ripple>' +
-    ' <span class="expand-icon"></span>' +
-    ' <span class="icon"></span>' +
-    ' <span class="label entry-name"></span>' +
-    '</div>' +
-    '<div class="tree-children" role="group"></div>';
-
 ////////////////////////////////////////////////////////////////////////////////
 // TreeItem
 
@@ -223,7 +215,7 @@
    * @override
    */
   get labelElement() {
-    return this.firstElementChild.querySelector('.label');
+    return this.rowElement.querySelector('.label');
   }
 }
 
@@ -274,7 +266,6 @@
     this.onMetadataUpdateBound_ = undefined;
   }
 
-
   /**
    * The DirectoryEntry corresponding to this DirectoryItem. This may be
    * a dummy DirectoryEntry.
@@ -854,8 +845,6 @@
 
   /**
    * The DirectoryEntry corresponding to this DirectoryItem.
-   * @type {DirectoryEntry}
-   * @override
    */
   get entry() {
     return this.dirEntry_;
@@ -863,7 +852,6 @@
 
   /**
    * Sets the DirectoryEntry corresponding to this DirectoryItem.
-   * @param {DirectoryEntry} value The directory entry.
    */
   set entry(value) {
     this.dirEntry_ = value;
@@ -1522,26 +1510,23 @@
  * A TreeItem which represents a shortcut for Drive folder.
  * Shortcut items are displayed as top-level children of DirectoryTree.
  */
-class ShortcutItem extends cr.ui.TreeItem {
+class ShortcutItem extends TreeItem {
   /**
    * @param {!NavigationModelShortcutItem} modelItem NavigationModelItem of this
    *     volume.
    * @param {!DirectoryTree} tree Current tree, which contains this item.
    */
   constructor(modelItem, tree) {
-    super();
-    // Get the original label id defined by TreeItem, before overwriting
-    // prototype.
-    const labelId = this.labelElement.id;
+    super(modelItem.entry.name, tree);
     this.__proto__ = ShortcutItem.prototype;
 
-    this.parentTree_ = tree;
+    if (window.IN_TEST) {
+      this.setAttribute('dir-type', 'ShortcutItem');
+    }
+
     this.dirEntry_ = modelItem.entry;
     this.modelItem_ = modelItem;
 
-    this.innerHTML = TREE_ITEM_INNER_HTML;
-    this.labelElement.id = labelId;
-
     const icon = this.querySelector('.icon');
     icon.classList.add('item-icon');
     icon.setAttribute('volume-type-icon', 'shortcut');
@@ -1549,13 +1534,6 @@
     if (tree.contextMenuForRootItems) {
       this.setContextMenu_(tree.contextMenuForRootItems);
     }
-
-    this.label = modelItem.entry.name;
-
-    if (window.IN_TEST) {
-      this.setAttribute('dir-type', 'ShortcutItem');
-      this.setAttribute('entry-label', this.label);
-    }
   }
 
   /**
@@ -1637,18 +1615,21 @@
         });
   }
 
+  /**
+   * The DirectoryEntry corresponding to this DirectoryItem.
+   */
   get entry() {
     return this.dirEntry_;
   }
+
+  /**
+   * @type {!NavigationModelVolumeItem}
+   */
   get modelItem() {
     return this.modelItem_;
   }
-  get labelElement() {
-    return this.firstElementChild.querySelector('.label');
-  }
 }
 
-
 ////////////////////////////////////////////////////////////////////////////////
 // AndroidAppItem
 
@@ -1656,38 +1637,30 @@
  * A TreeItem representing an Android picker app. These Android app items are
  * shown as top-level volume entries of the DirectoryTree.
  */
-class AndroidAppItem extends cr.ui.TreeItem {
+class AndroidAppItem extends TreeItem {
   /**
    * @param {!NavigationModelAndroidAppItem} modelItem NavigationModelItem
    *     associated with this volume.
    * @param {!DirectoryTree} tree Directory tree.
    */
   constructor(modelItem, tree) {
-    super();
-    // Get the original label id defined by TreeItem, before overwriting
-    // prototype.
-    const labelId = this.labelElement.id;
+    super(modelItem.androidApp.name, tree);
     this.__proto__ = AndroidAppItem.prototype;
 
-    /** @private {!DirectoryTree} */
-    this.parentTree_ = tree;
+    if (window.IN_TEST) {
+      this.setAttribute('dir-type', 'AndroidAppItem');
+    }
 
-    /** @private {!NavigationModelAndroidAppItem} */
     this.modelItem_ = modelItem;
 
-    this.innerHTML = TREE_ITEM_INNER_HTML;
-    this.labelElement.id = labelId;
+    const icon = this.querySelector('.icon');
+    icon.classList.add('item-icon');
 
-    /** @public {string} */
-    this.label = modelItem.androidApp.name;
-
-    const appIcon = this.querySelector('.icon');
-    appIcon.classList.add('item-icon');
     if (modelItem.androidApp.iconSet) {
       const backgroundImage =
           util.iconSetToCSSBackgroundImageValue(modelItem.androidApp.iconSet);
       if (backgroundImage !== 'none') {
-        appIcon.setAttribute('style', 'background-image: ' + backgroundImage);
+        icon.setAttribute('style', 'background-image: ' + backgroundImage);
       }
     }
 
@@ -1727,40 +1700,32 @@
 /**
  * FakeItem is used by Recent and Linux files.
  */
-class FakeItem extends cr.ui.TreeItem {
+class FakeItem extends TreeItem {
   /**
    * @param {!VolumeManagerCommon.RootType} rootType root type.
    * @param {!NavigationModelFakeItem} modelItem
    * @param {!DirectoryTree} tree Current tree, which contains this item.
    */
   constructor(rootType, modelItem, tree) {
-    super();
-    // Get the original label id defined by TreeItem, before overwriting
-    // prototype.
-    const labelId = this.labelElement.id;
+    super(modelItem.label, tree);
     this.__proto__ = FakeItem.prototype;
 
     if (window.IN_TEST) {
       this.setAttribute('dir-type', 'FakeItem');
-      this.setAttribute('entry-label', modelItem.label);
     }
 
-    this.rootType_ = rootType;
-    this.parentTree_ = tree;
-    this.modelItem_ = modelItem;
-    this.dirEntry_ = modelItem.entry;
-    this.innerHTML = TREE_ITEM_INNER_HTML;
-    this.labelElement.id = labelId;
-    this.label = modelItem.label;
     this.directoryModel_ = tree.directoryModel;
+    this.dirEntry_ = modelItem.entry;
+    this.modelItem_ = modelItem;
+    this.rootType_ = rootType;
+
+    const icon = this.querySelector('.icon');
+    icon.classList.add('item-icon');
+    icon.setAttribute('root-type-icon', rootType);
 
     if (tree.disabledContextMenu) {
       cr.ui.contextMenuHandler.setContextMenu(this, tree.disabledContextMenu);
     }
-
-    const icon = queryRequiredElement('.icon', this);
-    icon.classList.add('item-icon');
-    icon.setAttribute('root-type-icon', rootType);
   }
 
   /**
@@ -1806,19 +1771,23 @@
   }
 
   /**
-   * FakeItem doesn't really have shared status/icon so we define here as no-op.
+   * FakeItem's do not have shared status/icon.
    */
   updateDriveSpecificIcons() {}
 
+  /**
+   * The DirectoryEntry corresponding to this DirectoryItem.
+   */
   get entry() {
     return this.dirEntry_;
   }
+
+  /**
+   * @type {!NavigationModelVolumeItem}
+   */
   get modelItem() {
     return this.modelItem_;
   }
-  get labelElement() {
-    return this.firstElementChild.querySelector('.label');
-  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index f58099f2..0fcca1e 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -200,7 +200,6 @@
       node_data->SetCheckedState(is_checked ? ax::mojom::CheckedState::kTrue
                                             : ax::mojom::CheckedState::kFalse);
     } break;
-    case TITLE:
     case NORMAL:
     case SEPARATOR:
     case EMPTY:
@@ -1140,9 +1139,7 @@
           : style::CONTEXT_MENU;
 
   style::TextStyle text_style = style::STYLE_PRIMARY;
-  if (type_ == Type::TITLE)
-    text_style = style::STYLE_PRIMARY;
-  else if (type_ == HIGHLIGHTED)
+  if (type_ == HIGHLIGHTED)
     text_style = style::STYLE_HIGHLIGHTED;
   else if (!GetEnabled())
     text_style = style::STYLE_DISABLED;
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 481075d..e129bed9 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -95,9 +95,8 @@
     HIGHLIGHTED,         // Performs an action when selected, and has a
                          // different colored background that merges with the
                          // menu's rounded corners when placed at the bottom.
-    TITLE,               // Title text, does not perform any action.
-    EMPTY,               // EMPTY is a special type for empty menus that is only
-                         // used internally.
+    EMPTY,  // EMPTY is a special type for empty menus that is only used
+            // internally.
   };
 
   // Where the menu should be drawn, above or below the bounds (when
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index 6128da1..09b72733 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -70,9 +70,6 @@
   base::Optional<MenuItemView::Type> type;
   ui::MenuModel::ItemType menu_type = model->GetTypeAt(model_index);
   switch (menu_type) {
-    case ui::MenuModel::TYPE_TITLE:
-      type = MenuItemView::TITLE;
-      break;
     case ui::MenuModel::TYPE_COMMAND:
     case ui::MenuModel::TYPE_BUTTON_ITEM:
       type = MenuItemView::NORMAL;
diff --git a/ui/views/controls/menu/menu_model_adapter_unittest.cc b/ui/views/controls/menu/menu_model_adapter_unittest.cc
index 17ce9a0..d48ec3d 100644
--- a/ui/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/ui/views/controls/menu/menu_model_adapter_unittest.cc
@@ -213,9 +213,6 @@
 
     // Check type.
     switch (model_item.type) {
-      case ui::MenuModel::TYPE_TITLE:
-        EXPECT_EQ(views::MenuItemView::TITLE, item->GetType());
-        break;
       case ui::MenuModel::TYPE_COMMAND:
         EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
         break;
@@ -290,9 +287,6 @@
 
     // Check type.
     switch (model_item.type) {
-      case ui::MenuModel::TYPE_TITLE:
-        EXPECT_EQ(views::MenuItemView::TITLE, item->GetType());
-        break;
       case ui::MenuModel::TYPE_COMMAND:
         EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
         break;
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 284d1bc8..68e8f369 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -67,6 +67,8 @@
     "public/navigation_controller.h",
     "public/navigation_observer.h",
     "public/profile.h",
+    "utility/content_utility_client_impl.cc",
+    "utility/content_utility_client_impl.h",
   ]
 
   configs += [
@@ -91,6 +93,7 @@
     "//content/public/child",
     "//content/public/common",
     "//content/public/common:service_names",
+    "//content/public/utility",
     "//net",
     "//net:net_resources",
     "//sandbox",
diff --git a/weblayer/app/content_main_delegate_impl.cc b/weblayer/app/content_main_delegate_impl.cc
index 287761e..70f804f 100644
--- a/weblayer/app/content_main_delegate_impl.cc
+++ b/weblayer/app/content_main_delegate_impl.cc
@@ -20,6 +20,7 @@
 #include "ui/base/ui_base_paths.h"
 #include "weblayer/browser/content_browser_client_impl.h"
 #include "weblayer/common/content_client_impl.h"
+#include "weblayer/utility/content_utility_client_impl.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/apk_assets.h"
@@ -181,4 +182,10 @@
   return browser_client_.get();
 }
 
+content::ContentUtilityClient*
+ContentMainDelegateImpl::CreateContentUtilityClient() {
+  utility_client_ = std::make_unique<ContentUtilityClientImpl>();
+  return utility_client_.get();
+}
+
 }  // namespace weblayer
diff --git a/weblayer/app/content_main_delegate_impl.h b/weblayer/app/content_main_delegate_impl.h
index 9db9f4a..31ea1cb 100644
--- a/weblayer/app/content_main_delegate_impl.h
+++ b/weblayer/app/content_main_delegate_impl.h
@@ -16,6 +16,7 @@
 namespace weblayer {
 class ContentClientImpl;
 class ContentBrowserClientImpl;
+class ContentUtilityClientImpl;
 
 class ContentMainDelegateImpl : public content::ContentMainDelegate {
  public:
@@ -29,12 +30,14 @@
       const std::string& process_type,
       const content::MainFunctionParams& main_function_params) override;
   content::ContentBrowserClient* CreateContentBrowserClient() override;
+  content::ContentUtilityClient* CreateContentUtilityClient() override;
 
  private:
   void InitializeResourceBundle();
 
   MainParams params_;
   std::unique_ptr<ContentBrowserClientImpl> browser_client_;
+  std::unique_ptr<ContentUtilityClientImpl> utility_client_;
   std::unique_ptr<ContentClientImpl> content_client_;
 
   DISALLOW_COPY_AND_ASSIGN(ContentMainDelegateImpl);
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index e2d8a093..824c690 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -85,6 +85,7 @@
 
   sources = [
     "browsertests_main.cc",
+    "ssl_browsertest.cc",
     "test_launcher_delegate_impl.cc",
     "test_launcher_delegate_impl.h",
     "weblayer_browser_test.cc",
diff --git a/weblayer/test/DEPS b/weblayer/test/DEPS
index f619579..5b71f4a 100644
--- a/weblayer/test/DEPS
+++ b/weblayer/test/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+content/public/common",
   "+content/public/test",
   "+net/test",
 ]
diff --git a/weblayer/test/browsertests_main.cc b/weblayer/test/browsertests_main.cc
index c0dae98..cb6921c 100644
--- a/weblayer/test/browsertests_main.cc
+++ b/weblayer/test/browsertests_main.cc
@@ -4,12 +4,33 @@
 
 #include "base/command_line.h"
 #include "base/test/launcher/test_launcher.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/network_service_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "weblayer/test/test_launcher_delegate_impl.h"
+#include "weblayer/utility/content_utility_client_impl.h"
 
 int main(int argc, char** argv) {
   base::CommandLine::Init(argc, argv);
   size_t parallel_jobs = base::NumParallelJobs();
+
+  // Set up a working test environment for the network service in case it's
+  // used. Only create this object in the utility process, so that its members
+  // don't interfere with other test objects in the browser process.
+  std::unique_ptr<content::NetworkServiceTestHelper>
+      network_service_test_helper;
+  if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kProcessType) == switches::kUtilityProcess) {
+    network_service_test_helper =
+        std::make_unique<content::NetworkServiceTestHelper>();
+    weblayer::ContentUtilityClientImpl::
+        SetNetworkBinderCreationCallbackForTests(base::BindRepeating(
+            [](content::NetworkServiceTestHelper* helper,
+               service_manager::BinderRegistry* registry) {
+              helper->RegisterNetworkBinders(registry);
+            },
+            network_service_test_helper.get()));
+  }
   weblayer::TestLauncherDelegateImpl launcher_delegate;
   return content::LaunchTests(&launcher_delegate, parallel_jobs, argc, argv);
 }
diff --git a/weblayer/test/ssl_browsertest.cc b/weblayer/test/ssl_browsertest.cc
new file mode 100644
index 0000000..191e662
--- /dev/null
+++ b/weblayer/test/ssl_browsertest.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 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 "weblayer/test/weblayer_browser_test.h"
+
+#include "base/files/file_path.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
+
+namespace weblayer {
+
+IN_PROC_BROWSER_TEST_F(WebLayerBrowserTest, Https) {
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.AddDefaultHandlers(
+      base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
+
+  net::EmbeddedTestServer https_server_mismatched(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server_mismatched.SetSSLConfig(
+      net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+  https_server_mismatched.AddDefaultHandlers(
+      base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
+
+  ASSERT_TRUE(https_server.Start());
+  ASSERT_TRUE(https_server_mismatched.Start());
+
+  // First navigate to an OK page.
+  GURL initial_url = https_server.GetURL("/simple_page.html");
+  ASSERT_EQ("127.0.0.1", initial_url.host());
+
+  NavigateAndWaitForCompletion(initial_url, shell());
+
+  // Now do a navigation that should result in an SSL error.
+  GURL url_with_mismatched_cert =
+      https_server_mismatched.GetURL("/simple_page.html");
+
+  NavigateAndWaitForFailure(url_with_mismatched_cert, shell());
+
+  // TODO(blundell): Adapt the testing of the interstitial appearing from
+  // //chrome's ssl_browsertest.cc:(1462-1465) once the interstitial
+  // functionality is brought up.
+}
+
+}  // namespace weblayer
diff --git a/weblayer/test/weblayer_browser_test_test.cc b/weblayer/test/weblayer_browser_test_test.cc
index 417dda7e..242e321 100644
--- a/weblayer/test/weblayer_browser_test_test.cc
+++ b/weblayer/test/weblayer_browser_test_test.cc
@@ -13,7 +13,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url = embedded_test_server()->GetURL("/simple_page.html");
 
-  NavigateAndWait(url, shell());
+  NavigateAndWaitForCompletion(url, shell());
 }
 
 }  // namespace weblayer
diff --git a/weblayer/test/weblayer_browser_test_utils.cc b/weblayer/test/weblayer_browser_test_utils.cc
index a6754b3..28b6437 100644
--- a/weblayer/test/weblayer_browser_test_utils.cc
+++ b/weblayer/test/weblayer_browser_test_utils.cc
@@ -19,11 +19,15 @@
 // Runs |closure| once |url| is successfully navigated to.
 class TestNavigationObserver : public NavigationObserver {
  public:
+  enum class NavigationEventToObserve { Completion, Failure };
+
   TestNavigationObserver(base::OnceClosure closure,
                          const GURL& url,
+                         NavigationEventToObserve event,
                          Shell* shell)
       : closure_(std::move(closure)),
         url_(url),
+        event_(event),
         browser_(shell->browser_controller()) {
     browser_->GetNavigationController()->AddObserver(this);
   }
@@ -35,23 +39,47 @@
  private:
   // NavigationObserver implementation:
   void NavigationCompleted(Navigation* navigation) override {
-    if (navigation->GetURL() == url_)
+    if (navigation->GetURL() == url_ &&
+        event_ == NavigationEventToObserve::Completion)
       std::move(closure_).Run();
   }
 
+  void NavigationFailed(Navigation* navigation) override {
+    if (navigation->GetURL() == url_ &&
+        event_ == NavigationEventToObserve::Failure) {
+      std::move(closure_).Run();
+    }
+  }
+
   base::OnceClosure closure_;
   const GURL url_;
+  NavigationEventToObserve event_;
   BrowserController* browser_;
 };
 
-}  // namespace
-
-void NavigateAndWait(const GURL& url, Shell* shell) {
+// Navigates to |url| in |shell| and waits for |event| to occur.
+void NavigateAndWaitForEvent(
+    const GURL& url,
+    Shell* shell,
+    TestNavigationObserver::NavigationEventToObserve event) {
   base::RunLoop run_loop;
-  TestNavigationObserver test_observer(run_loop.QuitClosure(), url, shell);
+  TestNavigationObserver test_observer(run_loop.QuitClosure(), url, event,
+                                       shell);
 
   shell->browser_controller()->GetNavigationController()->Navigate(url);
   run_loop.Run();
 }
 
+}  // namespace
+
+void NavigateAndWaitForCompletion(const GURL& url, Shell* shell) {
+  NavigateAndWaitForEvent(
+      url, shell, TestNavigationObserver::NavigationEventToObserve::Completion);
+}
+
+void NavigateAndWaitForFailure(const GURL& url, Shell* shell) {
+  NavigateAndWaitForEvent(
+      url, shell, TestNavigationObserver::NavigationEventToObserve::Failure);
+}
+
 }  // namespace weblayer
diff --git a/weblayer/test/weblayer_browser_test_utils.h b/weblayer/test/weblayer_browser_test_utils.h
index 65daf4ca..d87a1380 100644
--- a/weblayer/test/weblayer_browser_test_utils.h
+++ b/weblayer/test/weblayer_browser_test_utils.h
@@ -10,8 +10,11 @@
 namespace weblayer {
 class Shell;
 
-// Navigates |shell| to |url| and wait for successful navigation.
-void NavigateAndWait(const GURL& url, Shell* shell);
+// Navigates |shell| to |url| and wait for completed navigation.
+void NavigateAndWaitForCompletion(const GURL& url, Shell* shell);
+
+// Navigates |shell| to |url| and wait for failed navigation.
+void NavigateAndWaitForFailure(const GURL& url, Shell* shell);
 
 }  // namespace weblayer
 
diff --git a/weblayer/utility/DEPS b/weblayer/utility/DEPS
new file mode 100644
index 0000000..8ad521e
--- /dev/null
+++ b/weblayer/utility/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/public/utility",
+]
diff --git a/weblayer/utility/content_utility_client_impl.cc b/weblayer/utility/content_utility_client_impl.cc
new file mode 100644
index 0000000..d9d648e
--- /dev/null
+++ b/weblayer/utility/content_utility_client_impl.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 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 "weblayer/utility/content_utility_client_impl.h"
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/no_destructor.h"
+
+namespace weblayer {
+
+namespace {
+
+base::LazyInstance<ContentUtilityClientImpl::NetworkBinderCreationCallback>::
+    Leaky g_network_binder_creation_callback = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+void ContentUtilityClientImpl::SetNetworkBinderCreationCallbackForTests(
+    NetworkBinderCreationCallback callback) {
+  g_network_binder_creation_callback.Get() = std::move(callback);
+}
+
+ContentUtilityClientImpl::ContentUtilityClientImpl() {}
+
+ContentUtilityClientImpl::~ContentUtilityClientImpl() = default;
+
+void ContentUtilityClientImpl::RegisterNetworkBinders(
+    service_manager::BinderRegistry* registry) {
+  if (g_network_binder_creation_callback.Get())
+    g_network_binder_creation_callback.Get().Run(registry);
+}
+
+}  // namespace weblayer
diff --git a/weblayer/utility/content_utility_client_impl.h b/weblayer/utility/content_utility_client_impl.h
new file mode 100644
index 0000000..798edd4
--- /dev/null
+++ b/weblayer/utility/content_utility_client_impl.h
@@ -0,0 +1,36 @@
+// Copyright 2019 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 WEBLAYER_UTILITY_CONTENT_UTILITY_CLIENT_IMPL_H_
+#define WEBLAYER_UTILITY_CONTENT_UTILITY_CLIENT_IMPL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "content/public/utility/content_utility_client.h"
+
+namespace weblayer {
+
+class ContentUtilityClientImpl : public content::ContentUtilityClient {
+ public:
+  using NetworkBinderCreationCallback =
+      base::RepeatingCallback<void(service_manager::BinderRegistry*)>;
+
+  static void SetNetworkBinderCreationCallbackForTests(
+      NetworkBinderCreationCallback callback);
+
+  ContentUtilityClientImpl();
+  ~ContentUtilityClientImpl() override;
+
+  // content::ContentUtilityClient:
+  void RegisterNetworkBinders(
+      service_manager::BinderRegistry* registry) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ContentUtilityClientImpl);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_UTILITY_CONTENT_UTILITY_CLIENT_IMPL_H_