diff --git a/BUILD.gn b/BUILD.gn
index e57916c..6b43e6b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -871,8 +871,8 @@
     deps = [
       "//chrome",
       "//chrome/test/chromedriver",
+      "//components/chromeos_camera:jpeg_decode_accelerator_unittest",
       "//media:media_unittests",
-      "//media/gpu:jpeg_decode_accelerator_unittest",
       "//ppapi/examples/video_decode",
       "//sandbox/linux:chrome_sandbox",
       "//sandbox/linux:sandbox_linux_unittests",
@@ -885,6 +885,7 @@
 
     if (use_v4l2_codec || use_vaapi) {
       deps += [
+        "//components/chromeos_camera:jpeg_encode_accelerator_unittest",
         "//media/gpu:video_decode_accelerator_perf_tests",
         "//media/gpu:video_decode_accelerator_tests",
 
@@ -894,9 +895,6 @@
         "//media/gpu:video_encode_accelerator_unittest",
       ]
     }
-    if (use_vaapi) {
-      deps += [ "//media/gpu:jpeg_encode_accelerator_unittest" ]
-    }
   }
 }
 
diff --git a/DEPS b/DEPS
index 212fcab..f86b534a 100644
--- a/DEPS
+++ b/DEPS
@@ -133,11 +133,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'f5ca01a69ab6f427c515bb3b4a9748047f04cb13',
+  'skia_revision': 'dbded16fadde7924ad23687ca13ad9134a60e7f8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'aa52ef3fa846058455b64a82627cc9945e01381d',
+  'v8_revision': '71f4b565e3bac4f90f2fbcba860519a01d303018',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -145,11 +145,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': 'ebab670cb32dccde42bdf48a02816c44ee6246c7',
+  'angle_revision': '064b1625059a66f15240412e084f2948b2d24e1b',
   # 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': '0264d8e12603cb3ef63436237013b666abe31c2b',
+  'swiftshader_revision': '7a60e31e4a236c3c2296fbd9dea5538499b6ea47',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -200,7 +200,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '09e818b4de419d2aa15c6f1ef36e49fdf6176a9f',
+  'catapult_revision': '580e942266666fd5cb2de90aa04d266f02fbf7bc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -256,7 +256,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': 'f2803c4a7f58237aa0dd9d39ccc6dea362527b96',
+  'spv_tools_revision': '89fe836fe22c3e5c2a062ebeade012e2c2f0839b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -268,7 +268,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.
-  'shaderc_revision': '1defed750e1fe3b7f1947306ee4ea86c1562403b',
+  'shaderc_revision': '5cf297dfa77c0cd76cfdb4bf4c290c0408d0e958',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -806,7 +806,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '50881afd45b2b652ac73443f5706d7051d7660a7',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '052c9073a10d0e622cd5aa2920fe38e1f1036c46',
       'condition': 'checkout_linux',
   },
 
@@ -900,7 +900,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'e291f7a09f6733f6634fe077a228056fabee881e',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'c11e3156af2297f89a23c8db3f5e2323733ee556',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1184,7 +1184,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'dc65ac0b74e9da50bb09b74f09e75fc45205adc7',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '2e8d209fba4bd4cfcc66aaa0b243edafdff7a924',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1355,7 +1355,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'd8b9ed77cf5263616703cc9fc159361e64222093',
+    Var('webrtc_git') + '/src.git' + '@' + 'bf47f340ee2e11d297f6dc14f5d76abbe7c9a1e2',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d3fa0d1e938a9eabe9b6c002b481210b3879ee4e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cc1b74c2b7a7e1f7c81af629846808176e7a6019',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index a1dc875..8df833e 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2208,8 +2208,7 @@
     'clipboard': ['dcheng@chromium.org'],
     'codereview_settings': ['agable+watch@chromium.org'],
     'components_deps': ['blundell+watchlist@chromium.org',
-                        'droger+watchlist@chromium.org',
-                        'sdefresne+watchlist@chromium.org'],
+                        'droger+watchlist@chromium.org'],
     'compositor_animator': ['mdjones+watch@chromium.org'],
     'content_bluetooth': ['mattreynolds+watch@chromium.org',
                           'ortuno+watch@chromium.org'],
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0b03c11..74cf7da 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -752,8 +752,13 @@
     "task/lazy_task_runner.h",
     "task/post_task.cc",
     "task/post_task.h",
+    "task/promise/abstract_promise.cc",
+    "task/promise/abstract_promise.h",
     "task/promise/dependent_list.cc",
     "task/promise/dependent_list.h",
+    "task/promise/no_op_promise_executor.cc",
+    "task/promise/no_op_promise_executor.h",
+    "task/promise/small_unique_object.h",
     "task/scoped_set_task_priority_for_current_thread.cc",
     "task/scoped_set_task_priority_for_current_thread.h",
     "task/sequence_manager/associated_thread_id.cc",
@@ -2607,6 +2612,7 @@
     "task/common/task_annotator_unittest.cc",
     "task/lazy_task_runner_unittest.cc",
     "task/post_task_unittest.cc",
+    "task/promise/abstract_promise_unittest.cc",
     "task/promise/dependent_list_unittest.cc",
     "task/scoped_set_task_priority_for_current_thread_unittest.cc",
     "task/sequence_manager/atomic_flag_set_unittest.cc",
diff --git a/base/containers/any_internal.h b/base/containers/any_internal.h
index d42ad567..d4c16624 100644
--- a/base/containers/any_internal.h
+++ b/base/containers/any_internal.h
@@ -85,8 +85,8 @@
   };
 
   struct alignas(sizeof(void*)) InlineAlloc {
-    // Holds a T if small.
-    char bytes[sizeof(void*)];
+    // Holds a T if small. Tweaked to hold a promise executor inline.
+    char bytes[sizeof(void*) * 3];
 
     template <typename T>
     T& value_as() {
diff --git a/base/containers/any_internal_unittest.cc b/base/containers/any_internal_unittest.cc
index 2b237f7..5afb279 100644
--- a/base/containers/any_internal_unittest.cc
+++ b/base/containers/any_internal_unittest.cc
@@ -13,6 +13,8 @@
 struct OutOfLineStruct {
   void* one;
   void* two;
+  void* three;
+  void* four;
 };
 }  // namespace
 
@@ -26,7 +28,7 @@
       "std::unique_ptr<int> should be stored inline");
   static_assert(
       !AnyInternal::InlineStorageHelper<OutOfLineStruct>::kUseInlineStorage,
-      "A struct with two pointers should be stored out of line");
+      "A struct with four pointers should be stored out of line");
 }
 
 }  // namespace internal
diff --git a/base/debug/task_trace.h b/base/debug/task_trace.h
index d976624..4893b8d 100644
--- a/base/debug/task_trace.h
+++ b/base/debug/task_trace.h
@@ -15,8 +15,10 @@
 namespace base {
 namespace debug {
 
-// Provides a snapshot of which places in the code posted tasks with a FROM_HERE
-// that led to the TaskTrace() constructor call.
+// Provides a snapshot of which places in the code called
+// base::TaskRunner::PostTask() that led to the TaskTrace() constructor call.
+// Analogous to base::StackTrace, but for posted tasks rather than function
+// calls.
 //
 // Example usage:
 //   TaskTrace().Print();
diff --git a/base/task/promise/abstract_promise.cc b/base/task/promise/abstract_promise.cc
new file mode 100644
index 0000000..46b53c8c
--- /dev/null
+++ b/base/task/promise/abstract_promise.cc
@@ -0,0 +1,510 @@
+// 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/task/promise/abstract_promise.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace base {
+namespace internal {
+
+AbstractPromise::~AbstractPromise() {
+#if DCHECK_IS_ON()
+  CheckedAutoLock lock(GetCheckedLock());
+
+  DCHECK(!must_catch_ancestor_that_could_reject_ ||
+         passed_catch_responsibility_)
+      << "Promise chain ending at " << from_here_.ToString()
+      << " didn't have a catch for potentially rejecting promise here "
+      << must_catch_ancestor_that_could_reject_->from_here().ToString();
+
+  DCHECK(!this_must_catch_ || passed_catch_responsibility_)
+      << "Potentially rejecting promise at " << from_here_.ToString()
+      << " doesn't have a catch .";
+#endif
+}
+
+bool AbstractPromise::IsCanceled() const {
+  if (dependents_.IsCanceled())
+    return true;
+
+  const Executor* executor = GetExecutor();
+  return executor && executor->IsCancelled();
+}
+
+const AbstractPromise* AbstractPromise::FindNonCurriedAncestor() const {
+  const AbstractPromise* promise = this;
+  while (promise->IsResolvedWithPromise()) {
+    promise =
+        unique_any_cast<scoped_refptr<AbstractPromise>>(promise->value_).get();
+  }
+  return promise;
+}
+
+void AbstractPromise::AddAsDependentForAllPrerequisites() {
+  DCHECK(prerequisites_);
+
+  for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
+    node.dependent_node.dependent = this;
+
+    // If |node.prerequisite| was canceled then early out because
+    // |prerequisites_->prerequisite_list| will have been cleared.
+    if (!node.prerequisite->InsertDependentOnAnyThread(&node.dependent_node))
+      break;
+  }
+}
+
+bool AbstractPromise::InsertDependentOnAnyThread(DependentList::Node* node) {
+  scoped_refptr<AbstractPromise>& dependent = node->dependent;
+
+#if DCHECK_IS_ON()
+  {
+    CheckedAutoLock lock(GetCheckedLock());
+    node->dependent->MaybeInheritChecks(this);
+  }
+#endif
+
+  // If |dependents_| has been consumed (i.e. this promise has been resolved
+  // or rejected) then |node| may be ready to run now.
+  switch (dependents_.Insert(node)) {
+    case DependentList::InsertResult::SUCCESS:
+      break;
+
+    case DependentList::InsertResult::FAIL_PROMISE_RESOLVED:
+      dependent->OnPrerequisiteResolved();
+      break;
+
+    case DependentList::InsertResult::FAIL_PROMISE_REJECTED:
+      dependent->OnPrerequisiteRejected();
+      break;
+
+    case DependentList::InsertResult::FAIL_PROMISE_CANCELED:
+      return dependent->OnPrerequisiteCancelled();
+  }
+
+  return true;
+}
+
+#if DCHECK_IS_ON()
+
+// static
+CheckedLock& AbstractPromise::GetCheckedLock() {
+  static base::NoDestructor<CheckedLock> instance;
+  return *instance;
+}
+
+void AbstractPromise::DoubleMoveDetector::CheckForDoubleMoveErrors(
+    const base::Location& new_dependent_location,
+    Executor::ArgumentPassingType new_dependent_executor_type) {
+  switch (new_dependent_executor_type) {
+    case Executor::ArgumentPassingType::kNoCallback:
+      return;
+
+    case Executor::ArgumentPassingType::kNormal:
+      DCHECK(!dependent_move_only_promise_)
+          << "Can't mix move only and non-move only " << callback_type_
+          << "callback arguments for the same " << callback_type_
+          << " prerequisite. See " << new_dependent_location.ToString()
+          << " and " << dependent_move_only_promise_->ToString()
+          << " with common ancestor " << from_here_.ToString();
+      dependent_normal_promise_ =
+          std::make_unique<Location>(new_dependent_location);
+      return;
+
+    case Executor::ArgumentPassingType::kMove:
+      DCHECK(!dependent_move_only_promise_)
+          << "Can't have multiple move only " << callback_type_
+          << " callbacks for same " << callback_type_ << " prerequisite. See "
+          << new_dependent_location.ToString() << " and "
+          << dependent_move_only_promise_->ToString() << " with common "
+          << callback_type_ << " prerequisite " << from_here_.ToString();
+      DCHECK(!dependent_normal_promise_)
+          << "Can't mix move only and non-move only " << callback_type_
+          << " callback arguments for the same " << callback_type_
+          << " prerequisite. See " << new_dependent_location.ToString()
+          << " and " << dependent_normal_promise_->ToString() << " with common "
+          << callback_type_ << " prerequisite " << from_here_.ToString();
+      dependent_move_only_promise_ =
+          std::make_unique<Location>(new_dependent_location);
+      return;
+  }
+}
+
+void AbstractPromise::MaybeInheritChecks(AbstractPromise* prerequisite) {
+  if (!ancestor_that_could_resolve_) {
+    // Inherit |prerequisite|'s resolve ancestor if it doesn't have a resolve
+    // callback.
+    if (prerequisite->resolve_argument_passing_type_ ==
+        Executor::ArgumentPassingType::kNoCallback) {
+      ancestor_that_could_resolve_ = prerequisite->ancestor_that_could_resolve_;
+    }
+
+    // If |prerequisite| didn't have a resolve callback (but it's reject
+    // callback could resolve) or if
+    // |prerequisite->ancestor_that_could_resolve_| is null then assign
+    // |prerequisite->this_resolve_|.
+    if (!ancestor_that_could_resolve_ && prerequisite->executor_can_resolve_)
+      ancestor_that_could_resolve_ = prerequisite->this_resolve_;
+  }
+
+  if (!ancestor_that_could_reject_) {
+    // Inherit |prerequisite|'s reject ancestor if it doesn't have a Catch.
+    if (prerequisite->reject_argument_passing_type_ ==
+        Executor::ArgumentPassingType::kNoCallback) {
+      ancestor_that_could_reject_ = prerequisite->ancestor_that_could_reject_;
+    }
+
+    // If |prerequisite| didn't have a reject callback (but it's resolve
+    // callback could reject) or if
+    // |prerequisite->ancestor_that_could_resolve_| is null then assign
+    // |prerequisite->this_reject_|.
+    if (!ancestor_that_could_reject_ && prerequisite->executor_can_reject_)
+      ancestor_that_could_reject_ = prerequisite->this_reject_;
+  }
+
+  if (!must_catch_ancestor_that_could_reject_) {
+    // Inherit |prerequisite|'s must catch ancestor if it doesn't have a Catch.
+    if (prerequisite->reject_argument_passing_type_ ==
+        Executor::ArgumentPassingType::kNoCallback) {
+      must_catch_ancestor_that_could_reject_ =
+          prerequisite->must_catch_ancestor_that_could_reject_;
+    }
+
+    // If |prerequisite| didn't have a reject callback (but it's resolve
+    // callback could reject) or if
+    // |prerequisite->must_catch_ancestor_that_could_reject_| is null then
+    // assign |prerequisite->this_must_catch_|.
+    if (!must_catch_ancestor_that_could_reject_ &&
+        prerequisite->executor_can_reject_) {
+      must_catch_ancestor_that_could_reject_ = prerequisite->this_must_catch_;
+    }
+  }
+
+  if (ancestor_that_could_resolve_) {
+    ancestor_that_could_resolve_->CheckForDoubleMoveErrors(
+        from_here_, resolve_argument_passing_type_);
+  }
+
+  if (ancestor_that_could_reject_) {
+    ancestor_that_could_reject_->CheckForDoubleMoveErrors(
+        from_here_, reject_argument_passing_type_);
+  }
+
+  prerequisite->passed_catch_responsibility_ = true;
+}
+
+void AbstractPromise::PassCatchResponsibilityOntoDependentsForCurriedPromise(
+    DependentList::Node* dependent_list) {
+  CheckedAutoLock lock(GetCheckedLock());
+  if (!dependent_list)
+    return;
+
+  if (IsResolvedWithPromise()) {
+    for (DependentList::Node* node = dependent_list; node;
+         node = node->next.load(std::memory_order_relaxed)) {
+      node->dependent->MaybeInheritChecks(this);
+    }
+  }
+}
+
+AbstractPromise::LocationRef::LocationRef(const Location& from_here)
+    : from_here_(from_here) {}
+
+AbstractPromise::LocationRef::~LocationRef() = default;
+
+AbstractPromise::DoubleMoveDetector::DoubleMoveDetector(
+    const Location& from_here,
+    const char* callback_type)
+    : from_here_(from_here), callback_type_(callback_type) {}
+
+AbstractPromise::DoubleMoveDetector::~DoubleMoveDetector() = default;
+
+#endif
+
+const AbstractPromise::Executor* AbstractPromise::GetExecutor() const {
+  const SmallUniqueObject<Executor>* executor =
+      base::unique_any_cast<SmallUniqueObject<Executor>>(&value_);
+  if (!executor)
+    return nullptr;
+  return executor->get();
+}
+
+AbstractPromise::Executor::PrerequisitePolicy
+AbstractPromise::GetPrerequisitePolicy() {
+  Executor* executor = GetExecutor();
+  if (!executor) {
+    // If there's no executor it's because the promise has already run. We
+    // can't run again however. The only circumstance in which we expect
+    // GetPrerequisitePolicy() to be called after execution is when it was
+    // resolved with a promise.
+    DCHECK(IsResolvedWithPromise());
+    return Executor::PrerequisitePolicy::kNever;
+  }
+  return executor->GetPrerequisitePolicy();
+}
+
+void AbstractPromise::Execute() {
+  if (IsCanceled())
+    return;
+
+#if DCHECK_IS_ON()
+  // Clear |must_catch_ancestor_that_could_reject_| if we can catch it.
+  if (reject_argument_passing_type_ !=
+      Executor::ArgumentPassingType::kNoCallback) {
+    CheckedAutoLock lock(GetCheckedLock());
+    must_catch_ancestor_that_could_reject_ = nullptr;
+  }
+#endif
+
+  if (IsResolvedWithPromise()) {
+    bool settled = DispatchIfNonCurriedRootSettled();
+    DCHECK(settled);
+
+    prerequisites_->prerequisite_list.clear();
+    return;
+  }
+
+  DCHECK(GetExecutor()) << from_here_.ToString() << " value_ contains "
+                        << value_.type();
+
+  // This is likely to delete the executor.
+  GetExecutor()->Execute(this);
+
+  // We need to release any AdjacencyListNodes we own to prevent memory leaks
+  // due to refcount cycles.
+  if (prerequisites_ && !IsResolvedWithPromise())
+    prerequisites_->prerequisite_list.clear();
+}
+
+bool AbstractPromise::DispatchIfNonCurriedRootSettled() {
+  const AbstractPromise* curried_root = FindNonCurriedAncestor();
+  if (!curried_root->IsSettled())
+    return false;
+
+  if (curried_root->IsResolved()) {
+    OnResolvePostReadyDependents();
+  } else if (curried_root->IsRejected()) {
+    OnRejectPostReadyDependents();
+  } else {
+    DCHECK(curried_root->IsCanceled());
+    OnPrerequisiteCancelled();
+  }
+  return true;
+}
+
+void AbstractPromise::OnPrerequisiteResolved() {
+  if (IsResolvedWithPromise()) {
+    bool settled = DispatchIfNonCurriedRootSettled();
+    DCHECK(settled);
+    return;
+  }
+
+  switch (GetPrerequisitePolicy()) {
+    case Executor::PrerequisitePolicy::kAll:
+      if (prerequisites_->DecrementPrerequisiteCountAndCheckIfZero())
+        task_runner_->PostPromiseInternal(this, TimeDelta());
+      break;
+
+    case Executor::PrerequisitePolicy::kAny:
+      // PrerequisitePolicy::kAny should resolve immediately.
+      task_runner_->PostPromiseInternal(this, TimeDelta());
+      break;
+
+    case Executor::PrerequisitePolicy::kNever:
+      break;
+  }
+}
+
+void AbstractPromise::OnPrerequisiteRejected() {
+  task_runner_->PostPromiseInternal(this, TimeDelta());
+}
+
+bool AbstractPromise::OnPrerequisiteCancelled() {
+  switch (GetPrerequisitePolicy()) {
+    case Executor::PrerequisitePolicy::kAll:
+      // PrerequisitePolicy::kAll should cancel immediately.
+      OnCanceled();
+      return false;
+
+    case Executor::PrerequisitePolicy::kAny:
+      // PrerequisitePolicy::kAny should only cancel if all if it's
+      // pre-requisites have been canceled.
+      if (prerequisites_->DecrementPrerequisiteCountAndCheckIfZero()) {
+        OnCanceled();
+        return false;
+      }
+      return true;
+
+    case Executor::PrerequisitePolicy::kNever:
+      // If we we where resolved with a promise then we can't have had
+      // PrerequisitePolicy::kAny or PrerequisitePolicy::kNever before the
+      // executor was replaced with the curried promise, so pass on
+      // cancellation.
+      if (IsResolvedWithPromise())
+        OnCanceled();
+      return false;
+  }
+}
+
+void AbstractPromise::OnResolvePostReadyDependents() {
+  DependentList::Node* dependent_list = dependents_.ConsumeOnceForResolve();
+  dependent_list = NonThreadSafeReverseList(dependent_list);
+
+#if DCHECK_IS_ON()
+  PassCatchResponsibilityOntoDependentsForCurriedPromise(dependent_list);
+#endif
+
+  // Propagate resolve to dependents.
+  for (DependentList::Node* node = dependent_list; node;
+       node = node->next.load(std::memory_order_relaxed)) {
+    // We want to release |node->dependent| but we need to do so before
+    // we post a task to execute |dependent| on what might be another thread.
+    scoped_refptr<AbstractPromise> dependent = std::move(node->dependent);
+    dependent->OnPrerequisiteResolved();
+  }
+}
+
+void AbstractPromise::OnRejectPostReadyDependents() {
+  DependentList::Node* dependent_list = dependents_.ConsumeOnceForReject();
+  dependent_list = NonThreadSafeReverseList(dependent_list);
+
+#if DCHECK_IS_ON()
+  PassCatchResponsibilityOntoDependentsForCurriedPromise(dependent_list);
+#endif
+
+  // Propagate rejection to dependents. We always propagate rejection
+  // immediately.
+  for (DependentList::Node* node = dependent_list; node;
+       node = node->next.load(std::memory_order_relaxed)) {
+    // We want to release |node->dependent| but we need to do so before
+    // we post a task to execute |dependent| on what might be another thread.
+    scoped_refptr<AbstractPromise> dependent = std::move(node->dependent);
+    dependent->OnPrerequisiteRejected();
+  }
+}
+
+void AbstractPromise::OnCanceled() {
+  if (dependents_.IsCanceled() || dependents_.IsResolved() ||
+      dependents_.IsRejected()) {
+    return;
+  }
+
+#if DCHECK_IS_ON()
+  {
+    CheckedAutoLock lock(GetCheckedLock());
+    passed_catch_responsibility_ = true;
+  }
+#endif
+
+  DependentList::Node* dependent_list = dependents_.ConsumeOnceForCancel();
+
+  // Release all pre-requisites to prevent memory leaks
+  if (prerequisites_) {
+    for (AdjacencyListNode& node : prerequisites_->prerequisite_list) {
+      node.prerequisite = nullptr;
+    }
+  }
+
+  // Propagate cancellation to dependents.
+  while (dependent_list) {
+    scoped_refptr<AbstractPromise> dependent =
+        std::move(dependent_list->dependent);
+    dependent->OnPrerequisiteCancelled();
+    dependent_list = dependent_list->next.load(std::memory_order_relaxed);
+  }
+}
+
+void AbstractPromise::OnResolved() {
+#if DCHECK_IS_ON()
+  DCHECK(executor_can_resolve_) << from_here_.ToString();
+#endif
+  if (IsResolvedWithPromise()) {
+    scoped_refptr<AbstractPromise> curried_promise =
+        unique_any_cast<scoped_refptr<AbstractPromise>>(value_);
+
+    // This only happens for PostTasks that returned a promise.
+    if (!task_runner_)
+      task_runner_ = SequencedTaskRunnerHandle::Get();
+
+    if (!curried_promise->prerequisites_)
+      curried_promise->prerequisites_ = std::make_unique<AdjacencyList>();
+
+      // Throw away any existing dependencies and make |curried_promise| the
+      // only dependency of this promise.
+#if DCHECK_IS_ON()
+    {
+      CheckedAutoLock lock(GetCheckedLock());
+      ancestor_that_could_resolve_ = nullptr;
+      ancestor_that_could_reject_ = nullptr;
+    }
+#endif
+    prerequisites_->ResetWithSingleDependency(curried_promise);
+    AddAsDependentForAllPrerequisites();
+  } else {
+    OnResolvePostReadyDependents();
+  }
+}
+
+void AbstractPromise::OnRejected() {
+#if DCHECK_IS_ON()
+  DCHECK(executor_can_reject_) << from_here_.ToString();
+#endif
+  OnRejectPostReadyDependents();
+}
+
+// static
+DependentList::Node* AbstractPromise::NonThreadSafeReverseList(
+    DependentList::Node* list) {
+  DependentList::Node* prev = nullptr;
+  while (list) {
+    DependentList::Node* next = list->next.load(std::memory_order_relaxed);
+    list->next.store(prev, std::memory_order_relaxed);
+    prev = list;
+    list = next;
+  }
+  return prev;
+}
+
+AbstractPromise::AdjacencyListNode::AdjacencyListNode() = default;
+
+AbstractPromise::AdjacencyListNode::AdjacencyListNode(
+    scoped_refptr<AbstractPromise> promise)
+    : prerequisite(std::move(promise)) {}
+
+AbstractPromise::AdjacencyListNode::~AdjacencyListNode() = default;
+
+AbstractPromise::AdjacencyListNode::AdjacencyListNode(
+    AdjacencyListNode&& other) noexcept = default;
+
+AbstractPromise::AdjacencyList::AdjacencyList() = default;
+
+AbstractPromise::AdjacencyList::AdjacencyList(
+    scoped_refptr<AbstractPromise> prerequisite)
+    : prerequisite_list(1), action_prerequisite_count(1) {
+  prerequisite_list[0].prerequisite = std::move(prerequisite);
+}
+
+AbstractPromise::AdjacencyList::AdjacencyList(
+    std::vector<AdjacencyListNode> nodes)
+    : prerequisite_list(std::move(nodes)),
+      action_prerequisite_count(prerequisite_list.size()) {}
+
+AbstractPromise::AdjacencyList::~AdjacencyList() = default;
+
+bool AbstractPromise::AdjacencyList::
+    DecrementPrerequisiteCountAndCheckIfZero() {
+  return action_prerequisite_count.fetch_sub(1, std::memory_order_acq_rel) == 1;
+}
+
+void AbstractPromise::AdjacencyList::ResetWithSingleDependency(
+    scoped_refptr<AbstractPromise> prerequisite) {
+  prerequisite_list.clear();
+  prerequisite_list.push_back(AdjacencyListNode{std::move(prerequisite)});
+  action_prerequisite_count = 1;
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/abstract_promise.h b/base/task/promise/abstract_promise.h
new file mode 100644
index 0000000..6656229
--- /dev/null
+++ b/base/task/promise/abstract_promise.h
@@ -0,0 +1,471 @@
+// 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 BASE_TASK_PROMISE_ABSTRACT_PROMISE_H_
+#define BASE_TASK_PROMISE_ABSTRACT_PROMISE_H_
+
+#include <utility>
+
+#include "base/containers/unique_any.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
+#include "base/task/common/checked_lock.h"
+#include "base/task/promise/dependent_list.h"
+#include "base/task/promise/small_unique_object.h"
+#include "base/thread_annotations.h"
+
+namespace base {
+class TaskRunner;
+
+// std::variant, std::tuple and other templates can't contain void but they can
+// contain the empty type Void. This is the same idea as std::monospace.
+struct Void {};
+
+// Signals that a promise doesn't reject.  E.g. Promise<int, NoReject>
+struct NoReject {};
+
+// This enum is used to configure AbstractPromise's uncaught reject detection.
+// Usually not catching a reject reason is a coding error, but at times that can
+// become onerous. When that happens kCatchNotRequired should be used.
+enum class RejectPolicy {
+  kMustCatchRejection,
+  kCatchNotRequired,
+};
+
+// Internally Resolved<> is used to store the result of a promise callback that
+// resolved. This lets us disambiguate promises with the same resolve and reject
+// type.
+template <typename T>
+struct BASE_EXPORT Resolved {
+  using Type = T;
+
+  Resolved() = default;
+
+  template <typename... Args>
+  Resolved(Args&&... args) noexcept : value(std::forward<Args>(args)...) {}
+
+  T value;
+};
+
+template <>
+struct BASE_EXPORT Resolved<void> {
+  using Type = void;
+  Void value;
+};
+
+// Internally Rejected<> is used to store the result of a promise callback that
+// rejected. This lets us disambiguate promises with the same resolve and reject
+// type.
+template <typename T>
+struct BASE_EXPORT Rejected {
+  using Type = T;
+  T value;
+
+  Rejected() = default;
+
+  template <typename... Args>
+  Rejected(Args&&... args) noexcept : value(std::forward<Args>(args)...) {}
+};
+
+template <>
+struct BASE_EXPORT Rejected<void> {
+  using Type = void;
+  Void value;
+};
+
+namespace internal {
+
+// Internal promise representation, maintains a graph of dependencies and posts
+// promises as they become ready. In debug builds various sanity checks are
+// performed to catch common errors such as double move or forgetting to catch a
+// potential reject (NB this last check can be turned off with
+// RejectPolicy::kCatchNotRequired).
+class BASE_EXPORT AbstractPromise
+    : public RefCountedThreadSafe<AbstractPromise> {
+ public:
+  struct AdjacencyList;
+
+  template <typename ConstructType, typename DerivedExecutorType>
+  struct ConstructWith {};
+
+  template <typename ConstructType,
+            typename DerivedExecutorType,
+            typename... ExecutorArgs>
+  AbstractPromise(scoped_refptr<TaskRunner>&& task_runner,
+                  const Location& from_here,
+                  std::unique_ptr<AdjacencyList> prerequisites,
+                  RejectPolicy reject_policy,
+                  ConstructWith<ConstructType, DerivedExecutorType>,
+                  ExecutorArgs&&... executor_args) noexcept
+      : task_runner_(std::move(task_runner)),
+        from_here_(std::move(from_here)),
+        value_(in_place_type_t<SmallUniqueObject<Executor>>(),
+               in_place_type_t<DerivedExecutorType>(),
+               std::forward<ExecutorArgs>(executor_args)...),
+#if DCHECK_IS_ON()
+        reject_policy_(reject_policy),
+        resolve_argument_passing_type_(
+            GetExecutor()->ResolveArgumentPassingType()),
+        reject_argument_passing_type_(
+            GetExecutor()->RejectArgumentPassingType()),
+        executor_can_resolve_(GetExecutor()->CanResolve()),
+        executor_can_reject_(GetExecutor()->CanReject()),
+#endif
+        dependents_(ConstructType()),
+        prerequisites_(std::move(prerequisites)) {
+#if DCHECK_IS_ON()
+    {
+      CheckedAutoLock lock(GetCheckedLock());
+      if (executor_can_resolve_) {
+        this_resolve_ =
+            MakeRefCounted<DoubleMoveDetector>(from_here_, "resolve");
+      }
+
+      if (executor_can_reject_) {
+        this_reject_ = MakeRefCounted<DoubleMoveDetector>(from_here_, "reject");
+
+        if (reject_policy_ == RejectPolicy::kMustCatchRejection) {
+          this_must_catch_ = MakeRefCounted<LocationRef>(from_here_);
+        }
+      }
+    }
+#endif
+
+    if (prerequisites_)
+      AddAsDependentForAllPrerequisites();
+  }
+
+  AbstractPromise(const AbstractPromise&) = delete;
+  AbstractPromise& operator=(const AbstractPromise&) = delete;
+
+  const Location& from_here() const { return from_here_; }
+
+  bool IsCanceled() const;
+  bool IsRejected() const { return dependents_.IsRejected(); }
+  bool IsResolved() const { return dependents_.IsResolved(); }
+  bool IsSettled() const { return dependents_.IsSettled(); }
+
+  bool IsResolvedWithPromise() const {
+    return value_.type() ==
+           TypeId::From<scoped_refptr<internal::AbstractPromise>>();
+  }
+
+  const unique_any& value() const { return FindNonCurriedAncestor()->value_; }
+
+  // Moves the value from within T::value.
+  template <typename T>
+  auto TakeInnerValue() {
+    T* ptr = unique_any_cast<T>(&FindNonCurriedAncestor()->value_);
+    DCHECK(ptr);
+    return std::move(ptr->value);
+  }
+
+  // If this promise isn't curried, returns this. Otherwise follows the chain of
+  // currying until a non-curried promise is found.
+  const AbstractPromise* FindNonCurriedAncestor() const;
+
+  AbstractPromise* FindNonCurriedAncestor() {
+    return const_cast<AbstractPromise*>(
+        const_cast<const AbstractPromise*>(this)->FindNonCurriedAncestor());
+  }
+
+  // Sets the |value_| to |t|. The caller should call OnResolved() or
+  // OnRejected() afterwards.
+  template <typename T>
+  void emplace(T&& t) {
+    DCHECK(GetExecutor() != nullptr) << "Only valid to emplace once";
+    value_ = std::forward<T>(t);
+  }
+
+  // Unresolved promises have an executor which invokes one of the callbacks
+  // associated with the promise. Once the callback has been invoked the
+  // Executor is destroyed.
+  class BASE_EXPORT Executor {
+   public:
+    virtual ~Executor() {}
+
+    // Controls whether or not a promise should wait for its prerequisites
+    // before becoming eligible for execution.
+    enum class PrerequisitePolicy : uint8_t {
+      // Wait for all prerequisites to resolve (or any to reject) before
+      // becoming eligible for execution. If any prerequisites are canceled it
+      // will be canceled too.
+      kAll,
+
+      // Wait for any prerequisite to resolve or reject before becoming eligible
+      // for execution. If all prerequisites are canceled it will be canceled
+      // too.
+      kAny,
+
+      // Never become eligible for execution. Cancellation is ignored.
+      kNever,
+    };
+
+    // Returns the associated PrerequisitePolicy.
+    virtual PrerequisitePolicy GetPrerequisitePolicy() = 0;
+
+    // NB if there is both a resolve and a reject executor we require them to
+    // be both canceled at the same time.
+    virtual bool IsCancelled() const = 0;
+
+    // Describes an executor callback.
+    enum class ArgumentPassingType : uint8_t {
+      // No callback. E.g. the RejectArgumentPassingType in a promise with a
+      // resolve callback but no reject callback.
+      kNoCallback,
+
+      // Executor callback argument passed by value or by reference.
+      kNormal,
+
+      // Executor callback argument passed by r-value reference.
+      kMove,
+    };
+
+#if DCHECK_IS_ON()
+    // Returns details of the resolve and reject executor callbacks if any. This
+    // data is used to diagnose double moves and missing catches.
+    virtual ArgumentPassingType ResolveArgumentPassingType() const = 0;
+    virtual ArgumentPassingType RejectArgumentPassingType() const = 0;
+    virtual bool CanResolve() const = 0;
+    virtual bool CanReject() const = 0;
+#endif
+
+    // Invokes the associate callback for |promise|. If the callback was
+    // cancelled it should call |promise->OnCanceled()|. If the callback
+    // resolved it should store the resolve result via |promise->emplace()| and
+    // call |promise->OnResolved()|. If the callback was rejected it should
+    // store the reject result in |promise->state()| and call
+    // |promise->OnResolved()|.
+    // Caution the Executor will be destructed when |promise->state()| is
+    // written to.
+    virtual void Execute(AbstractPromise* promise) = 0;
+  };
+
+  // Signals that this promise was cancelled. If executor hasn't run yet, this
+  // will prevent it from running and cancels any dependent promises unless they
+  // have PrerequisitePolicy::kAny, in which case they will only be canceled if
+  // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or
+  // OnRejected() has already run, this does nothing.
+  void OnCanceled();
+
+  // Signals that |value_| now contains a resolve value. Dependent promises may
+  // scheduled for execution.
+  void OnResolved();
+
+  // Signals that |value_| now contains a reject value. Dependent promises may
+  // scheduled for execution.
+  void OnRejected();
+
+  struct BASE_EXPORT AdjacencyListNode {
+    AdjacencyListNode();
+    explicit AdjacencyListNode(scoped_refptr<AbstractPromise> prerequisite);
+    explicit AdjacencyListNode(AdjacencyListNode&& other) noexcept;
+    ~AdjacencyListNode();
+
+    scoped_refptr<AbstractPromise> prerequisite;
+    DependentList::Node dependent_node;
+  };
+
+  // This is separate from AbstractPromise to reduce the memory footprint of
+  // regular PostTask without promise chains.
+  struct BASE_EXPORT AdjacencyList {
+    AdjacencyList();
+    explicit AdjacencyList(scoped_refptr<AbstractPromise> prerequisite);
+    explicit AdjacencyList(std::vector<AdjacencyListNode> prerequisite_list);
+    ~AdjacencyList();
+
+    void ResetWithSingleDependency(scoped_refptr<AbstractPromise> prerequisite);
+
+    bool DecrementPrerequisiteCountAndCheckIfZero();
+
+    std::vector<AdjacencyListNode> prerequisite_list;
+
+    // PrerequisitePolicy::kAny waits for at most 1 resolve or N cancellations.
+    // PrerequisitePolicy::kAll waits for N resolves or at most 1 cancellation.
+    // PrerequisitePolicy::kNever doesn't use this.
+    std::atomic_int action_prerequisite_count;
+  };
+
+  const std::vector<AdjacencyListNode>* prerequisite_list() const {
+    if (!prerequisites_)
+      return nullptr;
+    return &prerequisites_->prerequisite_list;
+  }
+
+  // Returns the first and only prerequisite AbstractPromise.  It's an error to
+  // call this if the number of prerequisites isn't exactly one.
+  AbstractPromise* GetOnlyPrerequisite() const {
+    DCHECK(prerequisites_);
+    DCHECK_EQ(prerequisites_->prerequisite_list.size(), 1u);
+    return prerequisites_->prerequisite_list[0].prerequisite.get();
+  }
+
+  // Calls |RunExecutor()| or posts a task to do so if |from_here_| is not
+  // nullopt.
+  void Execute();
+
+ private:
+  friend base::RefCountedThreadSafe<AbstractPromise>;
+
+  NOINLINE ~AbstractPromise();
+
+  // Returns the associated Executor if there is one.
+  const Executor* GetExecutor() const;
+
+  Executor* GetExecutor() {
+    return const_cast<Executor*>(
+        const_cast<const AbstractPromise*>(this)->GetExecutor());
+  }
+
+  // With the exception of curried promises, this may only be called before the
+  // executor has run.
+  Executor::PrerequisitePolicy GetPrerequisitePolicy();
+
+  void AddAsDependentForAllPrerequisites();
+
+  // If the promise hasn't executed then |node| is added to the list. If it has
+  // and it was resolved or rejected then the corresponding promise is scheduled
+  // for execution if necessary. If this promise was canceled this is a NOP.
+  // Returns false if this operation failed because this promise became canceled
+  // as a result of adding a dependency on a canceled |node|.
+  bool InsertDependentOnAnyThread(DependentList::Node* node);
+
+  // Checks if the promise is now ready to be executed and if so posts it on the
+  // given task runner.
+  void OnPrerequisiteResolved();
+
+  // Schedules the promise for execution.
+  void OnPrerequisiteRejected();
+
+  // Returns true if we are still potentially eligible to run despite the
+  // cancellation.
+  bool OnPrerequisiteCancelled();
+
+  // This promise was resolved, post any dependent promises that are now ready
+  // as a result.
+  void OnResolvePostReadyDependents();
+
+  // This promise was rejected, post any dependent promises that are now ready
+  // as a result.
+  void OnRejectPostReadyDependents();
+
+  // Reverses |list| so dependents can be dispatched in the order they where
+  // added. Assumes no other thread is accessing |list|.
+  static DependentList::Node* NonThreadSafeReverseList(
+      DependentList::Node* list);
+
+  // Finds the non-curried root, and if settled ready dependents are posted.
+  // Returns true if the non-curried root was settled.
+  bool DispatchIfNonCurriedRootSettled();
+
+  scoped_refptr<TaskRunner> task_runner_;
+
+  const Location from_here_;
+
+  // To save memory |value_| contains SmallUniqueObject<Executor> (which is
+  // stored inline) before it has run and afterwards it contains one of:
+  // * Resolved<T>
+  // * Rejected<T>
+  // * scoped_refptr<AbstractPromise> (for curried promises - i.e. a promise
+  //   which is resolved with a promise).
+  unique_any value_;
+
+#if DCHECK_IS_ON()
+  void MaybeInheritChecks(AbstractPromise* source)
+      EXCLUSIVE_LOCKS_REQUIRED(GetCheckedLock());
+
+  // Does nothing if this promise wasn't resolved by a promise.
+  void PassCatchResponsibilityOntoDependentsForCurriedPromise(
+      DependentList::Node* dependent_list);
+
+  // Controls how we deal with unhandled rejection.
+  const RejectPolicy reject_policy_;
+
+  // Cached because we need to access these values after the Executor they came
+  // from has gone away.
+  const Executor::ArgumentPassingType resolve_argument_passing_type_;
+  const Executor::ArgumentPassingType reject_argument_passing_type_;
+  const bool executor_can_resolve_;
+  const bool executor_can_reject_;
+
+  // Whether responsibility for catching rejected promise has been passed on to
+  // this promise's dependents.
+  bool passed_catch_responsibility_ GUARDED_BY(GetCheckedLock()) = false;
+
+  static CheckedLock& GetCheckedLock();
+
+  // Used to avoid refcounting cycles.
+  class BASE_EXPORT LocationRef : public RefCountedThreadSafe<LocationRef> {
+   public:
+    explicit LocationRef(const Location& from_here);
+
+    const Location& from_here() const { return from_here_; }
+
+   private:
+    Location from_here_;
+
+    friend class RefCountedThreadSafe<LocationRef>;
+    ~LocationRef();
+  };
+
+  // For catching missing catches.
+  scoped_refptr<LocationRef> must_catch_ancestor_that_could_reject_
+      GUARDED_BY(GetCheckedLock());
+
+  // Used to supply all child nodes with a single LocationRef.
+  scoped_refptr<LocationRef> this_must_catch_ GUARDED_BY(GetCheckedLock());
+
+  class BASE_EXPORT DoubleMoveDetector
+      : public RefCountedThreadSafe<DoubleMoveDetector> {
+   public:
+    DoubleMoveDetector(const Location& from_here, const char* callback_type);
+
+    void CheckForDoubleMoveErrors(
+        const base::Location& new_dependent_location,
+        Executor::ArgumentPassingType new_dependent_executor_type);
+
+   private:
+    const Location from_here_;
+    const char* callback_type_;
+    std::unique_ptr<Location> dependent_move_only_promise_;
+    std::unique_ptr<Location> dependent_normal_promise_;
+
+    friend class RefCountedThreadSafe<DoubleMoveDetector>;
+    ~DoubleMoveDetector();
+  };
+
+  // Used to supply all child nodes with a single DoubleMoveDetector.
+  scoped_refptr<DoubleMoveDetector> this_resolve_ GUARDED_BY(GetCheckedLock());
+
+  // Used to supply all child nodes with a single DoubleMoveDetector.
+  scoped_refptr<DoubleMoveDetector> this_reject_ GUARDED_BY(GetCheckedLock());
+
+  // Validates that the value of this promise, or the value of the closest
+  // ancestor that can resolve if this promise can't resolve, is not
+  // double-moved.
+  scoped_refptr<DoubleMoveDetector> ancestor_that_could_resolve_
+      GUARDED_BY(GetCheckedLock());
+
+  // Validates that the value of this promise, or the value of the closest
+  // ancestor that can reject if this promise can't reject, is not
+  // double-moved.
+  scoped_refptr<DoubleMoveDetector> ancestor_that_could_reject_
+      GUARDED_BY(GetCheckedLock());
+#endif
+
+  // List of promises which are dependent on this one.
+  DependentList dependents_;
+
+  // Details of any promises this promise is dependent on. If there are none
+  // |prerequisites_| will be null. This is a space optimization for the common
+  // case of a non-chained PostTask.
+  std::unique_ptr<AdjacencyList> prerequisites_;
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_ABSTRACT_PROMISE_H_
diff --git a/base/task/promise/abstract_promise_unittest.cc b/base/task/promise/abstract_promise_unittest.cc
new file mode 100644
index 0000000..5da9fbf
--- /dev/null
+++ b/base/task/promise/abstract_promise_unittest.cc
@@ -0,0 +1,2151 @@
+// 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/task/promise/abstract_promise.h"
+
+#include "base/test/bind_test_util.h"
+#include "base/test/do_nothing_promise.h"
+#include "base/test/gtest_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+using ArgumentPassingType =
+    base::internal::AbstractPromise::Executor::ArgumentPassingType;
+
+using PrerequisitePolicy =
+    base::internal::AbstractPromise::Executor::PrerequisitePolicy;
+
+namespace base {
+namespace internal {
+
+class TestExecutor : public AbstractPromise::Executor {
+ public:
+  TestExecutor(PrerequisitePolicy policy,
+#if DCHECK_IS_ON()
+               ArgumentPassingType resolve_executor_type,
+               ArgumentPassingType reject_executor_type,
+               bool can_resolve,
+               bool can_reject,
+#endif
+               base::OnceCallback<void(AbstractPromise*)> callback)
+      : callback_(std::move(callback)),
+#if DCHECK_IS_ON()
+        resolve_argument_passing_type_(resolve_executor_type),
+        reject_argument_passing_type_(reject_executor_type),
+        resolve_flags_(can_resolve + (can_reject << 1)),
+#endif
+        policy_(policy) {
+  }
+
+#if DCHECK_IS_ON()
+  ArgumentPassingType ResolveArgumentPassingType() const override {
+    return resolve_argument_passing_type_;
+  }
+
+  ArgumentPassingType RejectArgumentPassingType() const override {
+    return reject_argument_passing_type_;
+  }
+
+  bool CanResolve() const override { return resolve_flags_ & 1; }
+
+  bool CanReject() const override { return resolve_flags_ & 2; }
+#endif
+
+  PrerequisitePolicy GetPrerequisitePolicy() override { return policy_; }
+
+  bool IsCancelled() const override { return false; }
+
+  void Execute(AbstractPromise* p) override { std::move(callback_).Run(p); }
+
+ private:
+  base::OnceCallback<void(AbstractPromise*)> callback_;
+#if DCHECK_IS_ON()
+  const ArgumentPassingType resolve_argument_passing_type_;
+  const ArgumentPassingType reject_argument_passing_type_;
+  // On 32 bit platform we need to pack to fit in the space requirement of 3x
+  // void*.
+  uint8_t resolve_flags_;
+#endif
+  const PrerequisitePolicy policy_;
+};
+
+class AbstractPromiseTest : public testing::Test {
+ public:
+  enum class CallbackResultType : uint8_t {
+    kNoCallback,
+    kCanResolve,
+    kCanReject,
+    kCanResolveOrReject,
+  };
+
+  struct PromiseSettings {
+    PromiseSettings(
+        Location from_here,
+        std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites)
+        : from_here(from_here), prerequisites(std::move(prerequisites)) {}
+
+    Location from_here;
+
+    std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites;
+
+    PrerequisitePolicy prerequisite_policy =
+        AbstractPromise::Executor::PrerequisitePolicy::kAll;
+
+    bool executor_can_resolve = true;
+
+    bool executor_can_reject = false;
+
+    ArgumentPassingType resolve_executor_type = ArgumentPassingType::kNormal;
+
+    ArgumentPassingType reject_executor_type = ArgumentPassingType::kNoCallback;
+
+    RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection;
+
+    base::OnceCallback<void(AbstractPromise*)> callback;
+
+    scoped_refptr<TaskRunner> task_runner = ThreadTaskRunnerHandle::Get();
+  };
+
+  class PromiseSettingsBuilder {
+   public:
+    PromiseSettingsBuilder(
+        Location from_here,
+        std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites)
+        : settings(from_here, std::move(prerequisites)) {}
+
+    PromiseSettingsBuilder& With(PrerequisitePolicy prerequisite_policy) {
+      settings.prerequisite_policy = prerequisite_policy;
+      return *this;
+    }
+
+    PromiseSettingsBuilder& With(const scoped_refptr<TaskRunner>& task_runner) {
+      settings.task_runner = task_runner;
+      return *this;
+    }
+
+    PromiseSettingsBuilder& With(RejectPolicy reject_policy) {
+      settings.reject_policy = reject_policy;
+      return *this;
+    }
+
+    PromiseSettingsBuilder& With(
+        base::OnceCallback<void(AbstractPromise*)> callback) {
+      settings.callback = std::move(callback);
+      return *this;
+    }
+
+    PromiseSettingsBuilder& With(CallbackResultType callback_result_type) {
+      switch (callback_result_type) {
+        case CallbackResultType::kNoCallback:
+          settings.executor_can_resolve = false;
+          settings.executor_can_reject = false;
+          break;
+        case CallbackResultType::kCanResolve:
+          settings.executor_can_resolve = true;
+          settings.executor_can_reject = false;
+          break;
+        case CallbackResultType::kCanReject:
+          settings.executor_can_resolve = false;
+          settings.executor_can_reject = true;
+          break;
+        case CallbackResultType::kCanResolveOrReject:
+          settings.executor_can_resolve = true;
+          settings.executor_can_reject = true;
+          break;
+      };
+      return *this;
+    }
+
+    PromiseSettingsBuilder& WithResolve(
+        ArgumentPassingType resolve_executor_type) {
+      settings.resolve_executor_type = resolve_executor_type;
+      return *this;
+    }
+
+    PromiseSettingsBuilder& WithReject(
+        ArgumentPassingType reject_executor_type) {
+      settings.reject_executor_type = reject_executor_type;
+      return *this;
+    }
+
+    operator scoped_refptr<AbstractPromise>() {
+      return subtle::AdoptRefIfNeeded(
+          new AbstractPromise(
+              std::move(settings.task_runner), settings.from_here,
+              std::move(settings.prerequisites), settings.reject_policy,
+              AbstractPromise::ConstructWith<DependentList::ConstructUnresolved,
+                                             TestExecutor>(),
+              settings.prerequisite_policy,
+#if DCHECK_IS_ON()
+              settings.resolve_executor_type, settings.reject_executor_type,
+              settings.executor_can_resolve, settings.executor_can_reject,
+#endif
+              std::move(settings.callback)),
+          AbstractPromise::kRefCountPreference);
+    }
+
+   private:
+    PromiseSettings settings;
+  };
+
+  PromiseSettingsBuilder ThenPromise(Location from_here,
+                                     scoped_refptr<AbstractPromise> parent) {
+    PromiseSettingsBuilder builder(
+        from_here, parent ? std::make_unique<AbstractPromise::AdjacencyList>(
+                                std::move(parent))
+                          : std::make_unique<AbstractPromise::AdjacencyList>());
+    builder.With(BindOnce([](AbstractPromise* p) {
+      AbstractPromise* prerequisite = p->GetOnlyPrerequisite();
+      if (prerequisite->IsResolved()) {
+        p->emplace(Resolved<void>());
+        p->OnResolved();
+      } else if (prerequisite->IsRejected()) {
+        // Consistent with BaseThenAndCatchExecutor::ProcessNullExecutor.
+        p->emplace(scoped_refptr<AbstractPromise>(prerequisite));
+        p->OnResolved();
+      }
+    }));
+    return builder;
+  }
+
+  PromiseSettingsBuilder CatchPromise(Location from_here,
+                                      scoped_refptr<AbstractPromise> parent) {
+    PromiseSettingsBuilder builder(
+        from_here, parent ? std::make_unique<AbstractPromise::AdjacencyList>(
+                                std::move(parent))
+                          : std::make_unique<AbstractPromise::AdjacencyList>());
+    builder.With(CallbackResultType::kNoCallback)
+        .With(CallbackResultType::kCanResolve)
+        .WithResolve(ArgumentPassingType::kNoCallback)
+        .WithReject(ArgumentPassingType::kNormal)
+        .With(BindOnce([](AbstractPromise* p) {
+          AbstractPromise* prerequisite = p->GetOnlyPrerequisite();
+          if (prerequisite->IsResolved()) {
+            // Consistent with BaseThenAndCatchExecutor::ProcessNullExecutor.
+            p->emplace(scoped_refptr<AbstractPromise>(prerequisite));
+            p->OnResolved();
+          } else if (prerequisite->IsRejected()) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }
+        }));
+    return builder;
+  }
+
+  PromiseSettingsBuilder AllPromise(
+      Location from_here,
+      std::vector<internal::AbstractPromise::AdjacencyListNode>
+          prerequisite_list) {
+    PromiseSettingsBuilder builder(
+        from_here, std::make_unique<AbstractPromise::AdjacencyList>(
+                       std::move(prerequisite_list)));
+    builder.With(PrerequisitePolicy::kAll)
+        .With(BindOnce([](AbstractPromise* p) {
+          // Reject if any prerequisites rejected.
+          for (const AbstractPromise::AdjacencyListNode& node :
+               *p->prerequisite_list()) {
+            if (node.prerequisite->IsRejected()) {
+              p->emplace(Rejected<void>());
+              p->OnRejected();
+              return;
+            }
+          }
+          p->emplace(Resolved<void>());
+          p->OnResolved();
+        }));
+    return builder;
+  }
+
+  PromiseSettingsBuilder AnyPromise(
+      Location from_here,
+      std::vector<internal::AbstractPromise::AdjacencyListNode>
+          prerequisite_list) {
+    PromiseSettingsBuilder builder(
+        from_here, std::make_unique<AbstractPromise::AdjacencyList>(
+                       std::move(prerequisite_list)));
+    builder.With(PrerequisitePolicy::kAny)
+        .With(BindOnce([](AbstractPromise* p) {
+          // Reject if any prerequisites rejected.
+          for (const AbstractPromise::AdjacencyListNode& node :
+               *p->prerequisite_list()) {
+            if (node.prerequisite->IsRejected()) {
+              p->emplace(Rejected<void>());
+              p->OnRejected();
+              return;
+            }
+          }
+          p->emplace(Resolved<void>());
+          p->OnResolved();
+        }));
+    return builder;
+  }
+
+  test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(AbstractPromiseTest, UnfulfilledPromise) {
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  EXPECT_FALSE(promise->IsResolved());
+  EXPECT_FALSE(promise->IsRejected());
+  EXPECT_FALSE(promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, OnResolve) {
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  EXPECT_FALSE(promise->IsResolved());
+  promise->OnResolved();
+  EXPECT_TRUE(promise->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, OnReject) {
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy(
+          RejectPolicy::kCatchNotRequired);
+  EXPECT_FALSE(promise->IsRejected());
+  promise->OnRejected();
+  EXPECT_TRUE(promise->IsRejected());
+}
+
+TEST_F(AbstractPromiseTest, ExecuteOnResolve) {
+  scoped_refptr<AbstractPromise> promise =
+      ThenPromise(FROM_HERE, nullptr).With(BindOnce([](AbstractPromise* p) {
+        p->emplace(Resolved<void>());
+        p->OnResolved();
+      }));
+
+  EXPECT_FALSE(promise->IsResolved());
+  promise->Execute();
+  EXPECT_TRUE(promise->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, ExecuteOnReject) {
+  scoped_refptr<AbstractPromise> promise =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(RejectPolicy::kCatchNotRequired)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  EXPECT_FALSE(promise->IsRejected());
+  promise->Execute();
+  EXPECT_TRUE(promise->IsRejected());
+}
+
+TEST_F(AbstractPromiseTest, ExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3);
+  scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p4);
+
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p4->IsResolved());
+  EXPECT_FALSE(p5->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p1->IsResolved());
+  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, MoveExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1).WithResolve(ArgumentPassingType::kMove);
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p2).WithResolve(ArgumentPassingType::kMove);
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3).WithResolve(ArgumentPassingType::kMove);
+
+  scoped_refptr<AbstractPromise> p5 =
+      ThenPromise(FROM_HERE, p4).WithResolve(ArgumentPassingType::kMove);
+
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p4->IsResolved());
+  EXPECT_FALSE(p5->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p1->IsResolved());
+  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, MoveResolveCatchExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1)
+          .With(CallbackResultType::kCanReject)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p3 =
+      CatchPromise(FROM_HERE, p2)
+          .With(CallbackResultType::kCanResolve)
+          .WithReject(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(CallbackResultType::kCanReject)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p5 =
+      CatchPromise(FROM_HERE, p4)
+          .With(CallbackResultType::kCanResolve)
+          .WithReject(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsRejected());
+  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p4->IsRejected());
+  EXPECT_FALSE(p5->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p2->IsRejected());
+  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p4->IsRejected());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, MoveResolveCatchExecutionChainType2) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1)
+          .With(CallbackResultType::kCanReject)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p3 =
+      CatchPromise(FROM_HERE, p2)
+          .With(CallbackResultType::kCanReject)
+          .WithReject(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      CatchPromise(FROM_HERE, p3)
+          .With(CallbackResultType::kCanResolve)
+          .WithReject(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p5 =
+      ThenPromise(FROM_HERE, p4)
+          .With(CallbackResultType::kCanResolve)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p6 =
+      ThenPromise(FROM_HERE, p5)
+          .With(CallbackResultType::kCanReject)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p7 =
+      CatchPromise(FROM_HERE, p6)
+          .With(CallbackResultType::kCanReject)
+          .WithReject(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p8 =
+      CatchPromise(FROM_HERE, p7)
+          .With(CallbackResultType::kCanResolve)
+          .WithReject(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p9 =
+      ThenPromise(FROM_HERE, p8)
+          .With(CallbackResultType::kCanResolve)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsRejected());
+  EXPECT_FALSE(p3->IsRejected());
+  EXPECT_FALSE(p4->IsResolved());
+  EXPECT_FALSE(p5->IsResolved());
+  EXPECT_FALSE(p6->IsRejected());
+  EXPECT_FALSE(p7->IsRejected());
+  EXPECT_FALSE(p8->IsResolved());
+  EXPECT_FALSE(p9->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p2->IsRejected());
+  EXPECT_TRUE(p3->IsRejected());
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+  EXPECT_TRUE(p6->IsRejected());
+  EXPECT_TRUE(p7->IsRejected());
+  EXPECT_TRUE(p8->IsResolved());
+  EXPECT_TRUE(p9->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, MixedMoveAndNormalExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1).WithResolve(ArgumentPassingType::kMove);
+
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3).WithResolve(ArgumentPassingType::kMove);
+
+  scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p4);
+
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p4->IsResolved());
+  EXPECT_FALSE(p5->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p1->IsResolved());
+  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, MoveAtEndOfChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p2).WithResolve(ArgumentPassingType::kMove);
+}
+
+TEST_F(AbstractPromiseTest, BranchedExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p4);
+
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p4->IsResolved());
+  EXPECT_FALSE(p5->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p2->IsResolved());
+  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, PrerequisiteAlreadyResolved) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  p1->OnResolved();
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+
+  EXPECT_FALSE(p2->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p2->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, PrerequisiteAlreadyRejected) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  p1->OnRejected();
+
+  scoped_refptr<AbstractPromise> p2 = CatchPromise(FROM_HERE, p1);
+
+  EXPECT_FALSE(p2->IsResolved());
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p2->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, MultipleResolvedPrerequisitePolicyALL) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p4 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      4);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  prerequisite_list[3].prerequisite = p4;
+
+  scoped_refptr<AbstractPromise> all_promise =
+      AllPromise(FROM_HERE, std::move(prerequisite_list));
+
+  p1->OnResolved();
+  p2->OnResolved();
+  p3->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(all_promise->IsResolved());
+  p4->OnResolved();
+
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(all_promise->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest,
+       MultithreadedMultipleResolvedPrerequisitePolicyALL) {
+  constexpr int num_threads = 4;
+  constexpr int num_promises = 1000;
+
+  std::unique_ptr<Thread> thread[num_threads];
+  for (int i = 0; i < num_threads; i++) {
+    thread[i] = std::make_unique<Thread>("Test thread");
+    thread[i]->Start();
+  }
+
+  scoped_refptr<AbstractPromise> promise[num_promises];
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      num_promises);
+  for (int i = 0; i < num_promises; i++) {
+    promise[i] = DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+    prerequisite_list[i].prerequisite = promise[i];
+  }
+
+  scoped_refptr<AbstractPromise> all_promise =
+      AllPromise(FROM_HERE, std::move(prerequisite_list));
+
+  RunLoop run_loop;
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, all_promise)
+          .With(BindLambdaForTesting(
+              [&](AbstractPromise* p) { run_loop.Quit(); }));
+
+  for (int i = 0; i < num_promises; i++) {
+    thread[i % num_threads]->task_runner()->PostTask(
+        FROM_HERE, BindOnce(
+                       [](scoped_refptr<AbstractPromise> all_promise,
+                          scoped_refptr<AbstractPromise> promise) {
+                         EXPECT_FALSE(all_promise->IsResolved());
+                         promise->OnResolved();
+                       },
+                       all_promise, promise[i]));
+  }
+
+  run_loop.Run();
+
+  for (int i = 0; i < num_promises; i++) {
+    EXPECT_TRUE(promise[i]->IsResolved());
+  }
+
+  EXPECT_TRUE(all_promise->IsResolved());
+
+  for (int i = 0; i < num_threads; i++) {
+    thread[i]->Stop();
+  }
+}
+
+TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyALL) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  scoped_refptr<AbstractPromise> p4 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      4);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  prerequisite_list[3].prerequisite = p4;
+
+  scoped_refptr<AbstractPromise> all_promise =
+      AllPromise(FROM_HERE, std::move(prerequisite_list))
+          .With(CallbackResultType::kCanResolveOrReject);
+
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, all_promise);
+
+  p3->OnRejected();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(all_promise->IsRejected());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, SingleResolvedPrerequisitesPrerequisitePolicyANY) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p4 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      4);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  prerequisite_list[3].prerequisite = p4;
+
+  scoped_refptr<AbstractPromise> any_promise =
+      AnyPromise(FROM_HERE, std::move(prerequisite_list));
+
+  p2->OnResolved();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(any_promise->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, SingleRejectPrerequisitePolicyANY) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+  scoped_refptr<AbstractPromise> p4 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      4);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  prerequisite_list[3].prerequisite = p4;
+
+  scoped_refptr<AbstractPromise> any_promise =
+      AnyPromise(FROM_HERE, std::move(prerequisite_list))
+          .With(CallbackResultType::kCanResolveOrReject);
+
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, any_promise);
+
+  p3->OnRejected();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(any_promise->IsRejected());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, SingleResolvePrerequisitePolicyANY) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p4 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      4);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  prerequisite_list[3].prerequisite = p4;
+
+  scoped_refptr<AbstractPromise> any_promise =
+      AnyPromise(FROM_HERE, std::move(prerequisite_list));
+
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, any_promise);
+
+  p3->OnResolved();
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(any_promise->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, IsCanceled) {
+  scoped_refptr<AbstractPromise> promise = ThenPromise(FROM_HERE, nullptr);
+  EXPECT_FALSE(promise->IsCanceled());
+  promise->OnCanceled();
+  EXPECT_TRUE(promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, OnCanceledPreventsExecution) {
+  scoped_refptr<AbstractPromise> promise =
+      ThenPromise(FROM_HERE, nullptr).With(BindOnce([](AbstractPromise* p) {
+        FAIL() << "Should not be called";
+      }));
+  promise->OnCanceled();
+  promise->Execute();
+}
+
+TEST_F(AbstractPromiseTest, CancelationStopsExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1).With(BindOnce([](AbstractPromise* p) {
+        p->OnCanceled();
+        p->OnCanceled();  // NOP shouldn't crash.
+      }));
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3);
+
+  p1->OnResolved();
+
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p3->IsCanceled());
+  EXPECT_TRUE(p4->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, CancelationStopsBranchedExecutionChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1).With(BindOnce([](AbstractPromise* p) {
+        p->OnCanceled();
+      }));
+
+  // Branch one
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3);
+
+  // Branch two
+  scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> promise6 = ThenPromise(FROM_HERE, p5);
+
+  p1->OnResolved();
+
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p3->IsCanceled());
+  EXPECT_TRUE(p4->IsCanceled());
+  EXPECT_TRUE(p5->IsCanceled());
+  EXPECT_TRUE(promise6->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, CancelChainCanReject) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2);
+
+  p0->OnCanceled();
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, CancelationPrerequisitePolicyALL) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      3);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+
+  scoped_refptr<AbstractPromise> all_promise =
+      AllPromise(FROM_HERE, std::move(prerequisite_list));
+
+  p2->OnCanceled();
+  EXPECT_TRUE(all_promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, CancelationPrerequisitePolicyANY) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      3);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+
+  scoped_refptr<AbstractPromise> any_promise =
+      AnyPromise(FROM_HERE, std::move(prerequisite_list));
+
+  p3->OnCanceled();
+  p2->OnCanceled();
+  EXPECT_FALSE(any_promise->IsCanceled());
+
+  p1->OnCanceled();
+  EXPECT_TRUE(any_promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, AlreadyCanceledPrerequisitePolicyALL) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      3);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  p2->OnCanceled();
+
+  scoped_refptr<AbstractPromise> all_promise =
+      AllPromise(FROM_HERE, std::move(prerequisite_list));
+
+  EXPECT_TRUE(all_promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, SomeAlreadyCanceledPrerequisitePolicyANY) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      3);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  p2->OnCanceled();
+
+  scoped_refptr<AbstractPromise> any_promise =
+      AnyPromise(FROM_HERE, std::move(prerequisite_list));
+
+  EXPECT_FALSE(any_promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, AllAlreadyCanceledPrerequisitePolicyANY) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  scoped_refptr<AbstractPromise> p3 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  std::vector<internal::AbstractPromise::AdjacencyListNode> prerequisite_list(
+      3);
+  prerequisite_list[0].prerequisite = p1;
+  prerequisite_list[1].prerequisite = p2;
+  prerequisite_list[2].prerequisite = p3;
+  p1->OnCanceled();
+  p2->OnCanceled();
+  p3->OnCanceled();
+
+  scoped_refptr<AbstractPromise> any_promise =
+      AnyPromise(FROM_HERE, std::move(prerequisite_list));
+
+  EXPECT_TRUE(any_promise->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, DetectResolveDoubleMoveHazard) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0).WithResolve(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH({
+    scoped_refptr<AbstractPromise> p2 =
+        ThenPromise(FROM_HERE, p0).WithResolve(ArgumentPassingType::kMove);
+  });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, DetectMixedResolveCallbackMoveAndNonMoveHazard) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0).WithResolve(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH(
+      { scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0); });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, MultipleNonMoveCatchCallbacksAreOK) {
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  E3      E4
+   *   C      C
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p2);
+}
+
+TEST_F(AbstractPromiseTest, DetectCatchCallbackDoubleMoveHazard) {
+#if DCHECK_IS_ON()
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   C      C
+   *
+   * We need to make sure P3 & P4's reject callback don't both use move
+   * semantics since they share a common ancestor with no intermediate catches.
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p1 =
+      CatchPromise(FROM_HERE, p0).WithReject(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH({
+    scoped_refptr<AbstractPromise> p2 =
+        CatchPromise(FROM_HERE, p0).WithReject(ArgumentPassingType::kMove);
+  });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, DetectCatchCallbackDoubleMoveHazardInChain) {
+#if DCHECK_IS_ON()
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P3      P4
+   *   C      C
+   *
+   * We need to make sure P3 & P4's reject callback don't both use move
+   * semantics since they share a common ancestor with no intermediate catches.
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0);
+
+  scoped_refptr<AbstractPromise> p3 =
+      CatchPromise(FROM_HERE, p1).WithReject(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH({
+    scoped_refptr<AbstractPromise> p4 =
+        CatchPromise(FROM_HERE, p2).WithReject(ArgumentPassingType::kMove);
+  });
+#endif
+}
+
+TEST_F(AbstractPromiseTest,
+       DetectCatchCallbackDoubleMoveHazardInChainIntermediateThensCanReject) {
+#if DCHECK_IS_ON()
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P3      P4
+   *   C      C
+   *
+   * We need to make sure P3 & P4's reject callback don't both use move
+   * semantics since they share a common ancestor with no intermediate catches.
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p0).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p3 =
+      CatchPromise(FROM_HERE, p1).WithReject(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH({
+    scoped_refptr<AbstractPromise> p4 =
+        CatchPromise(FROM_HERE, p2).WithReject(ArgumentPassingType::kMove);
+  });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, DetectMixedCatchCallbackMoveAndNonMoveHazard) {
+#if DCHECK_IS_ON()
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P3      P4
+   *   C      C
+   *
+   * We can't guarantee the order in which P3 and P4's reject callbacks run so
+   * we need to need to catch the case where move and non-move semantics are
+   * mixed.
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      ThenPromise(FROM_HERE, nullptr).With(CallbackResultType::kCanReject);
+
+  scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0);
+
+  scoped_refptr<AbstractPromise> p3 =
+      CatchPromise(FROM_HERE, p1).WithReject(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH(
+      { scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p2); });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, DetectThenCallbackDoubleMoveHazardInChain) {
+#if DCHECK_IS_ON()
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   C      C
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   C      C
+   *   |      |
+   *  \|      |/
+   *  P3      P4
+   *   T      T
+   *
+   * We need to make sure P3 & P4's resolve callback don't both use move
+   * semantics since they share a common ancestor with no intermediate then's.
+   */
+  scoped_refptr<AbstractPromise> p0 = ThenPromise(FROM_HERE, nullptr);
+  scoped_refptr<AbstractPromise> p1 = CatchPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p2 = CatchPromise(FROM_HERE, p0);
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p1).WithResolve(ArgumentPassingType::kMove);
+
+  EXPECT_DCHECK_DEATH({
+    scoped_refptr<AbstractPromise> p4 =
+        ThenPromise(FROM_HERE, p2).WithResolve(ArgumentPassingType::kMove);
+  });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, SimpleMissingCatch) {
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p1| is deleted.
+  EXPECT_DCHECK_DEATH({ p1 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p2| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p2 = CatchPromise(FROM_HERE, p1);
+}
+
+TEST_F(AbstractPromiseTest, MissingCatch) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  // The missing catch here will get noticed.
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+
+  p0->OnResolved();
+
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p2| is deleted.
+  EXPECT_DCHECK_DEATH({ p2 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p2| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2);
+#endif
+}
+
+TEST_F(AbstractPromiseTest, MissingCatchNotRequired) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(RejectPolicy::kCatchNotRequired)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  // The missing catch here will gets ignored.
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+
+  p0->OnResolved();
+
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, MissingCatchFromCurriedPromise) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p0)
+          .With(BindOnce(
+              [](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) {
+                // Resolve with a promise that can and does reject.
+                ThreadTaskRunnerHandle::Get()->PostTask(
+                    FROM_HERE, BindOnce(&AbstractPromise::Execute, p1));
+                p->emplace(std::move(p1));
+                p->OnResolved();
+              },
+              std::move(p1)));
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p2| is deleted.
+  EXPECT_DCHECK_DEATH({ p2 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p2| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, MissingCatchFromCurriedPromiseWithDependent) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p0)
+          .With(BindOnce(
+              [&](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) {
+                // Resolve with a promise that can and does reject.
+                ThreadTaskRunnerHandle::Get()->PostTask(
+                    FROM_HERE, BindOnce(&AbstractPromise::Execute, p1));
+                p->emplace(std::move(p1));
+                p->OnResolved();
+              },
+              std::move(p1)));
+
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p3| is deleted.
+  EXPECT_DCHECK_DEATH({ p3 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p3| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest,
+       MissingCatchFromCurriedPromiseWithDependentAddedAfterExecution) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p0)
+          .With(BindOnce(
+              [&](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) {
+                // Resolve with a promise that can and does reject.
+                ThreadTaskRunnerHandle::Get()->PostTask(
+                    FROM_HERE, BindOnce(&AbstractPromise::Execute, p1));
+                p->emplace(std::move(p1));
+                p->OnResolved();
+              },
+              std::move(p1)));
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p3| is deleted.
+  EXPECT_DCHECK_DEATH({ p3 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p3| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, MissingCatchLongChain) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p4| is deleted.
+  EXPECT_DCHECK_DEATH({ p4 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, p4);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest,
+       ThenAddedToSettledPromiseWithMissingCatchAndSeveralDependents) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p2);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p2);
+
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p4| is deleted.
+  EXPECT_DCHECK_DEATH({ p5 = nullptr; });
+
+  // Tidy up.
+  scoped_refptr<AbstractPromise> p6 = CatchPromise(FROM_HERE, p3);
+  scoped_refptr<AbstractPromise> p7 = CatchPromise(FROM_HERE, p4);
+  scoped_refptr<AbstractPromise> p8 = CatchPromise(FROM_HERE, p5);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, ThenAddedAfterChainExecutionWithMissingCatch) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  // The missing catch here will get noticed.
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3);
+
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p4| is deleted.
+  EXPECT_DCHECK_DEATH({ p4 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, p4);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, CatchAddedAfterChainExecution) {
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3);
+
+  // We shouldn't get a DCHECK failure because |p4| catches the rejection
+  // from |p1|.
+  RunLoop().RunUntilIdle();
+}
+
+TEST_F(AbstractPromiseTest, MultipleThensAddedAfterChainExecution) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+
+  // |p5| - |p7| should still inherit catch responsibility despite this.
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  // The missing catches will get noticed.
+  scoped_refptr<AbstractPromise> p5 = ThenPromise(FROM_HERE, p3);
+  scoped_refptr<AbstractPromise> p6 = ThenPromise(FROM_HERE, p3);
+  scoped_refptr<AbstractPromise> p7 = ThenPromise(FROM_HERE, p3);
+
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p5|, |p6| or |p7| are deleted.
+  EXPECT_DCHECK_DEATH({ p5 = nullptr; });
+  EXPECT_DCHECK_DEATH({ p6 = nullptr; });
+  EXPECT_DCHECK_DEATH({ p7 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p8 = CatchPromise(FROM_HERE, p5);
+  scoped_refptr<AbstractPromise> p9 = CatchPromise(FROM_HERE, p6);
+  scoped_refptr<AbstractPromise> p10 = CatchPromise(FROM_HERE, p7);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, MultipleDependentsAddedAfterChainExecution) {
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p3);
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, p4);
+  scoped_refptr<AbstractPromise> p6 = ThenPromise(FROM_HERE, p3);
+  scoped_refptr<AbstractPromise> p7 = CatchPromise(FROM_HERE, p6);
+
+  // We shouldn't get a DCHECK failure because |p6| and |p7| catch the rejection
+  // from |p1|.
+  RunLoop().RunUntilIdle();
+}
+
+TEST_F(AbstractPromiseTest, CatchAfterLongChain) {
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, p0)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p3 = ThenPromise(FROM_HERE, p2);
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p3);
+
+  p0->OnResolved();
+
+  RunLoop().RunUntilIdle();
+}
+
+TEST_F(AbstractPromiseTest, MissingCatchOneSideOfBranchedExecutionChain) {
+#if DCHECK_IS_ON()
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P3      P4
+   *   C      T
+   *
+   * The missing catch for P4 should get noticed.
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+
+  scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p4 = ThenPromise(FROM_HERE, p2);
+
+  p0->OnRejected();
+
+  RunLoop().RunUntilIdle();
+
+  // This should DCHECK when |p4| is deleted.
+  EXPECT_DCHECK_DEATH({ p4 = nullptr; });
+
+  // Under the hood EXPECT_DCHECK_DEATH uses fork() so |p4| isn't actually
+  // cleared so we need to tidy up.
+  scoped_refptr<AbstractPromise> p5 = CatchPromise(FROM_HERE, p4);
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, CantResolveIfpromiseDeclaredAsNonResolving) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p = DoNothingPromiseBuilder(FROM_HERE);
+
+  EXPECT_DCHECK_DEATH({ p->OnResolved(); });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, CantRejectIfpromiseDeclaredAsNonRejecting) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p = DoNothingPromiseBuilder(FROM_HERE);
+
+  EXPECT_DCHECK_DEATH({ p->OnRejected(); });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, DoubleMoveDoNothingPromise) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1)
+          .WithResolve(ArgumentPassingType::kMove)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<int>(42));
+            p->OnResolved();
+          }));
+
+  EXPECT_DCHECK_DEATH({
+    scoped_refptr<AbstractPromise> p3 =
+        ThenPromise(FROM_HERE, p1)
+            .WithResolve(ArgumentPassingType::kMove)
+            .With(BindOnce([](AbstractPromise* p) {
+              p->emplace(Resolved<int>(42));
+              p->OnResolved();
+            }));
+  });
+#endif
+}
+
+TEST_F(AbstractPromiseTest, CatchBothSidesOfBranchedExecutionChain) {
+  /*
+   * Key:  T = Then, C = Catch
+   *
+   *      P0
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P1      P2
+   *   T      T
+   *   |      |
+   *  \|      |/
+   *  P3      P4
+   *   C      C
+   *
+   * This should execute without DCHECKS.
+   */
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetCanResolve(
+          false);
+
+  scoped_refptr<AbstractPromise> p1 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p2 = ThenPromise(FROM_HERE, p0);
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p1);
+  scoped_refptr<AbstractPromise> p4 = CatchPromise(FROM_HERE, p2);
+
+  p0->OnRejected();
+
+  RunLoop().RunUntilIdle();
+}
+
+TEST_F(AbstractPromiseTest, ResolvedCurriedPromise) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  std::vector<int> run_order;
+
+  // Promise |p3| will be resolved with.
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(2);
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(3);
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p2));
+            p->emplace(std::move(p2));
+            p->OnResolved();
+
+            EXPECT_TRUE(p3->IsResolvedWithPromise());
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(4);
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  p1->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_order, ElementsAre(3, 2, 4));
+}
+
+TEST_F(AbstractPromiseTest, UnresolvedCurriedPromise) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  std::vector<int> run_order;
+
+  // Promise |p3| will be resolved with.
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(3);
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p2));
+            p->emplace(p2);
+            p->OnResolved();
+
+            EXPECT_TRUE(p3->IsResolvedWithPromise());
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(4);
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  p1->OnResolved();
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, ElementsAre(3));
+
+  p2->OnResolved();
+  RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, ElementsAre(3, 4));
+}
+
+TEST_F(AbstractPromiseTest, CanceledCurriedPromise) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  // Promise |p3| will be resolved with.
+  scoped_refptr<AbstractPromise> p2 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  p2->OnCanceled();
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p2));
+            p->emplace(p2);
+            p->OnResolved();
+
+            EXPECT_TRUE(p3->IsResolvedWithPromise());
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting(
+              [&](AbstractPromise* p) { FAIL() << "Should not get here"; }));
+
+  p1->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(p4->IsCanceled());
+}
+
+TEST_F(AbstractPromiseTest, CurriedPromiseChain) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+  std::vector<int> run_order;
+
+  // Promise |p3| will be resolved with.
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(2);
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  // Promise |p4| will be resolved with.
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(3);
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p2));
+            p->emplace(std::move(p2));
+            p->OnResolved();
+          }));
+
+  // Promise |p5| will be resolved with.
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(4);
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p3));
+            p->emplace(std::move(p3));
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p5 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(5);
+            // Resolve with a promise.
+            ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, BindOnce(&AbstractPromise::Execute, p4));
+            p->emplace(std::move(p4));
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p6 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            run_order.push_back(6);
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }));
+
+  p1->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_order, ElementsAre(5, 4, 3, 2, 6));
+}
+
+TEST_F(AbstractPromiseTest, CurriedPromiseChainType2) {
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(p1);
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p2)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(p2);
+            p->OnResolved();
+          }));
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(p3);
+            p->OnResolved();
+          }));
+
+  p1->OnResolved();
+  RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_EQ(p1.get(), p4->FindNonCurriedAncestor());
+}
+
+TEST_F(AbstractPromiseTest, CurriedPromiseMoveArg) {
+#if DCHECK_IS_ON()
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+          }))
+          .WithResolve(ArgumentPassingType::kMove);
+  ;
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p0)
+          .With(BindOnce(
+              [](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) {
+                ThreadTaskRunnerHandle::Get()->PostTask(
+                    FROM_HERE, BindOnce(&AbstractPromise::Execute, p1));
+                p->emplace(std::move(p1));
+                p->OnResolved();
+              },
+              std::move(p1)))
+          .WithResolve(ArgumentPassingType::kMove);
+
+  p0->OnResolved();
+  RunLoop().RunUntilIdle();
+#endif
+}
+
+TEST_F(AbstractPromiseTest, CatchCurriedPromise) {
+  scoped_refptr<AbstractPromise> p0 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p1 =
+      ThenPromise(FROM_HERE, nullptr)
+          .With(CallbackResultType::kCanReject)
+          .With(BindOnce([](AbstractPromise* p) {
+            p->emplace(Rejected<void>());
+            p->OnRejected();
+          }));
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p0)
+          .With(BindOnce(
+              [&](scoped_refptr<AbstractPromise> p1, AbstractPromise* p) {
+                // Resolve with a promise that can and does reject.
+                ThreadTaskRunnerHandle::Get()->PostTask(
+                    FROM_HERE, BindOnce(&AbstractPromise::Execute, p1));
+                p->emplace(std::move(p1));
+                p->OnResolved();
+              },
+              std::move(p1)));
+
+  scoped_refptr<AbstractPromise> p3 = CatchPromise(FROM_HERE, p2);
+
+  p0->OnResolved();
+  EXPECT_FALSE(p3->IsResolved());
+
+  RunLoop().RunUntilIdle();
+  EXPECT_TRUE(p3->IsResolved());
+}
+
+TEST_F(AbstractPromiseTest, ThreadHopping) {
+  std::unique_ptr<Thread> thread_a(new Thread("AbstractPromiseTest_Thread_A"));
+  std::unique_ptr<Thread> thread_b(new Thread("AbstractPromiseTest_Thread_B"));
+  std::unique_ptr<Thread> thread_c(new Thread("AbstractPromiseTest_Thread_C"));
+  thread_a->Start();
+  thread_b->Start();
+  thread_c->Start();
+  RunLoop run_loop;
+
+  scoped_refptr<AbstractPromise> p1 =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  scoped_refptr<AbstractPromise> p2 =
+      ThenPromise(FROM_HERE, p1)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+            CHECK(thread_a->task_runner()->BelongsToCurrentThread());
+          }))
+          .With(thread_a->task_runner());
+
+  scoped_refptr<AbstractPromise> p3 =
+      ThenPromise(FROM_HERE, p2)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+            CHECK(thread_b->task_runner()->BelongsToCurrentThread());
+          }))
+          .With(thread_b->task_runner());
+
+  scoped_refptr<AbstractPromise> p4 =
+      ThenPromise(FROM_HERE, p3)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+            CHECK(thread_c->task_runner()->BelongsToCurrentThread());
+          }))
+          .With(thread_c->task_runner());
+
+  scoped_refptr<SingleThreadTaskRunner> main_thread =
+      ThreadTaskRunnerHandle::Get();
+  scoped_refptr<AbstractPromise> p5 =
+      ThenPromise(FROM_HERE, p4)
+          .With(BindLambdaForTesting([&](AbstractPromise* p) {
+            p->emplace(Resolved<void>());
+            p->OnResolved();
+            run_loop.Quit();
+            CHECK(main_thread->BelongsToCurrentThread());
+          }))
+          .With(main_thread);
+
+  p1->OnResolved();
+
+  EXPECT_FALSE(p2->IsResolved());
+  EXPECT_FALSE(p3->IsResolved());
+  EXPECT_FALSE(p4->IsResolved());
+  EXPECT_FALSE(p5->IsResolved());
+  run_loop.Run();
+  EXPECT_TRUE(p2->IsResolved());
+  EXPECT_TRUE(p3->IsResolved());
+  EXPECT_TRUE(p4->IsResolved());
+  EXPECT_TRUE(p5->IsResolved());
+
+  thread_a->Stop();
+  thread_b->Stop();
+  thread_c->Stop();
+}
+
+TEST_F(AbstractPromiseTest, MutipleThreadsAddingDependants) {
+  constexpr int num_threads = 4;
+  constexpr int num_promises = 10000;
+
+  std::unique_ptr<Thread> thread[num_threads];
+  for (int i = 0; i < num_threads; i++) {
+    thread[i] = std::make_unique<Thread>("Test thread");
+    thread[i]->Start();
+  }
+
+  scoped_refptr<AbstractPromise> root =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  RunLoop run_loop;
+  std::atomic<int> pending_count(num_promises);
+
+  // After being called |num_promises| times |decrement_cb| will quit |run_loop|
+  auto decrement_cb = BindLambdaForTesting([&](AbstractPromise* p) {
+    int count = pending_count.fetch_sub(1, std::memory_order_acq_rel);
+    if (count == 1)
+      run_loop.Quit();
+  });
+
+  // Post a bunch of tasks on multiple threads that create Then promises
+  // dependent on |root| which call |decrement_cb| when resolved.
+  for (int i = 0; i < num_promises; i++) {
+    thread[i % num_threads]->task_runner()->PostTask(
+        FROM_HERE, BindLambdaForTesting([&]() {
+          scoped_refptr<AbstractPromise> p =
+              ThenPromise(FROM_HERE, root).With(decrement_cb);
+          p->OnResolved();
+        }));
+
+    // Mid way through post a task to resolve |root|.
+    if (i == num_promises / 2) {
+      thread[i % num_threads]->task_runner()->PostTask(
+          FROM_HERE, BindOnce(&AbstractPromise::OnResolved, root));
+    }
+  }
+
+  // This should exit cleanly without any TSan errors.
+  run_loop.Run();
+
+  for (int i = 0; i < num_threads; i++) {
+    thread[i]->Stop();
+  }
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/dependent_list.cc b/base/task/promise/dependent_list.cc
index cfe0b7d4..e1c1a0a7 100644
--- a/base/task/promise/dependent_list.cc
+++ b/base/task/promise/dependent_list.cc
@@ -4,6 +4,8 @@
 
 #include "base/task/promise/dependent_list.h"
 
+#include "base/task/promise/abstract_promise.h"
+
 namespace base {
 namespace internal {
 
@@ -17,6 +19,13 @@
 
 DependentList::Node::Node() = default;
 
+DependentList::Node::Node(Node&& other) {
+  dependent = std::move(other.dependent);
+  DCHECK_EQ(other.next, nullptr);
+}
+
+DependentList::Node::~Node() = default;
+
 DependentList::InsertResult DependentList::Insert(Node* node) {
   // This method uses std::memory_order_acquire semantics on read (the failure
   // case of compare_exchange_weak() below is a read) to ensure setting
@@ -83,6 +92,12 @@
   return reinterpret_cast<Node*>(prev_head);
 }
 
+bool DependentList::IsSettled() const {
+  uintptr_t value = head_.load(std::memory_order_acquire);
+  return value == kResolvedSentinel || value == kRejectedSentinel ||
+         value == kCanceledSentinel;
+}
+
 bool DependentList::IsResolved() const {
   return head_.load(std::memory_order_acquire) == kResolvedSentinel;
 }
@@ -91,7 +106,7 @@
   return head_.load(std::memory_order_acquire) == kRejectedSentinel;
 }
 
-bool DependentList::IsCancelled() const {
+bool DependentList::IsCanceled() const {
   return head_.load(std::memory_order_acquire) == kCanceledSentinel;
 }
 
diff --git a/base/task/promise/dependent_list.h b/base/task/promise/dependent_list.h
index 02ab141..02e80e4 100644
--- a/base/task/promise/dependent_list.h
+++ b/base/task/promise/dependent_list.h
@@ -10,6 +10,7 @@
 #include "base/base_export.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 
 namespace base {
 namespace internal {
@@ -41,9 +42,10 @@
 
   struct BASE_EXPORT Node {
     Node();
+    explicit Node(Node&& other) noexcept;
+    ~Node();
 
-    // TODO(alexclarke): Make this a scoped_refptr.
-    AbstractPromise* dependent;
+    scoped_refptr<AbstractPromise> dependent;
     std::atomic<Node*> next{nullptr};
   };
 
@@ -61,9 +63,10 @@
   // A ConsumeXXX function may only be called once.
   Node* ConsumeOnceForCancel();
 
+  bool IsSettled() const;
   bool IsResolved() const;
   bool IsRejected() const;
-  bool IsCancelled() const;
+  bool IsCanceled() const;
 
  private:
   std::atomic<uintptr_t> head_;
diff --git a/base/task/promise/dependent_list_unittest.cc b/base/task/promise/dependent_list_unittest.cc
index 9df62a29..7c3c1e3fc 100644
--- a/base/task/promise/dependent_list_unittest.cc
+++ b/base/task/promise/dependent_list_unittest.cc
@@ -13,8 +13,9 @@
   DependentList::Node node;
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node));
   EXPECT_FALSE(list.IsRejected());
-  EXPECT_FALSE(list.IsCancelled());
+  EXPECT_FALSE(list.IsCanceled());
   EXPECT_FALSE(list.IsResolved());
+  EXPECT_FALSE(list.IsSettled());
 }
 
 TEST(DependentList, ConstructResolved) {
@@ -24,7 +25,8 @@
             list.Insert(&node));
   EXPECT_TRUE(list.IsResolved());
   EXPECT_FALSE(list.IsRejected());
-  EXPECT_FALSE(list.IsCancelled());
+  EXPECT_FALSE(list.IsCanceled());
+  EXPECT_TRUE(list.IsSettled());
 }
 
 TEST(DependentList, ConstructRejected) {
@@ -33,8 +35,9 @@
   EXPECT_EQ(DependentList::InsertResult::FAIL_PROMISE_REJECTED,
             list.Insert(&node));
   EXPECT_TRUE(list.IsRejected());
-  EXPECT_FALSE(list.IsCancelled());
+  EXPECT_FALSE(list.IsCanceled());
   EXPECT_FALSE(list.IsResolved());
+  EXPECT_TRUE(list.IsSettled());
 }
 
 TEST(DependentList, ConsumeOnceForResolve) {
@@ -47,10 +50,12 @@
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
 
   EXPECT_FALSE(list.IsResolved());
+  EXPECT_FALSE(list.IsSettled());
   DependentList::Node* result = list.ConsumeOnceForResolve();
   EXPECT_TRUE(list.IsResolved());
   EXPECT_FALSE(list.IsRejected());
-  EXPECT_FALSE(list.IsCancelled());
+  EXPECT_FALSE(list.IsCanceled());
+  EXPECT_TRUE(list.IsSettled());
 
   EXPECT_EQ(&node3, result);
   EXPECT_EQ(&node2, result->next.load());
@@ -73,10 +78,12 @@
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
 
   EXPECT_FALSE(list.IsRejected());
+  EXPECT_FALSE(list.IsSettled());
   DependentList::Node* result = list.ConsumeOnceForReject();
   EXPECT_TRUE(list.IsRejected());
   EXPECT_FALSE(list.IsResolved());
-  EXPECT_FALSE(list.IsCancelled());
+  EXPECT_FALSE(list.IsCanceled());
+  EXPECT_TRUE(list.IsSettled());
 
   EXPECT_EQ(&node3, result);
   EXPECT_EQ(&node2, result->next.load());
@@ -98,11 +105,13 @@
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
   EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
 
-  EXPECT_FALSE(list.IsCancelled());
+  EXPECT_FALSE(list.IsCanceled());
+  EXPECT_FALSE(list.IsSettled());
   DependentList::Node* result = list.ConsumeOnceForCancel();
-  EXPECT_TRUE(list.IsCancelled());
+  EXPECT_TRUE(list.IsCanceled());
   EXPECT_FALSE(list.IsResolved());
   EXPECT_FALSE(list.IsRejected());
+  EXPECT_TRUE(list.IsSettled());
 
   EXPECT_EQ(&node3, result);
   EXPECT_EQ(&node2, result->next.load());
diff --git a/base/task/promise/no_op_promise_executor.cc b/base/task/promise/no_op_promise_executor.cc
new file mode 100644
index 0000000..17cffac
--- /dev/null
+++ b/base/task/promise/no_op_promise_executor.cc
@@ -0,0 +1,67 @@
+// 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/task/promise/no_op_promise_executor.h"
+#include "base/task_runner.h"
+
+namespace base {
+namespace internal {
+
+NoOpPromiseExecutor::NoOpPromiseExecutor(bool can_resolve, bool can_reject)
+#if DCHECK_IS_ON()
+    : can_resolve_(can_resolve),
+      can_reject_(can_reject)
+#endif
+{
+}
+
+NoOpPromiseExecutor::~NoOpPromiseExecutor() {}
+
+AbstractPromise::Executor::PrerequisitePolicy
+NoOpPromiseExecutor::GetPrerequisitePolicy() {
+  return PrerequisitePolicy::kNever;
+}
+
+bool NoOpPromiseExecutor::IsCancelled() const {
+  return false;
+}
+
+#if DCHECK_IS_ON()
+AbstractPromise::Executor::ArgumentPassingType
+NoOpPromiseExecutor::ResolveArgumentPassingType() const {
+  return ArgumentPassingType::kNoCallback;
+}
+
+AbstractPromise::Executor::ArgumentPassingType
+NoOpPromiseExecutor::RejectArgumentPassingType() const {
+  return ArgumentPassingType::kNoCallback;
+}
+
+bool NoOpPromiseExecutor::CanResolve() const {
+  return can_resolve_;
+}
+
+bool NoOpPromiseExecutor::CanReject() const {
+  return can_reject_;
+}
+#endif
+
+void NoOpPromiseExecutor::Execute(AbstractPromise* promise) {}
+
+// static
+scoped_refptr<internal::AbstractPromise> NoOpPromiseExecutor::Create(
+    Location from_here,
+    bool can_resolve,
+    bool can_reject,
+    RejectPolicy reject_policy) {
+  return MakeRefCounted<internal::AbstractPromise>(
+      nullptr, from_here, nullptr, reject_policy,
+      internal::AbstractPromise::ConstructWith<
+          internal::DependentList::ConstructUnresolved,
+          internal::NoOpPromiseExecutor>(),
+      can_resolve, can_reject);
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/no_op_promise_executor.h b/base/task/promise/no_op_promise_executor.h
new file mode 100644
index 0000000..9984249
--- /dev/null
+++ b/base/task/promise/no_op_promise_executor.h
@@ -0,0 +1,48 @@
+// 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 BASE_TASK_PROMISE_NO_OP_PROMISE_EXECUTOR_H_
+#define BASE_TASK_PROMISE_NO_OP_PROMISE_EXECUTOR_H_
+
+#include "base/macros.h"
+#include "base/task/promise/abstract_promise.h"
+
+namespace base {
+namespace internal {
+
+// An Executor that doesn't do anything.
+class BASE_EXPORT NoOpPromiseExecutor : public AbstractPromise::Executor {
+ public:
+  NoOpPromiseExecutor(bool can_resolve, bool can_reject);
+
+  ~NoOpPromiseExecutor() override;
+
+  static scoped_refptr<internal::AbstractPromise> Create(
+      Location from_here,
+      bool can_resolve,
+      bool can_reject,
+      RejectPolicy reject_policy);
+
+  PrerequisitePolicy GetPrerequisitePolicy() override;
+  bool IsCancelled() const override;
+
+#if DCHECK_IS_ON()
+  ArgumentPassingType ResolveArgumentPassingType() const override;
+  ArgumentPassingType RejectArgumentPassingType() const override;
+  bool CanResolve() const override;
+  bool CanReject() const override;
+#endif
+  void Execute(AbstractPromise* promise) override;
+
+ private:
+#if DCHECK_IS_ON()
+  bool can_resolve_;
+  bool can_reject_;
+#endif
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_NO_OP_PROMISE_EXECUTOR_H_
diff --git a/base/task/promise/small_unique_object.h b/base/task/promise/small_unique_object.h
new file mode 100644
index 0000000..d446d035
--- /dev/null
+++ b/base/task/promise/small_unique_object.h
@@ -0,0 +1,98 @@
+// 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 BASE_TASK_PROMISE_SMALL_UNIQUE_OBJECT_H_
+#define BASE_TASK_PROMISE_SMALL_UNIQUE_OBJECT_H_
+
+#include <cstring>
+
+#include "base/macros.h"
+#include "base/template_util.h"
+
+namespace base {
+namespace internal {
+
+// A container intended for inline storage of a derived virtual object up to a
+// maximum size. This is useful to avoid unnecessary heap allocations.
+template <typename T, size_t MaxSize = sizeof(int*) * 3>
+class BASE_EXPORT SmallUniqueObject {
+ public:
+  SmallUniqueObject() { memset(&union_, 0, sizeof(union_)); }
+
+  template <typename Derived, typename... Args>
+  SmallUniqueObject(in_place_type_t<Derived>, Args&&... args) noexcept {
+    static_assert(std::is_base_of<T, Derived>::value,
+                  "T is not a base of Derived");
+    static_assert(sizeof(Derived) <= MaxSize,
+                  "Derived is too big to be held by SmallUniqueObject");
+    static_assert(sizeof(T) <= MaxSize,
+                  "T is too big to be held by SmallUniqueObject");
+    new (union_.storage) Derived(std::forward<Args>(args)...);
+  }
+
+  ~SmallUniqueObject() { reset(); }
+
+  explicit operator bool() const { return !Empty(); }
+
+  bool Empty() const {
+    for (size_t i = 0; i < kMaxInts; i++) {
+      if (union_.flag[i])
+        return false;
+    }
+    return true;
+  }
+
+  void reset() {
+    if (!Empty()) {
+      get()->~T();
+      memset(&union_, 0, sizeof(union_));
+    }
+  }
+
+  T* get() noexcept {
+    if (Empty())
+      return nullptr;
+    return reinterpret_cast<T*>(union_.storage);
+  }
+
+  const T* get() const noexcept {
+    if (Empty())
+      return nullptr;
+    return reinterpret_cast<const T*>(union_.storage);
+  }
+
+  const T& operator*() const {
+    DCHECK(!Empty());
+    return *get();
+  }
+
+  T& operator*() {
+    DCHECK(!Empty());
+    return *get();
+  }
+
+  const T* operator->() const noexcept {
+    DCHECK(!Empty());
+    return get();
+  }
+
+  T* operator->() noexcept {
+    DCHECK(!Empty());
+    return get();
+  }
+
+ private:
+  static constexpr size_t kMaxInts = MaxSize / sizeof(int);
+  union {
+    // The vtable will be stored somewhere within |storage|, the actual location
+    // is compiler specific but where ever it is, it can't be zero.
+    int flag[kMaxInts];
+    char storage[MaxSize];
+  } union_;
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_SMALL_UNIQUE_OBJECT_H_
diff --git a/base/task_runner.cc b/base/task_runner.cc
index 73130163..0bd9ba99 100644
--- a/base/task_runner.cc
+++ b/base/task_runner.cc
@@ -6,8 +6,10 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/task/promise/abstract_promise.h"
 #include "base/threading/post_task_and_reply_impl.h"
 
 namespace base {
@@ -51,6 +53,14 @@
       from_here, std::move(task), std::move(reply));
 }
 
+bool TaskRunner::PostPromiseInternal(
+    const scoped_refptr<internal::AbstractPromise>& promise,
+    base::TimeDelta delay) {
+  return PostDelayedTask(
+      promise->from_here(),
+      BindOnce(&internal::AbstractPromise::Execute, std::move(promise)), delay);
+}
+
 TaskRunner::TaskRunner() = default;
 
 TaskRunner::~TaskRunner() = default;
diff --git a/base/task_runner.h b/base/task_runner.h
index 1d302ab0..468e1383 100644
--- a/base/task_runner.h
+++ b/base/task_runner.h
@@ -15,6 +15,10 @@
 
 namespace base {
 
+namespace internal {
+class AbstractPromise;
+}  // namespace internal
+
 struct TaskRunnerTraits;
 
 // A TaskRunner is an object that runs posted tasks (in the form of
@@ -133,6 +137,12 @@
                         OnceClosure task,
                         OnceClosure reply);
 
+  // TODO(alexclarke): This should become pure virtual and replace
+  // PostDelayedTask. NB passing by reference to reduce binary size.
+  bool PostPromiseInternal(
+      const scoped_refptr<internal::AbstractPromise>& promise,
+      base::TimeDelta delay);
+
  protected:
   friend struct TaskRunnerTraits;
 
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 1d20e94e..02e2708 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -51,6 +51,8 @@
     "bind_test_util.h",
     "copy_only_int.cc",
     "copy_only_int.h",
+    "do_nothing_promise.cc",
+    "do_nothing_promise.h",
     "fuzzed_data_provider.h",
     "gtest_util.cc",
     "gtest_util.h",
diff --git a/base/test/do_nothing_promise.cc b/base/test/do_nothing_promise.cc
new file mode 100644
index 0000000..6b4709ae
--- /dev/null
+++ b/base/test/do_nothing_promise.cc
@@ -0,0 +1,15 @@
+// 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/test/do_nothing_promise.h"
+
+namespace base {
+
+DoNothingPromiseBuilder::operator scoped_refptr<internal::AbstractPromise>()
+    const {
+  return internal::NoOpPromiseExecutor::Create(from_here, can_resolve,
+                                               can_reject, reject_policy);
+}
+
+}  // namespace base
diff --git a/base/test/do_nothing_promise.h b/base/test/do_nothing_promise.h
new file mode 100644
index 0000000..202d623
--- /dev/null
+++ b/base/test/do_nothing_promise.h
@@ -0,0 +1,41 @@
+// 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 BASE_TEST_DO_NOTHING_PROMISE_H_
+#define BASE_TEST_DO_NOTHING_PROMISE_H_
+
+#include "base/task/promise/no_op_promise_executor.h"
+
+namespace base {
+
+// Creates a promise whose executor doesn't do anything.
+struct DoNothingPromiseBuilder {
+  explicit DoNothingPromiseBuilder(Location from_here) : from_here(from_here) {}
+
+  const Location from_here;
+  bool can_resolve = false;
+  bool can_reject = false;
+  RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection;
+
+  DoNothingPromiseBuilder& SetCanResolve(bool can_resolve) {
+    this->can_resolve = can_resolve;
+    return *this;
+  }
+
+  DoNothingPromiseBuilder& SetCanReject(bool can_reject) {
+    this->can_reject = can_reject;
+    return *this;
+  }
+
+  DoNothingPromiseBuilder& SetRejectPolicy(RejectPolicy reject_policy) {
+    this->reject_policy = reject_policy;
+    return *this;
+  }
+
+  operator scoped_refptr<internal::AbstractPromise>() const;
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_DO_NOTHING_PROMISE_H_
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index a4e230c7..fe25637 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -213,10 +213,10 @@
     # Turns off android lint. Useful for prototyping or for faster local builds.
     disable_android_lint = false
 
-    # Location of aapt2 binary used for app bundles. For now, a more recent version
+    # Location of aapt2 used for app bundles. For now, a more recent version
     # than the one distributed with the Android SDK is required.
-    android_sdk_tools_bundle_aapt2 =
-        "//third_party/android_build_tools/aapt2/aapt2"
+    android_sdk_tools_bundle_aapt2_dir =
+        "//third_party/android_build_tools/aapt2"
 
     # Use R8 for Java optimization rather than ProGuard for all targets. R8 is
     # already used as the default for public targets. This will evenutally be
@@ -370,6 +370,8 @@
   android_gdbserver =
       "$android_ndk_root/prebuilt/$android_prebuilt_arch/gdbserver/gdbserver"
 
+  android_sdk_tools_bundle_aapt2 = "${android_sdk_tools_bundle_aapt2_dir}/aapt2"
+
   # Toolchain stuff ------------------------------------------------------------
 
   android_libcpp_root = "$android_ndk_root/sources/cxx-stl/llvm-libc++"
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 81b40645..2436de0 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -4615,6 +4615,7 @@
     }
 
     # Generate a wrapper script for the bundle.
+    _android_aapt2_dir = android_sdk_tools_bundle_aapt2_dir
     _android_aapt2_path = android_sdk_tools_bundle_aapt2
 
     _bundle_apks_path = "$_bundle_base_path/$_bundle_name.apks"
@@ -4634,7 +4635,7 @@
       data = [
         _bundle_wrapper_script_path,
         _android_aapt2_path,
-        _android_aapt2_path + "/lib64/libc++.so",
+        _android_aapt2_dir + "/lib64/libc++.so",
         _keystore_path,
       ]
       data_deps = [
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 2262e8c..cf9b921 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8913984631952060992
\ No newline at end of file
+8913958623180868512
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 5d0c70db..f8a7b3c0a 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8914004655801404528
\ No newline at end of file
+8913966693334837136
\ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 57df018..56c066a 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -785,9 +785,9 @@
     "//components/signin/core/browser/android:signin_java_test_support",
     "//components/signin/core/browser/android:signin_javatests",
     "//components/sync:sync_java_test_support",
-    "//components/sync:test_support_proto_java",
     "//components/sync/android:sync_java",
     "//components/sync/android:sync_javatests",
+    "//components/sync/protocol:test_support_java",
     "//components/url_formatter/android:url_formatter_java",
     "//components/web_restrictions:provider_java",
     "//content/public/android:content_java",
@@ -1571,7 +1571,7 @@
     "//base:base_java_test_support",
     "//chrome/android/public/profiles:java",
     "//components/offline_items_collection/core:core_java",
-    "//components/sync:test_support_proto_java",
+    "//components/sync/protocol:test_support_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:com_google_protobuf_protobuf_lite_java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index f4526fa..5f2e94a 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -366,6 +366,7 @@
   "javatests/src/org/chromium/chrome/browser/permissions/PermissionTestRule.java",
   "javatests/src/org/chromium/chrome/browser/permissions/QuotaTest.java",
   "javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java",
+  "javatests/src/org/chromium/chrome/browser/photo_picker/DecoderServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/policy/CombinedPolicyProviderTest.java",
   "javatests/src/org/chromium/chrome/browser/portals/PortalsTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/ContextualSuggestionsPreferenceTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncPreferenceUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncPreferenceUtils.java
index 63cec44..ad3e76a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncPreferenceUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncPreferenceUtils.java
@@ -17,6 +17,7 @@
 
 import org.chromium.base.BuildInfo;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.IntentHandler;
@@ -213,6 +214,7 @@
      */
     public static void openGoogleMyAccount(Activity activity) {
         assert ChromeSigninController.get().isSignedIn();
+        RecordUserAction.record("SyncPreferences_ManageGoogleAccountClicked");
         openCustomTabWithURL(activity,
                 String.format(
                         MY_ACCOUNT_URL, ChromeSigninController.get().getSignedInAccountName()));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/DecoderServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/DecoderServiceTest.java
new file mode 100644
index 0000000..298d9bc
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/DecoderServiceTest.java
@@ -0,0 +1,170 @@
+// 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.
+
+package org.chromium.chrome.browser.photo_picker;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.support.test.filters.LargeTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.chrome.browser.ChromeActivity;
+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.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+
+/**
+ * Tests for the out-of-process DecoderService.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class DecoderServiceTest {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    Context mContext;
+
+    // Flag indicating whether we are bound to the service.
+    private boolean mBound;
+
+    private class DecoderServiceCallback extends IDecoderServiceCallback.Stub {
+        // The returned bundle from the decoder.
+        private Bundle mDecodedBundle;
+
+        public boolean resolved() {
+            return mDecodedBundle != null;
+        }
+
+        public Bundle getBundle() {
+            return mDecodedBundle;
+        }
+
+        @Override
+        public void onDecodeImageDone(final Bundle payload) {
+            mDecodedBundle = payload;
+        }
+    }
+
+    IDecoderService mIRemoteService;
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mIRemoteService = IDecoderService.Stub.asInterface(service);
+            mBound = true;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mIRemoteService = null;
+            mBound = false;
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mContext = mActivityTestRule.getActivity();
+    }
+
+    private void startDecoderService() {
+        Intent intent = new Intent(mContext, DecoderService.class);
+        intent.setAction(IDecoderService.class.getName());
+        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return mBound;
+            }
+        });
+    }
+
+    private void decode(String filePath, FileDescriptor fd, int size,
+            final DecoderServiceCallback callback) throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putString(DecoderService.KEY_FILE_PATH, filePath);
+        ParcelFileDescriptor pfd = null;
+        if (fd != null) {
+            pfd = ParcelFileDescriptor.dup(fd);
+            Assert.assertTrue(pfd != null);
+        }
+        bundle.putParcelable(DecoderService.KEY_FILE_DESCRIPTOR, pfd);
+        bundle.putInt(DecoderService.KEY_SIZE, size);
+
+        mIRemoteService.decodeImage(bundle, callback);
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return callback.resolved();
+            }
+        });
+    }
+
+    @Test
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/888931")
+    @LargeTest
+    public void testServiceDecodeNullFileDescriptor() throws Throwable {
+        startDecoderService();
+
+        // Attempt to decode to a 50x50 thumbnail without a valid FileDescriptor (null).
+        DecoderServiceCallback callback = new DecoderServiceCallback();
+        decode("path", null, 50, callback);
+
+        Bundle bundle = callback.getBundle();
+        Assert.assertFalse(
+                "Expected decode to fail", bundle.getBoolean(DecoderService.KEY_SUCCESS));
+        Assert.assertEquals("path", bundle.getString(DecoderService.KEY_FILE_PATH));
+        Assert.assertEquals(null, bundle.getParcelable(DecoderService.KEY_IMAGE_BITMAP));
+        Assert.assertEquals(0, bundle.getLong(DecoderService.KEY_DECODE_TIME));
+    }
+
+    @Test
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/888931")
+    @LargeTest
+    public void testServiceDecodeSimple() throws Exception {
+        startDecoderService();
+
+        File file = new File(UrlUtils.getIsolatedTestFilePath(
+                "chrome/test/data/android/photo_picker/blue100x100.jpg"));
+        FileInputStream inStream = new FileInputStream(file);
+
+        // Attempt to decode a valid 100x100 image file to a 50x50 thumbnail.
+        DecoderServiceCallback callback = new DecoderServiceCallback();
+        decode(file.getPath(), inStream.getFD(), 50, callback);
+
+        Bundle bundle = callback.getBundle();
+        Assert.assertTrue(
+                "Expecting success being returned", bundle.getBoolean(DecoderService.KEY_SUCCESS));
+        Assert.assertEquals(file.getPath(), bundle.getString(DecoderService.KEY_FILE_PATH));
+        Assert.assertFalse("Decoding should take a non-zero amount of time",
+                0 == bundle.getLong(DecoderService.KEY_DECODE_TIME));
+
+        Bitmap decodedBitmap = bundle.getParcelable(DecoderService.KEY_IMAGE_BITMAP);
+        Assert.assertFalse("Decoded bitmap should not be null", null == decodedBitmap);
+        Assert.assertEquals(50, decodedBitmap.getWidth());
+        Assert.assertEquals(50, decodedBitmap.getHeight());
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
index 610240ea..499c034da 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
@@ -197,7 +197,7 @@
     }
 
     @Test
-    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/761060")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/888931")
     @LargeTest
     public void testNoSelection() throws Throwable {
         createDialog(false, Arrays.asList("image/*")); // Multi-select = false.
@@ -215,7 +215,7 @@
     }
 
     @Test
-    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/761060")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/888931")
     @Ignore("crbug/941488")
     @LargeTest
     public void testSingleSelectionPhoto() throws Throwable {
@@ -237,7 +237,7 @@
     }
 
     @Test
-    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/761060")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/888931")
     @Ignore("crbug/941488")
     @LargeTest
     public void testMultiSelectionPhoto() throws Throwable {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 0d99794..5dcc9fb 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2111,6 +2111,7 @@
   sources = [
     "dbus/org.chromium.ChromeFeaturesService.conf",
     "dbus/org.chromium.ComponentUpdaterService.conf",
+    "dbus/org.chromium.CryptohomeKeyDelegate.conf",
     "dbus/org.chromium.DriveFileStreamService.conf",
     "dbus/org.chromium.KioskAppService.conf",
     "dbus/org.chromium.LibvdaService.conf",
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc
index 0108f0e..7a589d6 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.cc
@@ -23,10 +23,7 @@
 }
 
 GURL UnescapeArcUrl(const std::string& escaped_arc_url) {
-  return GURL(net::UnescapeURLComponent(
-      escaped_arc_url,
-      net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
-          net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS));
+  return GURL(net::UnescapeBinaryURLComponent(escaped_arc_url));
 }
 
 GURL ArcUrlToExternalFileUrl(const GURL& arc_url) {
diff --git a/chrome/browser/chromeos/arc/fileapi/chrome_content_provider_url_util.cc b/chrome/browser/chromeos/arc/fileapi/chrome_content_provider_url_util.cc
index 61e0557..e8d30c6 100644
--- a/chrome/browser/chromeos/arc/fileapi/chrome_content_provider_url_util.cc
+++ b/chrome/browser/chromeos/arc/fileapi/chrome_content_provider_url_util.cc
@@ -31,10 +31,7 @@
                         base::CompareCase::SENSITIVE))
     return GURL();
   const std::string escaped = spec.substr(strlen(kChromeContentProviderUrl));
-  return GURL(net::UnescapeURLComponent(
-      escaped,
-      net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
-          net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS));
+  return GURL(net::UnescapeBinaryURLComponent(escaped));
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/dbus/org.chromium.CryptohomeKeyDelegate.conf b/chrome/browser/chromeos/dbus/org.chromium.CryptohomeKeyDelegate.conf
new file mode 100644
index 0000000..d4403000
--- /dev/null
+++ b/chrome/browser/chromeos/dbus/org.chromium.CryptohomeKeyDelegate.conf
@@ -0,0 +1,25 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<!--
+  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.
+-->
+<busconfig>
+  <!-- Allow the Chrome browser process to run this service. -->
+  <policy user="chronos">
+    <allow own="org.chromium.CryptohomeKeyDelegate"/>
+  </policy>
+  <!-- Allow Tast integration tests (running as root) to run this service. -->
+  <policy user="root">
+    <allow own="org.chromium.TestingCryptohomeKeyDelegate"/>
+  </policy>
+
+  <!-- Allow the cryptohomed daemon (running as root) to use this service. -->
+  <policy user="root">
+    <allow send_destination="org.chromium.CryptohomeKeyDelegate"
+           send_interface="org.chromium.CryptohomeKeyDelegateInterface" />
+    <allow send_destination="org.chromium.TestingCryptohomeKeyDelegate"
+           send_interface="org.chromium.CryptohomeKeyDelegateInterface" />
+  </policy>
+</busconfig>
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index a6c8b41c..4486f66 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -1010,6 +1010,11 @@
         TestCase("metadataLargeDrive").EnableDriveFs(),
         TestCase("metadataLargeDrive").EnableDriveFs().EnableMyFilesVolume()));
 
+WRAPPED_INSTANTIATE_TEST_SUITE_P(
+    NavigationList, /* navigation_list.js */
+    FilesAppBrowserTest,
+    ::testing::Values(TestCase("navigationScrollsWhenClipped")));
+
 // Structure to describe an account info.
 struct TestAccountInfo {
   const char* const gaia_id;
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 3cc270e3..e2b1bd7 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -59,7 +59,7 @@
 #include "content/public/test/test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -383,9 +383,9 @@
   // registration id (they increment from 0).
   const int64_t service_worker_registration_id = 1234LL;
 
-  blink::PushSubscriptionOptionsParams options;
+  blink::WebPushSubscriptionOptions options;
   options.user_visible_only = true;
-  options.sender_info = GetTestApplicationServerKey();
+  options.application_server_key = GetTestApplicationServerKey();
   base::RunLoop run_loop;
   push_service()->SubscribeFromWorker(
       requesting_origin, service_worker_registration_id, options,
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index 1103e3d5..b85b48d2 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -53,7 +53,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_switches.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -432,7 +432,7 @@
     int64_t service_worker_registration_id,
     int renderer_id,
     int render_frame_id,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     RegisterCallback callback) {
   PushMessagingAppIdentifier app_identifier =
@@ -483,7 +483,7 @@
 void PushMessagingServiceImpl::SubscribeFromWorker(
     const GURL& requesting_origin,
     int64_t service_worker_registration_id,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     RegisterCallback register_callback) {
   PushMessagingAppIdentifier app_identifier =
       PushMessagingAppIdentifier::FindByServiceWorker(
@@ -539,7 +539,7 @@
 
 void PushMessagingServiceImpl::DoSubscribe(
     const PushMessagingAppIdentifier& app_identifier,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     RegisterCallback register_callback,
     ContentSetting content_setting) {
   if (content_setting != CONTENT_SETTING_ALLOW) {
@@ -553,13 +553,13 @@
 
   GetInstanceIDDriver()
       ->GetInstanceID(app_identifier.app_id())
-      ->GetToken(
-          NormalizeSenderInfo(options.sender_info), kGCMScope,
-          std::map<std::string, std::string>() /* options */,
-          false /* is_lazy */,
-          base::BindOnce(&PushMessagingServiceImpl::DidSubscribe,
-                         weak_factory_.GetWeakPtr(), app_identifier,
-                         options.sender_info, std::move(register_callback)));
+      ->GetToken(NormalizeSenderInfo(options.application_server_key), kGCMScope,
+                 std::map<std::string, std::string>() /* options */,
+                 false /* is_lazy */,
+                 base::BindOnce(&PushMessagingServiceImpl::DidSubscribe,
+                                weak_factory_.GetWeakPtr(), app_identifier,
+                                options.application_server_key,
+                                std::move(register_callback)));
 }
 
 void PushMessagingServiceImpl::SubscribeEnd(
@@ -1015,17 +1015,19 @@
 // Helper methods --------------------------------------------------------------
 
 std::string PushMessagingServiceImpl::NormalizeSenderInfo(
-    const std::string& sender_info) const {
-  // Only encode the |sender_info| when it is a NIST P-256 public key in
-  // uncompressed format, verified through its length and the 0x04 prefix byte.
-  if (sender_info.size() != 65 || sender_info[0] != 0x04)
-    return sender_info;
+    const std::string& application_server_key) const {
+  // Only encode the |application_server_key| when it is a NIST P-256 public key
+  // in uncompressed format, verified through its length and the 0x04 prefix
+  // byte.
+  if (application_server_key.size() != 65 || application_server_key[0] != 0x04)
+    return application_server_key;
 
-  std::string encoded_sender_info;
-  base::Base64UrlEncode(sender_info, base::Base64UrlEncodePolicy::OMIT_PADDING,
-                        &encoded_sender_info);
+  std::string encoded_application_server_key;
+  base::Base64UrlEncode(application_server_key,
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &encoded_application_server_key);
 
-  return encoded_sender_info;
+  return encoded_application_server_key;
 }
 
 // Assumes user_visible always since this is just meant to check
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 1d03322..3feb01e 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -42,7 +42,7 @@
 enum class PushRegistrationStatus;
 }  // namespace mojom
 
-struct PushSubscriptionOptionsParams;
+struct WebPushSubscriptionOptions;
 }  // namespace blink
 
 namespace gcm {
@@ -83,17 +83,16 @@
 
   // content::PushMessagingService implementation:
   GURL GetEndpoint(bool standard_protocol) const override;
-  void SubscribeFromDocument(
-      const GURL& requesting_origin,
-      int64_t service_worker_registration_id,
-      int renderer_id,
-      int render_frame_id,
-      const blink::PushSubscriptionOptionsParams& options,
-      bool user_gesture,
-      RegisterCallback callback) override;
+  void SubscribeFromDocument(const GURL& requesting_origin,
+                             int64_t service_worker_registration_id,
+                             int renderer_id,
+                             int render_frame_id,
+                             const blink::WebPushSubscriptionOptions& options,
+                             bool user_gesture,
+                             RegisterCallback callback) override;
   void SubscribeFromWorker(const GURL& requesting_origin,
                            int64_t service_worker_registration_id,
-                           const blink::PushSubscriptionOptionsParams& options,
+                           const blink::WebPushSubscriptionOptions& options,
                            RegisterCallback callback) override;
   void GetSubscriptionInfo(const GURL& origin,
                            int64_t service_worker_registration_id,
@@ -159,7 +158,7 @@
   // Subscribe methods ---------------------------------------------------------
 
   void DoSubscribe(const PushMessagingAppIdentifier& app_identifier,
-                   const blink::PushSubscriptionOptionsParams& options,
+                   const blink::WebPushSubscriptionOptions& options,
                    RegisterCallback callback,
                    ContentSetting permission_status);
 
diff --git a/chrome/browser/push_messaging/push_messaging_service_unittest.cc b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
index 4ab2165..8cc0dcdd 100644
--- a/chrome/browser/push_messaging/push_messaging_service_unittest.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
@@ -29,7 +29,7 @@
 #include "components/gcm_driver/gcm_profile_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
 
 #if defined(OS_ANDROID)
@@ -168,9 +168,9 @@
 
   // (2) Subscribe for Push Messaging, and verify that we've got the required
   // information in order to be able to create encrypted messages.
-  blink::PushSubscriptionOptionsParams options;
+  blink::WebPushSubscriptionOptions options;
   options.user_visible_only = true;
-  options.sender_info = kTestSenderId;
+  options.application_server_key = kTestSenderId;
   push_service->SubscribeFromWorker(
       origin, kTestServiceWorkerId, options,
       base::Bind(&PushMessagingServiceTest::DidRegister, base::Unretained(this),
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index ea243ae..2855954 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -225,7 +225,7 @@
   height: calc(var(--modes-height) - var(--modes-gradient-padding) * 2);
   justify-content: flex-start;
   overflow: scroll;
-  padding: var(--modes-gradient-padding) 0;
+  padding: var(--modes-gradient-padding) 3px;
   pointer-events: auto;
 }
 
@@ -244,7 +244,7 @@
   line-height: 32px;
   margin: 8px 0;
   padding: 0 12px;
-  text-shadow: 2px 3px 3px rgba(32, 33, 36, 0.3);
+  text-shadow: 0 2px 3px rgba(32, 33, 36, 0.3);
 }
 
 .mode-item>input {
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js b/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js
index 85395c6e..ee5c97e7 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js
@@ -24,6 +24,13 @@
 };
 
 /**
+ * The prefix of thumbnail files.
+ * @type {string}
+ * @const
+ */
+cca.models.FileSystem.THUMBNAIL_PREFIX = 'thumb-';
+
+/**
  * Directory in the internal file system.
  * @type {DirectoryEntry}
  */
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 22fc385..c9e4d46 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -325,9 +325,7 @@
                 restamp>
               <settings-section page-title="$i18n{resetPageTitle}"
                   section="reset">
-                <settings-reset-page prefs="{{prefs}}"
-                    page-visibility="[[pageVisibility.reset]]">
-                </settings-reset-page>
+                <settings-reset-page prefs="{{prefs}}"></settings-reset-page>
               </settings-section>
             </template>
           </div>
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 5dbff8e1..2a1b1036 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -64,7 +64,6 @@
   deps = [
     "os_people_page:closure_compile",
     "os_privacy_page:closure_compile",
-    "os_reset_page:closure_compile",
     "os_settings_main:closure_compile",
     "os_settings_menu:closure_compile",
     "os_settings_page:closure_compile",
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.html b/chrome/browser/resources/settings/chromeos/lazy_load.html
index 8309a33..278048b7 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.html
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.html
@@ -6,7 +6,7 @@
   <link rel="import" href="../downloads_page/downloads_page.html">
   <link rel="import" href="../languages_page/languages_page.html">
   <link rel="import" href="../printing_page/printing_page.html">
-  <link rel="import" href="os_reset_page/os_reset_page.html">
+  <link rel="import" href="../reset_page/reset_page.html">
   <link rel="import" href="os_privacy_page/os_privacy_page.html">
 </body>
 </html>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
deleted file mode 100644
index 85fc4d8..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
+++ /dev/null
@@ -1,36 +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.
-
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_type_check("closure_compile") {
-  deps = [
-    ":os_powerwash_dialog",
-    ":os_reset_page",
-    ":reset_os_proxy",
-  ]
-}
-
-js_library("os_powerwash_dialog") {
-  deps = [
-    ":reset_os_proxy",
-    "../..:lifetime_browser_proxy",
-  ]
-}
-
-js_library("os_reset_page") {
-  deps = [
-    "../..:page_visibility",
-    "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js/cr/ui:focus_without_ink",
-  ]
-}
-
-js_library("reset_os_proxy") {
-  deps = [
-    "//ui/webui/resources/js:cr",
-  ]
-  externs_list = [ "$externs_path/chrome_send.js" ]
-}
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
deleted file mode 100644
index 4fd8ff17..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="reset_os_proxy.html">
-<link rel="import" href="../../lifetime_browser_proxy.html">
-<link rel="import" href="../../settings_shared_css.html">
-
-<dom-module id="os-settings-powerwash-dialog">
-  <template>
-    <style include="settings-shared">
-    </style>
-    <cr-dialog id="dialog" close-text="$i18n{close}"
-        ignore-enter-key>
-      <div slot="title">$i18n{powerwashDialogTitle}</div>
-      <div slot="body">
-        <span>
-          $i18n{powerwashDialogExplanation}
-          <a href="$i18nRaw{powerwashLearnMoreUrl}" target="_blank">
-            $i18n{learnMore}
-          </a>
-        </span>
-      </div>
-      <div slot="button-container">
-        <paper-button class="cancel-button" on-click="onCancelTap_"
-            id="cancel">$i18n{cancel}</paper-button>
-        <paper-button class="action-button" id="powerwash"
-            on-click="onRestartTap_">$i18n{powerwashDialogButton}</paper-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="os_powerwash_dialog.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
deleted file mode 100644
index 809b25e..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-/**
- * @fileoverview
- * 'os-settings-powerwash-dialog' is a dialog shown to request confirmation
- * from the user for a device reset (aka powerwash).
- */
-Polymer({
-  is: 'os-settings-powerwash-dialog',
-
-  properties: {
-    /** @public */
-    requestTpmFirmwareUpdate: {
-      type: Boolean,
-      value: false,
-    }
-  },
-
-  /** @override */
-  attached: function() {
-    settings.ResetOsProxyImpl.getInstance().onPowerwashDialogShow();
-    this.$.dialog.showModal();
-  },
-
-  /** @private */
-  onCancelTap_: function() {
-    this.$.dialog.close();
-  },
-
-  /** @private */
-  onRestartTap_: function() {
-    settings.LifetimeBrowserProxyImpl.getInstance().factoryReset(
-        this.requestTpmFirmwareUpdate);
-  },
-});
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html
deleted file mode 100644
index 79d46be..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
-<link rel="import" href="os_powerwash_dialog.html">
-<link rel="import" href="../../i18n_setup.html">
-
-<dom-module id="os-settings-reset-page">
-  <template>
-    <cr-link-row class="hr" id="powerwash"
-        label="$i18n{powerwashTitle}" on-click="onShowPowerwashDialog_"
-        sub-label="$i18n{powerwashDescription}"></cr-link-row>
-    <template is="dom-if" if="[[showPowerwashDialog_]]" restamp>
-      <settings-powerwash-dialog on-close="onPowerwashDialogClose_">
-      </settings-powerwash-dialog>
-    </template>
-  </template>
-  <script src="os_reset_page.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js
deleted file mode 100644
index 12a8142..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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.
-
-/**
- * @fileoverview
- * 'os-settings-reset-page' is the OS settings page containing reset
- * settings.
- */
-Polymer({
-  is: 'os-settings-reset-page',
-
-
-  properties: {
-    /** @private */
-    showPowerwashDialog_: Boolean,
-  },
-
-  /** @private */
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onShowPowerwashDialog_: function(e) {
-    e.preventDefault();
-    this.showPowerwashDialog_ = true;
-  },
-
-  /** @private */
-  onPowerwashDialogClose_: function() {
-    this.showPowerwashDialog_ = false;
-    cr.ui.focusWithoutInk(assert(this.$.powerwash));
-  },
-});
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.html b/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.html
deleted file mode 100644
index 77b9e53..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<link rel="import" href="chrome://resources/html/cr.html">
-<script src="reset_os_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.js b/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.js
deleted file mode 100644
index 83929c9..0000000
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/reset_os_proxy.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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.
-
-cr.define('settings', function() {
-  /** @interface */
-  class ResetOsProxy {
-    /**
-     * A method to be called when the reset powerwash dialog is shown.
-     */
-    onPowerwashDialogShow() {}
-
-    /**
-     * Initiates a factory reset and restarts ChromeOS.
-     */
-    requestFactoryResetRestart() {}
-  }
-
-  /**
-   * @implements {settings.ResetOsProxy}
-   */
-  class ResetOsProxyImpl {
-    /** @override */
-    onPowerwashDialogShow() {
-      chrome.send('onPowerwashDialogShow');
-    }
-
-    /** @override */
-    requestFactoryResetRestart() {
-      chrome.send('requestFactoryResetRestart');
-    }
-  }
-
-  cr.addSingletonGetter(ResetOsProxyImpl);
-
-  return {
-    ResetOsProxy: ResetOsProxy,
-    ResetOsProxyImpl: ResetOsProxyImpl,
-  };
-});
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 6824edc..099dd4ac 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -83,6 +83,10 @@
     <template is="dom-if" if="[[showBasicPage_(
         currentRoute_, inSearchMode, hasExpandedSection_)]]">
       <div id="basicPage">
+        <template is="dom-if" if="[[showResetProfileBanner_]]" restamp>
+          <settings-reset-profile-banner on-close="onResetProfileBannerClosed_">
+          </settings-reset-profile-banner>
+        </template>
         <div id="secondaryUserBanner" hidden="[[!showSecondaryUserBanner_]]">
           <div id="secondaryUserIcon"></div>
           <div class="flex">$i18n{secondaryUserBannerText}</div>
@@ -263,7 +267,7 @@
                 restamp>
               <settings-section page-title="$i18n{resetPageTitle}"
                   section="reset">
-                <os-settings-reset-page></os-settings-reset-page>
+                <settings-reset-page prefs="{{prefs}}"></settings-reset-page>
               </settings-section>
             </template>
           </div>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
index f6591654..024e64d 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
@@ -67,6 +67,18 @@
     },
 
     /**
+     * True if the basic page should currently display the reset profile banner.
+     * @private {boolean}
+     */
+    showResetProfileBanner_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('showResetProfileBanner');
+      },
+    },
+
+    // <if expr="chromeos">
+    /**
      * Whether the user is a secondary user. Computed so that it is calculated
      * correctly after loadTimeData is available.
      * @private
@@ -75,6 +87,7 @@
       type: Boolean,
       computed: 'computeShowSecondaryUserBanner_(hasExpandedSection_)',
     },
+    // </if>
 
     /** @private {!settings.Route|undefined} */
     currentRoute_: Object,
@@ -194,6 +207,7 @@
     });
   },
 
+  // <if expr="chromeos">
   /**
    * @return {boolean}
    * @private
@@ -202,6 +216,12 @@
     return !this.hasExpandedSection_ &&
         loadTimeData.getBoolean('isSecondaryUser');
   },
+  // </if>
+
+  /** @private */
+  onResetProfileBannerClosed_: function() {
+    this.showResetProfileBanner_ = false;
+  },
 
   /**
    * @param {!AndroidAppsInfo} info
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 11f31e9..25d5096 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -464,23 +464,37 @@
                  type="chrome_html"
                  preprocess="true" />
       <structure name="IDR_OS_SETTINGS_POWERWASH_DIALOG_HTML"
-                 file="chromeos/os_reset_page/os_powerwash_dialog.html"
+                 file="reset_page/powerwash_dialog.html"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_POWERWASH_DIALOG_JS"
-                 file="chromeos/os_reset_page/os_powerwash_dialog.js"
+                 file="reset_page/powerwash_dialog.js"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_RESET_PAGE_HTML"
-                 file="chromeos/os_reset_page/os_reset_page.html"
+                 file="reset_page/reset_page.html"
                  type="chrome_html"
                  preprocess="true" />
       <structure name="IDR_OS_SETTINGS_RESET_PAGE_JS"
-                 file="chromeos/os_reset_page/os_reset_page.js"
+                 file="reset_page/reset_page.js"
+                 preprocess="true"
                  type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_RESET_OS_PROXY_JS"
-                 file="chromeos/os_reset_page/reset_os_proxy.js"
+      <structure name="IDR_OS_SETTINGS_RESET_PROFILE_DIALOG_HTML"
+                 file="reset_page/reset_profile_dialog.html"
                  type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_RESET_OS_PROXY_HTML"
-                 file="chromeos/os_reset_page/reset_os_proxy.html"
+      <structure name="IDR_OS_SETTINGS_RESET_PROFILE_DIALOG_JS"
+                 file="reset_page/reset_profile_dialog.js"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_RESET_PROFILE_BANNER_HTML"
+                 file="reset_page/reset_profile_banner.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_RESET_PROFILE_BANNER_JS"
+                 file="reset_page/reset_profile_banner.js"
+                 type="chrome_html"/>
+      <structure name="IDR_OS_SETTINGS_RESET_BROWSER_PROXY_JS"
+                 file="reset_page/reset_browser_proxy.js"
+                 preprocess="true"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_RESET_BROWSER_PROXY_HTML"
+                 file="reset_page/reset_browser_proxy.html"
                  type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_LANGUAGES_HTML"
                  file="languages_page/languages.html"
diff --git a/chrome/browser/resources/settings/page_visibility.js b/chrome/browser/resources/settings/page_visibility.js
index ab17349..823fb641 100644
--- a/chrome/browser/resources/settings/page_visibility.js
+++ b/chrome/browser/resources/settings/page_visibility.js
@@ -21,7 +21,7 @@
  *   onStartup: (boolean|undefined),
  *   people: (boolean|undefined|PeoplePageVisibility),
  *   privacy: (boolean|undefined|PrivacyPageVisibility),
- *   reset:(boolean|undefined|ResetPageVisibility),
+ *   reset:(boolean|undefined),
  * }}
  */
 let PageVisibility;
@@ -63,13 +63,6 @@
  */
 let PrivacyPageVisibility;
 
-/**
- * @typedef {{
- *   powerwash: boolean,
- * }}
- */
-let ResetPageVisibility;
-
 cr.define('settings', function() {
   /**
    * Dictionary defining page visibility.
@@ -102,9 +95,7 @@
       autofill: false,
       people: false,
       onStartup: false,
-      reset: {
-        powerwash: false,
-      },
+      reset: false,
       appearance: {
         setWallpaper: false,
         setTheme: false,
@@ -142,9 +133,7 @@
         manageUsers: showOSSettings,
       },
       onStartup: true,
-      reset: {
-        powerwash: showOSSettings,
-      },
+      reset: true,
       appearance: {
         setWallpaper: showOSSettings,
         setTheme: true,
diff --git a/chrome/browser/resources/settings/reset_page/BUILD.gn b/chrome/browser/resources/settings/reset_page/BUILD.gn
index e8ce13f..53cc82a 100644
--- a/chrome/browser/resources/settings/reset_page/BUILD.gn
+++ b/chrome/browser/resources/settings/reset_page/BUILD.gn
@@ -23,7 +23,6 @@
 js_library("reset_page") {
   deps = [
     ":reset_profile_dialog",
-    "..:page_visibility",
     "..:route",
     "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render",
     "//ui/webui/resources/js:assert",
diff --git a/chrome/browser/resources/settings/reset_page/reset_page.js b/chrome/browser/resources/settings/reset_page/reset_page.js
index 2d1606c..22cf6ae 100644
--- a/chrome/browser/resources/settings/reset_page/reset_page.js
+++ b/chrome/browser/resources/settings/reset_page/reset_page.js
@@ -25,20 +25,15 @@
     prefs: Object,
 
     // <if expr="chromeos">
-    /**
-     * Dictionary defining page visibility.
-     * @type {!ResetPageVisibility}
-     */
-    pageVisibility: Object,
-
     /** @private */
     showPowerwashDialog_: Boolean,
-
-    /** @private */
-    allowPowerwash_:
-        {type: Boolean, value: loadTimeData.getBoolean('allowPowerwash')},
     // </if>
 
+    /** @private */
+    allowPowerwash_: {
+      type: Boolean,
+      value: cr.isChromeOS ? loadTimeData.getBoolean('allowPowerwash') : false
+    },
 
     // <if expr="_google_chrome and is_win">
     /** @private */
@@ -51,15 +46,6 @@
     // </if>
   },
 
-  // <if expr="chromeos">
-  /** @override */
-  ready: function() {
-    // TODO(hsuregan): Remove when OS settings migration is complete.
-    this.allowPowerwash_ =
-        this.allowPowerwash_ && this.pageVisibility.powerwash;
-  },
-  // </if>
-
   /**
    * settings.RouteObserverBehavior
    * @param {!settings.Route} route
diff --git a/chrome/browser/sync/test/integration/migration_test.cc b/chrome/browser/sync/test/integration/migration_test.cc
index dcf383b9..37acdc4 100644
--- a/chrome/browser/sync/test/integration/migration_test.cc
+++ b/chrome/browser/sync/test/integration/migration_test.cc
@@ -96,6 +96,14 @@
     DCHECK(GetSyncService(0));
     syncer::ModelTypeSet preferred_data_types =
         GetSyncService(0)->GetPreferredDataTypes();
+
+    // Make sure all clients have the same preferred data types.
+    for (int i = 1; i < num_clients(); ++i) {
+      const syncer::ModelTypeSet other_preferred_data_types =
+          GetSyncService(i)->GetPreferredDataTypes();
+      EXPECT_EQ(other_preferred_data_types, preferred_data_types);
+    }
+
     preferred_data_types.RemoveAll(syncer::ProxyTypes());
 
     // Supervised user data types will be "unready" during this test, so we
@@ -115,12 +123,6 @@
     // Doesn't make sense to migrate commit only types.
     preferred_data_types.RemoveAll(syncer::CommitOnlyTypes());
 
-    // Make sure all clients have the same preferred data types.
-    for (int i = 1; i < num_clients(); ++i) {
-      const syncer::ModelTypeSet other_preferred_data_types =
-          GetSyncService(i)->GetPreferredDataTypes();
-      EXPECT_EQ(other_preferred_data_types, preferred_data_types);
-    }
     return preferred_data_types;
   }
 
@@ -359,32 +361,22 @@
 
 // Migrate every datatype in sequence; the catch being that the server
 // will only tell the client about the migrations one at a time.
-// TODO(rsimha): This test takes longer than 60 seconds, and will cause tree
-// redness due to sharding.
-// Re-enable this test after syncer::kInitialBackoffShortRetrySeconds is reduced
-// to zero.
-IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
-                       DISABLED_MigrationHellWithoutNigori) {
+IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigrationHellWithoutNigori) {
   ASSERT_TRUE(SetupClients());
   MigrationList migration_list = GetPreferredDataTypesList();
-  // Let the first nudge be a datatype that's neither prefs nor
-  // bookmarks.
+  // Let the first nudge be a datatype that's neither prefs nor bookmarks.
   migration_list.push_front(MakeSet(syncer::THEMES));
+  ASSERT_EQ(MakeSet(syncer::NIGORI), migration_list.back());
+  migration_list.pop_back();
   RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
 }
 
-IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
-                       DISABLED_MigrationHellWithNigori) {
+IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigrationHellWithNigori) {
   ASSERT_TRUE(SetupClients());
   MigrationList migration_list = GetPreferredDataTypesList();
-  // Let the first nudge be a datatype that's neither prefs nor
-  // bookmarks.
+  // Let the first nudge be a datatype that's neither prefs nor bookmarks.
   migration_list.push_front(MakeSet(syncer::THEMES));
-  // Pop off one so that we don't migrate all data types; the syncer
-  // freaks out if we do that (see http://crbug.com/94882).
-  ASSERT_GE(migration_list.size(), 2u);
-  ASSERT_NE(MakeSet(syncer::NIGORI), migration_list.back());
-  migration_list.back() = MakeSet(syncer::NIGORI);
+  ASSERT_EQ(MakeSet(syncer::NIGORI), migration_list.back());
   RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
 }
 
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index 4ec3843..44c08c63 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -11,6 +11,8 @@
 #include "base/macros.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/browser/chromeos/file_manager/file_tasks_notifier.h"
+#include "chrome/browser/chromeos/file_manager/file_tasks_notifier_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
@@ -72,9 +74,21 @@
         app_list_features::kEnableAdaptiveResultRanker, "boost_coefficient",
         0.1);
   }
+
+  profile_ = profile;
+  if (auto* notifier =
+          file_manager::file_tasks::FileTasksNotifier::GetForProfile(profile_))
+    notifier->AddObserver(this);
+  /*file_tasks_observer_.Add(
+      file_manager::file_tasks::FileTasksNotifierFactory::GetInstance()
+          ->GetForProfile(profile));*/
 }
 
-SearchResultRanker::~SearchResultRanker() = default;
+SearchResultRanker::~SearchResultRanker() {
+  if (auto* notifier =
+          file_manager::file_tasks::FileTasksNotifier::GetForProfile(profile_))
+    notifier->RemoveObserver(this);
+}
 
 void SearchResultRanker::FetchRankings() {
   // The search controller potentially calls SearchController::FetchResults
@@ -125,4 +139,9 @@
   }
 }
 
+void SearchResultRanker::OnFilesOpened(
+    const std::vector<FileOpenEvent>& file_opens) {
+  // TODO(959679): route file open events to a model as training signals.
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
index 3b825c0..788447c9 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.h
@@ -6,10 +6,13 @@
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_SEARCH_RESULT_RANKER_H_
 
 #include "base/containers/flat_map.h"
+#include "base/scoped_observer.h"
 #include "base/time/time.h"
+#include "chrome/browser/chromeos/file_manager/file_tasks_notifier.h"
+#include "chrome/browser/chromeos/file_manager/file_tasks_observer.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/mixer.h"
 
-class Profile;
 
 namespace app_list {
 
@@ -22,10 +25,10 @@
 // FetchRankings queries each model for ranking results. Rank modifies the
 // scores of provided search results, which are intended to be the output of a
 // search provider.
-class SearchResultRanker {
+class SearchResultRanker : file_manager::file_tasks::FileTasksObserver {
  public:
   explicit SearchResultRanker(Profile* profile);
-  ~SearchResultRanker();
+  ~SearchResultRanker() override;
 
   // Queries each model contained with the SearchResultRanker for its results,
   // and saves them for use on subsequent calls to Rank().
@@ -43,6 +46,9 @@
   // relevant ChromeSearchResult's ID.
   void Train(const std::string& id, RankingItemType type);
 
+  // file_manager::file_tasks::FileTaskObserver:
+  void OnFilesOpened(const std::vector<FileOpenEvent>& file_opens) override;
+
  private:
   // Records the time of the last call to FetchRankings() and is used to
   // limit the number of queries to the models within a short timespan.
@@ -62,6 +68,11 @@
 
   // TODO(931149): Move the AppSearchResultRanker instance and associated logic
   // to here.
+
+  Profile* profile_;
+  /*ScopedObserver<file_manager::file_tasks::FileTasksNotifier,
+                 file_manager::file_tasks::FileTasksObserver>
+      file_tasks_observer_{this};*/
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 5c81e15..f574695c 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -693,8 +693,7 @@
       // make the index refer to a different tab.
       auto gesture_type = user_initiated ? TabStripModel::GestureType::kOther
                                          : TabStripModel::GestureType::kNone;
-      params->browser->tab_strip_model()->ActivateTabAt(singleton_index,
-                                                        {gesture_type});
+      bool should_close_this_tab = false;
       if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB) {
         // Close orphaned NTP (and the like) with no history when the user
         // switches away from them.
@@ -704,11 +703,18 @@
              params->source_contents->GetLastCommittedURL().spec() !=
                  chrome::kChromeSearchLocalNtpUrl &&
              params->source_contents->GetLastCommittedURL().spec() !=
-                 url::kAboutBlankURL))
+                 url::kAboutBlankURL)) {
+          // Blur location bar before state save in ActivateTabAt() below.
           params->source_contents->Focus();
-        else
-          params->source_contents->Close();
+        } else {
+          should_close_this_tab = true;
+        }
       }
+      params->browser->tab_strip_model()->ActivateTabAt(singleton_index,
+                                                        {gesture_type});
+      // Close tab after switch so index remains correct.
+      if (should_close_this_tab)
+        params->source_contents->Close();
     }
   }
 
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index ee0837d..4ec41aa 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -32,6 +32,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/navigation_handle.h"
@@ -807,6 +808,33 @@
   EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
 }
 
+// Make sure that switching tabs preserves the post-focus state (of the
+// content area) of the previous tab.
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, SaveAfterFocusTabSwitchTest) {
+  GURL first_url("chrome://dino/");
+  GURL second_url("chrome://history/");
+
+  NavigateHelper(first_url, browser(), WindowOpenDisposition::CURRENT_TAB,
+                 true);
+
+  // Generate history so the tab isn't closed.
+  NavigateHelper(second_url, browser(),
+                 WindowOpenDisposition::NEW_FOREGROUND_TAB, true);
+
+  LocationBar* location_bar = browser()->window()->GetLocationBar();
+  location_bar->FocusLocation(true);
+
+  NavigateHelper(first_url, browser(), WindowOpenDisposition::SWITCH_TO_TAB,
+                 false);
+
+  browser()->tab_strip_model()->ActivateTabAt(
+      1, {TabStripModel::GestureType::kOther});
+
+  OmniboxView* omnibox_view = location_bar->GetOmniboxView();
+  EXPECT_EQ(omnibox_view->model()->focus_state(),
+            OmniboxFocusState::OMNIBOX_FOCUS_NONE);
+}
+
 // This test verifies that we're picking the correct browser and tab to
 // switch to. It verifies that we don't recommend the active tab, and that,
 // when switching, we don't mistakenly pick the current browser.
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index c3a805e..d521712b 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -410,7 +410,7 @@
   bool sync_error =
       active_item ? AddSyncErrorViewIfNeeded(*active_item) : false;
 
-  if (!(dice_enabled_ && sync_error)) {
+  if (!sync_error || !dice_enabled_) {
     // Guest windows don't have an active profile.
     if (active_item)
       AddCurrentProfileView(*active_item, /* is_guest = */ false);
@@ -448,10 +448,19 @@
 
   if (dice_enabled_) {
     AddDiceSyncErrorView(avatar_item, error, button_string_id);
-    return true;
+  } else {
+    AddPreDiceSyncErrorView(avatar_item, error, button_string_id,
+                            content_string_id);
   }
 
-  // Create pre-dice sync error view.
+  return true;
+}
+
+void ProfileChooserView::AddPreDiceSyncErrorView(
+    const AvatarMenu::Item& avatar_item,
+    sync_ui_util::AvatarSyncErrorType error,
+    int button_string_id,
+    int content_string_id) {
   AddMenuGroup();
   auto sync_problem_icon = std::make_unique<views::ImageView>();
   sync_problem_icon->SetImage(gfx::CreateVectorIcon(
@@ -470,8 +479,6 @@
     // ButtonPressed().
     sync_error_button_->SetID(error);
   }
-
-  return true;
 }
 
 void ProfileChooserView::AddDiceSyncErrorView(
@@ -567,27 +574,55 @@
         IDS_PROFILES_EDIT_SIGNED_IN_PROFILE_ACCESSIBLE_NAME, profile_name,
         avatar_item.username));
   } else {
-    AddMenuGroup(false /* add_separator */);
     bool is_signin_allowed =
         profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed);
-    if (!dice_enabled_ && is_signin_allowed) {
-      CreateAndAddLabel(l10n_util::GetStringUTF16(IDS_PROFILES_SIGNIN_PROMO));
-
-      signin_current_profile_button_ = CreateAndAddBlueButton(
-          l10n_util::GetStringFUTF16(
-              IDS_SYNC_START_SYNC_BUTTON_LABEL,
-              l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
-          true /* md_style */);
-
-      signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
-          signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
-    }
+    // For the dice promo equivalent, see AddDiceSigninPromo() call sites.
+    if (!dice_enabled_ && is_signin_allowed)
+      AddPreDiceSigninPromo();
 
     current_profile_card_->SetAccessibleName(l10n_util::GetStringFUTF16(
         IDS_PROFILES_EDIT_PROFILE_ACCESSIBLE_NAME, profile_name));
   }
 }
 
+void ProfileChooserView::AddPreDiceSigninPromo() {
+  AddMenuGroup(false /* add_separator */);
+  CreateAndAddLabel(l10n_util::GetStringUTF16(IDS_PROFILES_SIGNIN_PROMO));
+
+  signin_current_profile_button_ = CreateAndAddBlueButton(
+      l10n_util::GetStringFUTF16(
+          IDS_SYNC_START_SYNC_BUTTON_LABEL,
+          l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
+      true /* md_style */);
+
+  signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
+      signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
+}
+
+void ProfileChooserView::AddDiceSigninPromo() {
+  AddMenuGroup();
+
+  // Show promo illustration + text when there is no promo account.
+  if (GetDiceSigninPromoShowCount() <=
+      kDiceSigninPromoIllustrationShowCountMax) {
+    // Add the illustration.
+    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+    std::unique_ptr<NonAccessibleImageView> illustration =
+        std::make_unique<NonAccessibleImageView>();
+    illustration->SetImage(
+        *rb.GetNativeImageNamed(IDR_PROFILES_DICE_TURN_ON_SYNC).ToImageSkia());
+    AddViewItem(std::move(illustration));
+  }
+  // Add the promo text.
+  CreateAndAddLabel(l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PROMO));
+
+  // Create a sign-in button without account information.
+  std::unique_ptr<DiceSigninButtonView> signin_button =
+      std::make_unique<DiceSigninButtonView>(this);
+  dice_signin_button_view_ = CreateAndAddDiceSigninButton();
+  signin_current_profile_button_ = dice_signin_button_view_->signin_button();
+}
+
 void ProfileChooserView::AddDiceSigninView() {
   IncrementDiceSigninPromoShowCount();
   // Create a view that holds an illustration, a promo text and a button to turn
@@ -603,29 +638,9 @@
       promo_account_available);
 
   if (!promo_account_available) {
-    AddMenuGroup();
-
-    // Show promo illustration+text when there is no promo account.
-    if (GetDiceSigninPromoShowCount() <=
-        kDiceSigninPromoIllustrationShowCountMax) {
-      // Add the illustration.
-      ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-      std::unique_ptr<NonAccessibleImageView> illustration =
-          std::make_unique<NonAccessibleImageView>();
-      illustration->SetImage(
-          *rb.GetNativeImageNamed(IDR_PROFILES_DICE_TURN_ON_SYNC)
-               .ToImageSkia());
-      AddViewItem(std::move(illustration));
-    }
-    // Add the promo text.
-    CreateAndAddLabel(l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PROMO));
-
-    // Create a sign-in button without account information.
-    std::unique_ptr<DiceSigninButtonView> signin_button =
-        std::make_unique<DiceSigninButtonView>(this);
-    dice_signin_button_view_ = CreateAndAddDiceSigninButton();
-    signin_current_profile_button_ = dice_signin_button_view_->signin_button();
-
+    // For the pre-dice promo equivalent, see AddPreDiceSigninPromo() call
+    // sites.
+    AddDiceSigninPromo();
     return;
   }
 
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.h b/chrome/browser/ui/views/profiles/profile_chooser_view.h
index 6421116c..79201aca 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.h
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.h
@@ -108,12 +108,25 @@
   // Returns true if header is created.
   bool AddSyncErrorViewIfNeeded(const AvatarMenu::Item& avatar_item);
 
+  // Adds a view showing a sync error and an error button, when dice is not
+  // enabled.
+  void AddPreDiceSyncErrorView(const AvatarMenu::Item& avatar_item,
+                               sync_ui_util::AvatarSyncErrorType error,
+                               int button_string_id,
+                               int content_string_id);
+
   // Adds a view showing the profile associated with |avatar_item| and an error
-  // button below.
+  // button below, when dice is enabled.
   void AddDiceSyncErrorView(const AvatarMenu::Item& avatar_item,
                             sync_ui_util::AvatarSyncErrorType error,
                             int button_string_id);
 
+  // Adds a promo for signin, if dice is not enabled.
+  void AddPreDiceSigninPromo();
+
+  // Adds a promo for signin, if dice is enabled.
+  void AddDiceSigninPromo();
+
   // Clean-up done after an action was performed in the ProfileChooser.
   void PostActionPerformed(ProfileMetrics::ProfileDesktopMenu action_performed);
 
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 8dcb57e3..9ff78d7 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -34,11 +34,7 @@
   const auto& rp_id = dialog_model->relying_party_id();
   DCHECK(!rp_id.empty());
   GURL rp_id_url(kRpIdUrlPrefix + rp_id);
-  auto max_static_string_length = gfx::GetStringWidthF(
-      l10n_util::GetStringUTF16(IDS_WEBAUTHN_GENERIC_TITLE), gfx::FontList(),
-      gfx::Typesetter::DEFAULT);
-  return url_formatter::ElideHost(rp_id_url, gfx::FontList(),
-                                  kDialogWidth - max_static_string_length);
+  return url_formatter::ElideHost(rp_id_url, gfx::FontList(), kDialogWidth);
 }
 
 // Possibly returns a resident key warning if the model indicates that it's
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
index 6cd9a4d..fdcd1e9 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/installable/installable_manager.h"
 #include "chrome/browser/profiles/profile.h"
@@ -44,9 +45,11 @@
 
 BookmarkAppInstallationTask::BookmarkAppInstallationTask(
     Profile* profile,
+    web_app::AppRegistrar* registrar,
     web_app::InstallFinalizer* install_finalizer,
     web_app::InstallOptions install_options)
     : profile_(profile),
+      registrar_(registrar),
       install_finalizer_(install_finalizer),
       externally_installed_app_prefs_(profile_->GetPrefs()),
       install_options_(std::move(install_options)) {}
@@ -72,6 +75,18 @@
 void BookmarkAppInstallationTask::InstallPlaceholder(ResultCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  base::Optional<web_app::AppId> app_id =
+      externally_installed_app_prefs_.LookupPlaceholderAppId(
+          install_options_.url);
+  if (app_id.has_value() && registrar_->IsInstalled(app_id.value())) {
+    // No need to install a placeholder app again.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(callback),
+                       Result(web_app::InstallResultCode::kSuccess, app_id)));
+    return;
+  }
+
   WebApplicationInfo web_app_info;
   web_app_info.title = base::UTF8ToUTF16(install_options_.url.spec());
   web_app_info.app_url = install_options_.url;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h
index 0dd8c2e..2c568aa 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/components/install_options.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
@@ -57,6 +58,7 @@
   // policy, etc.
   explicit BookmarkAppInstallationTask(
       Profile* profile,
+      web_app::AppRegistrar* registrar,
       web_app::InstallFinalizer* install_finalizer,
       web_app::InstallOptions install_options);
 
@@ -76,6 +78,7 @@
                          web_app::InstallResultCode code);
 
   Profile* profile_;
+  web_app::AppRegistrar* registrar_;
   web_app::InstallFinalizer* install_finalizer_;
 
   web_app::ExternallyInstalledWebAppPrefs externally_installed_app_prefs_;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
index e0213b7..094bfe10 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
@@ -24,16 +24,19 @@
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/installable/installable_data.h"
 #include "chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
+#include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_data_retriever.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
+#include "chrome/browser/web_applications/test/test_app_registrar.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
-#include "chrome/browser/web_applications/test/test_install_finalizer.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/crx_file/id_util.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/web_contents_tester.h"
 #include "extensions/common/constants.h"
@@ -112,6 +115,135 @@
   DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppHelper);
 };
 
+class TestBookmarkAppInstallFinalizer : public web_app::InstallFinalizer {
+ public:
+  explicit TestBookmarkAppInstallFinalizer(web_app::TestAppRegistrar* registrar)
+      : registrar_(registrar) {}
+  ~TestBookmarkAppInstallFinalizer() override = default;
+
+  // Returns what would be the AppId if an app is installed with |url|.
+  web_app::AppId GetAppIdForUrl(const GURL& url) {
+    return crx_file::id_util::GenerateId("fake_app_id_for:" + url.spec());
+  }
+
+  void SetNextFinalizeInstallResult(const GURL& url,
+                                    web_app::InstallResultCode code) {
+    DCHECK(!base::ContainsKey(next_finalize_install_results_, url));
+
+    web_app::AppId app_id;
+    if (code == web_app::InstallResultCode::kSuccess) {
+      app_id = GetAppIdForUrl(url);
+    }
+    next_finalize_install_results_[url] = {app_id, code};
+  }
+
+  void SetNextCreateOsShortcutsResult(const web_app::AppId& app_id,
+                                      bool shortcut_created) {
+    DCHECK(!base::ContainsKey(next_create_os_shortcuts_results_, app_id));
+    next_create_os_shortcuts_results_[app_id] = shortcut_created;
+  }
+
+  const std::vector<WebApplicationInfo>& web_app_info_list() {
+    return web_app_info_list_;
+  }
+
+  const std::vector<FinalizeOptions>& finalize_options_list() {
+    return finalize_options_list_;
+  }
+
+  size_t num_create_os_shortcuts_calls() {
+    return num_create_os_shortcuts_calls_;
+  }
+
+  // InstallFinalizer
+  void FinalizeInstall(const WebApplicationInfo& web_app_info,
+                       const FinalizeOptions& options,
+                       InstallFinalizedCallback callback) override {
+    DCHECK(base::ContainsKey(next_finalize_install_results_,
+                             web_app_info.app_url));
+
+    web_app_info_list_.push_back(web_app_info);
+    finalize_options_list_.push_back(options);
+
+    web_app::AppId app_id;
+    web_app::InstallResultCode code;
+    std::tie(app_id, code) =
+        next_finalize_install_results_[web_app_info.app_url];
+    next_finalize_install_results_.erase(web_app_info.app_url);
+
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            base::BindLambdaForTesting(
+                [&, app_id, code](InstallFinalizedCallback callback) {
+                  registrar_->AddAsInstalled(app_id);
+                  std::move(callback).Run(app_id, code);
+                }),
+            std::move(callback)));
+  }
+
+  bool CanCreateOsShortcuts() const override { return true; }
+
+  void CreateOsShortcuts(const web_app::AppId& app_id,
+                         bool add_to_desktop,
+                         CreateOsShortcutsCallback callback) override {
+    DCHECK(base::ContainsKey(next_create_os_shortcuts_results_, app_id));
+    ++num_create_os_shortcuts_calls_;
+    bool shortcut_created = next_create_os_shortcuts_results_[app_id];
+    next_create_os_shortcuts_results_.erase(app_id);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), shortcut_created));
+  }
+
+  bool CanPinAppToShelf() const override { return true; }
+
+  void PinAppToShelf(const web_app::AppId& app_id) override {
+    ++num_pin_app_to_shelf_calls_;
+  }
+
+  bool CanReparentTab(const web_app::AppId& app_id,
+                      bool shortcut_created) const override {
+    NOTIMPLEMENTED();
+    return true;
+  }
+
+  void ReparentTab(const web_app::AppId& app_id,
+                   content::WebContents* web_contents) override {
+    NOTIMPLEMENTED();
+  }
+
+  bool CanRevealAppShim() const override {
+    NOTIMPLEMENTED();
+    return true;
+  }
+
+  void RevealAppShim(const web_app::AppId& app_id) override {
+    NOTIMPLEMENTED();
+  }
+
+  bool CanSkipAppUpdateForSync(
+      const web_app::AppId& app_id,
+      const WebApplicationInfo& web_app_info) const override {
+    NOTIMPLEMENTED();
+    return true;
+  }
+
+ private:
+  web_app::TestAppRegistrar* registrar_ = nullptr;
+
+  std::vector<WebApplicationInfo> web_app_info_list_;
+  std::vector<FinalizeOptions> finalize_options_list_;
+
+  size_t num_create_os_shortcuts_calls_ = 0;
+  size_t num_pin_app_to_shelf_calls_ = 0;
+
+  std::map<GURL, std::pair<web_app::AppId, web_app::InstallResultCode>>
+      next_finalize_install_results_;
+  std::map<web_app::AppId, bool> next_create_os_shortcuts_results_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppInstallFinalizer);
+};
+
 class BookmarkAppInstallationTaskTest : public ChromeRenderViewHostTestHarness {
  public:
   BookmarkAppInstallationTaskTest() {}
@@ -121,7 +253,10 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
 
-    install_finalizer_ = std::make_unique<web_app::TestInstallFinalizer>();
+    registrar_ =
+        std::make_unique<web_app::TestAppRegistrar>(/*profile=*/nullptr);
+    install_finalizer_ =
+        std::make_unique<TestBookmarkAppInstallFinalizer>(registrar_.get());
 
     // CrxInstaller in BookmarkAppInstaller needs an ExtensionService, so
     // create one for the profile.
@@ -139,7 +274,9 @@
     return *test_helper_;
   }
 
-  web_app::TestInstallFinalizer* install_finalizer() {
+  web_app::TestAppRegistrar* registrar() { return registrar_.get(); }
+
+  TestBookmarkAppInstallFinalizer* install_finalizer() {
     return install_finalizer_.get();
   }
 
@@ -174,7 +311,8 @@
         }));
   }
 
-  std::unique_ptr<web_app::TestInstallFinalizer> install_finalizer_;
+  std::unique_ptr<web_app::TestAppRegistrar> registrar_;
+  std::unique_ptr<TestBookmarkAppInstallFinalizer> install_finalizer_;
   TestBookmarkAppHelper* test_helper_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallationTaskTest);
@@ -183,7 +321,7 @@
 TEST_F(BookmarkAppInstallationTaskTest,
        WebAppOrShortcutFromContents_InstallationSucceeds) {
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(),
+      profile(), registrar(), install_finalizer(),
       web_app::InstallOptions(kWebAppUrl, web_app::LaunchContainer::kDefault,
                               web_app::InstallSource::kInternal));
 
@@ -226,7 +364,7 @@
 TEST_F(BookmarkAppInstallationTaskTest,
        WebAppOrShortcutFromContents_InstallationFails) {
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(),
+      profile(), registrar(), install_finalizer(),
       web_app::InstallOptions(kWebAppUrl, web_app::LaunchContainer::kWindow,
                               web_app::InstallSource::kInternal));
 
@@ -264,7 +402,7 @@
                                           web_app::InstallSource::kInternal);
   install_options.add_to_desktop = false;
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -293,7 +431,7 @@
                                           web_app::InstallSource::kInternal);
   install_options.add_to_quick_launch_bar = false;
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -325,7 +463,7 @@
   install_options.add_to_desktop = false;
   install_options.add_to_quick_launch_bar = false;
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -354,7 +492,7 @@
       web_app::InstallOptions(kWebAppUrl, web_app::LaunchContainer::kWindow,
                               web_app::InstallSource::kInternal);
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -381,7 +519,7 @@
       web_app::InstallOptions(kWebAppUrl, web_app::LaunchContainer::kTab,
                               web_app::InstallSource::kInternal);
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -408,7 +546,7 @@
       web_app::InstallOptions(kWebAppUrl, web_app::LaunchContainer::kDefault,
                               web_app::InstallSource::kInternal);
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -434,7 +572,7 @@
       web_app::InstallOptions(kWebAppUrl, web_app::LaunchContainer::kDefault,
                               web_app::InstallSource::kExternalPolicy);
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(install_options));
+      profile(), registrar(), install_finalizer(), std::move(install_options));
 
   bool callback_called = false;
   task->Install(web_contents(),
@@ -458,7 +596,11 @@
   web_app::InstallOptions options(kWebAppUrl, web_app::LaunchContainer::kWindow,
                                   web_app::InstallSource::kExternalPolicy);
   auto task = std::make_unique<BookmarkAppInstallationTask>(
-      profile(), install_finalizer(), std::move(options));
+      profile(), registrar(), install_finalizer(), std::move(options));
+  install_finalizer()->SetNextFinalizeInstallResult(
+      kWebAppUrl, web_app::InstallResultCode::kSuccess);
+  install_finalizer()->SetNextCreateOsShortcutsResult(
+      install_finalizer()->GetAppIdForUrl(kWebAppUrl), true);
 
   base::RunLoop run_loop;
   task->InstallPlaceholder(base::BindLambdaForTesting(
@@ -468,17 +610,60 @@
 
         EXPECT_TRUE(IsPlaceholderApp(profile(), kWebAppUrl));
 
-        EXPECT_EQ(1, install_finalizer()->num_create_os_shortcuts_calls());
+        EXPECT_EQ(1u, install_finalizer()->num_create_os_shortcuts_calls());
         EXPECT_EQ(1u, install_finalizer()->finalize_options_list().size());
         EXPECT_TRUE(
             install_finalizer()->finalize_options_list()[0].policy_installed);
-        std::unique_ptr<WebApplicationInfo> web_app_info =
-            install_finalizer()->web_app_info();
+        const WebApplicationInfo& web_app_info =
+            install_finalizer()->web_app_info_list().at(0);
 
-        EXPECT_EQ(base::UTF8ToUTF16(kWebAppUrl.spec()), web_app_info->title);
-        EXPECT_EQ(kWebAppUrl, web_app_info->app_url);
-        EXPECT_TRUE(web_app_info->open_as_window);
-        EXPECT_TRUE(web_app_info->icons.empty());
+        EXPECT_EQ(base::UTF8ToUTF16(kWebAppUrl.spec()), web_app_info.title);
+        EXPECT_EQ(kWebAppUrl, web_app_info.app_url);
+        EXPECT_TRUE(web_app_info.open_as_window);
+        EXPECT_TRUE(web_app_info.icons.empty());
+
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+TEST_F(BookmarkAppInstallationTaskTest, InstallPlaceholderTwice) {
+  web_app::InstallOptions options(kWebAppUrl, web_app::LaunchContainer::kWindow,
+                                  web_app::InstallSource::kExternalPolicy);
+  std::string placeholder_app_id;
+
+  // Install a placeholder app.
+  {
+    auto task = std::make_unique<BookmarkAppInstallationTask>(
+        profile(), registrar(), install_finalizer(), options);
+    install_finalizer()->SetNextFinalizeInstallResult(
+        kWebAppUrl, web_app::InstallResultCode::kSuccess);
+    install_finalizer()->SetNextCreateOsShortcutsResult(
+        install_finalizer()->GetAppIdForUrl(kWebAppUrl), true);
+
+    base::RunLoop run_loop;
+    task->InstallPlaceholder(base::BindLambdaForTesting(
+        [&](BookmarkAppInstallationTask::Result result) {
+          EXPECT_EQ(web_app::InstallResultCode::kSuccess, result.code);
+          placeholder_app_id = result.app_id.value();
+
+          EXPECT_EQ(1u, install_finalizer()->finalize_options_list().size());
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+
+  // Try to install it again.
+  auto task = std::make_unique<BookmarkAppInstallationTask>(
+      profile(), registrar(), install_finalizer(), options);
+  base::RunLoop run_loop;
+  task->InstallPlaceholder(base::BindLambdaForTesting(
+      [&](BookmarkAppInstallationTask::Result result) {
+        EXPECT_EQ(web_app::InstallResultCode::kSuccess, result.code);
+        EXPECT_EQ(placeholder_app_id, result.app_id.value());
+
+        // There shouldn't be a second call to the finalizer.
+        EXPECT_EQ(1u, install_finalizer()->finalize_options_list().size());
 
         run_loop.Quit();
       }));
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
index 8ddbaf3..49535fe5 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -26,10 +26,11 @@
 
 std::unique_ptr<BookmarkAppInstallationTask> InstallationTaskCreateWrapper(
     Profile* profile,
+    web_app::AppRegistrar* registrar,
     web_app::InstallFinalizer* install_finalizer,
     web_app::InstallOptions install_options) {
   return std::make_unique<BookmarkAppInstallationTask>(
-      profile, install_finalizer, std::move(install_options));
+      profile, registrar, install_finalizer, std::move(install_options));
 }
 
 }  // namespace
@@ -62,7 +63,7 @@
 void PendingBookmarkAppManager::Install(web_app::InstallOptions install_options,
                                         OnceInstallCallback callback) {
   pending_tasks_and_callbacks_.push_front(std::make_unique<TaskAndCallback>(
-      task_factory_.Run(profile_, install_finalizer_,
+      task_factory_.Run(profile_, registrar_, install_finalizer_,
                         std::move(install_options)),
       std::move(callback)));
 
@@ -77,7 +78,7 @@
     const RepeatingInstallCallback& callback) {
   for (auto& install_options : install_options_list) {
     pending_tasks_and_callbacks_.push_back(std::make_unique<TaskAndCallback>(
-        task_factory_.Run(profile_, install_finalizer_,
+        task_factory_.Run(profile_, registrar_, install_finalizer_,
                           std::move(install_options)),
         callback));
   }
@@ -268,15 +269,6 @@
     return;
   }
 
-  base::Optional<web_app::AppId> app_id =
-      externally_installed_app_prefs_.LookupPlaceholderAppId(
-          install_options.url);
-  if (app_id.has_value() && registrar_->IsInstalled(app_id.value())) {
-    // No need to install a placeholder app again.
-    CurrentInstallationFinished(app_id.value());
-    return;
-  }
-
   // TODO(ortuno): Move this into BookmarkAppInstallationTask::Install() once
   // loading the URL is part of Install().
   if (install_options.install_placeholder) {
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index 0a45204..26401be3 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -48,6 +48,7 @@
   using TaskFactory =
       base::RepeatingCallback<std::unique_ptr<BookmarkAppInstallationTask>(
           Profile*,
+          web_app::AppRegistrar*,
           web_app::InstallFinalizer*,
           web_app::InstallOptions)>;
 
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
index 92846e2..30f02f11 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
@@ -83,6 +83,7 @@
                                   web_app::InstallOptions install_options,
                                   bool succeeds)
       : BookmarkAppInstallationTask(profile,
+                                    registrar,
                                     install_finalizer,
                                     std::move(install_options)),
         profile_(profile),
@@ -245,8 +246,10 @@
 
   std::unique_ptr<BookmarkAppInstallationTask> CreateSuccessfulInstallationTask(
       Profile* profile,
+      web_app::AppRegistrar* registrar,
       web_app::InstallFinalizer* install_finalizer,
       web_app::InstallOptions install_options) {
+    DCHECK_EQ(registrar, registrar_.get());
     return CreateInstallationTask(profile, install_finalizer,
                                   std::move(install_options),
                                   true /* succeeds */);
@@ -254,8 +257,10 @@
 
   std::unique_ptr<BookmarkAppInstallationTask> CreateFailingInstallationTask(
       Profile* profile,
+      web_app::AppRegistrar* registrar,
       web_app::InstallFinalizer* install_finalizer,
       web_app::InstallOptions install_options) {
+    DCHECK_EQ(registrar, registrar_.get());
     return CreateInstallationTask(profile, install_finalizer,
                                   std::move(install_options),
                                   false /* succeeds */);
@@ -1283,7 +1288,7 @@
     EXPECT_EQ(1u, install_placeholder_run_count());
   }
 
-  // Reinstall placeholder
+  // Try to reinstall placeholder
   {
     install_options.reinstall_placeholder = true;
     url_loader()->SetNextLoadUrlResult(
@@ -1303,7 +1308,10 @@
     EXPECT_EQ(0u, uninstall_call_count());
 
     EXPECT_EQ(0u, install_run_count());
-    EXPECT_EQ(1u, install_placeholder_run_count());
+    // Even though the placeholder app is already install, we make a call to
+    // InstallFinalizer. InstallFinalizer ensures we don't unnecessarily
+    // install the placeholder app again.
+    EXPECT_EQ(2u, install_placeholder_run_count());
   }
 }
 
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index 171ab375..b6e2795 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_data_retriever.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/browser/web_applications/web_app_install_task.h"
 #include "chrome/common/web_application_info.h"
@@ -19,7 +20,10 @@
 
 WebAppInstallManager::WebAppInstallManager(Profile* profile,
                                            InstallFinalizer* install_finalizer)
-    : InstallManager(profile), install_finalizer_(install_finalizer) {}
+    : InstallManager(profile), install_finalizer_(install_finalizer) {
+  data_retriever_factory_ = base::BindRepeating(
+      []() { return std::make_unique<WebAppDataRetriever>(); });
+}
 
 WebAppInstallManager::~WebAppInstallManager() = default;
 
@@ -39,8 +43,8 @@
     OnceInstallCallback callback) {
   DCHECK(AreWebAppsUserInstallable(profile()));
 
-  auto task =
-      std::make_unique<WebAppInstallTask>(profile(), install_finalizer_);
+  auto task = std::make_unique<WebAppInstallTask>(
+      profile(), install_finalizer_, data_retriever_factory_.Run());
   task->InstallWebAppFromManifest(
       contents, install_source, std::move(dialog_callback),
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -57,8 +61,8 @@
     OnceInstallCallback callback) {
   DCHECK(AreWebAppsUserInstallable(profile()));
 
-  auto task =
-      std::make_unique<WebAppInstallTask>(profile(), install_finalizer_);
+  auto task = std::make_unique<WebAppInstallTask>(
+      profile(), install_finalizer_, data_retriever_factory_.Run());
   task->InstallWebAppFromManifestWithFallback(
       contents, force_shortcut_app, install_source, std::move(dialog_callback),
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -74,8 +78,8 @@
     OnceInstallCallback callback) {
   DCHECK(AreWebAppsUserInstallable(profile()));
 
-  auto task =
-      std::make_unique<WebAppInstallTask>(profile(), install_finalizer_);
+  auto task = std::make_unique<WebAppInstallTask>(
+      profile(), install_finalizer_, data_retriever_factory_.Run());
   task->InstallWebAppFromInfo(
       std::move(web_application_info), no_network_install, install_source,
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -107,6 +111,11 @@
   NOTIMPLEMENTED();
 }
 
+void WebAppInstallManager::SetDataRetrieverFactoryForTesting(
+    DataRetrieverFactory data_retriever_factory) {
+  data_retriever_factory_ = std::move(data_retriever_factory);
+}
+
 void WebAppInstallManager::OnTaskCompleted(WebAppInstallTask* task,
                                            OnceInstallCallback callback,
                                            const AppId& app_id,
diff --git a/chrome/browser/web_applications/web_app_install_manager.h b/chrome/browser/web_applications/web_app_install_manager.h
index 2a72bcfd..80b3585 100644
--- a/chrome/browser/web_applications/web_app_install_manager.h
+++ b/chrome/browser/web_applications/web_app_install_manager.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/callback_forward.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
@@ -19,6 +20,7 @@
 
 enum class InstallResultCode;
 class InstallFinalizer;
+class WebAppDataRetriever;
 class WebAppInstallTask;
 
 class WebAppInstallManager final : public InstallManager {
@@ -54,12 +56,19 @@
       std::unique_ptr<WebApplicationInfo> web_application_info,
       OnceInstallCallback callback) override;
 
+  using DataRetrieverFactory =
+      base::RepeatingCallback<std::unique_ptr<WebAppDataRetriever>()>;
+  void SetDataRetrieverFactoryForTesting(
+      DataRetrieverFactory data_retriever_factory);
+
  private:
   void OnTaskCompleted(WebAppInstallTask* task,
                        OnceInstallCallback callback,
                        const AppId& app_id,
                        InstallResultCode code);
 
+  DataRetrieverFactory data_retriever_factory_;
+
   using Tasks = base::flat_set<std::unique_ptr<WebAppInstallTask>,
                                base::UniquePtrComparator>;
   Tasks tasks_;
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index 418d6b01..aea3703 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -22,9 +22,11 @@
 
 namespace web_app {
 
-WebAppInstallTask::WebAppInstallTask(Profile* profile,
-                                     InstallFinalizer* install_finalizer)
-    : data_retriever_(std::make_unique<WebAppDataRetriever>()),
+WebAppInstallTask::WebAppInstallTask(
+    Profile* profile,
+    InstallFinalizer* install_finalizer,
+    std::unique_ptr<WebAppDataRetriever> data_retriever)
+    : data_retriever_(std::move(data_retriever)),
       install_finalizer_(install_finalizer),
       profile_(profile) {}
 
@@ -102,11 +104,6 @@
   CallInstallCallback(AppId(), InstallResultCode::kWebContentsDestroyed);
 }
 
-void WebAppInstallTask::SetDataRetrieverForTesting(
-    std::unique_ptr<WebAppDataRetriever> data_retriever) {
-  data_retriever_ = std::move(data_retriever);
-}
-
 void WebAppInstallTask::SetInstallFinalizerForTesting(
     InstallFinalizer* install_finalizer) {
   install_finalizer_ = install_finalizer;
diff --git a/chrome/browser/web_applications/web_app_install_task.h b/chrome/browser/web_applications/web_app_install_task.h
index a62cc72..faff59b2 100644
--- a/chrome/browser/web_applications/web_app_install_task.h
+++ b/chrome/browser/web_applications/web_app_install_task.h
@@ -33,7 +33,9 @@
 
 class WebAppInstallTask : content::WebContentsObserver {
  public:
-  WebAppInstallTask(Profile* profile, InstallFinalizer* install_finalizer);
+  WebAppInstallTask(Profile* profile,
+                    InstallFinalizer* install_finalizer,
+                    std::unique_ptr<WebAppDataRetriever> data_retriever);
   ~WebAppInstallTask() override;
 
   // Checks a WebApp installability, retrieves manifest and icons and
@@ -69,8 +71,6 @@
   // WebContentsObserver:
   void WebContentsDestroyed() override;
 
-  void SetDataRetrieverForTesting(
-      std::unique_ptr<WebAppDataRetriever> data_retriever);
   void SetInstallFinalizerForTesting(InstallFinalizer* install_finalizer);
 
  private:
diff --git a/chrome/browser/web_applications/web_app_install_task_unittest.cc b/chrome/browser/web_applications/web_app_install_task_unittest.cc
index 7611b22..1d600d7d 100644
--- a/chrome/browser/web_applications/web_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_task_unittest.cc
@@ -19,9 +19,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/installable/fake_installable_manager.h"
 #include "chrome/browser/installable/installable_data.h"
-#include "chrome/browser/installable/installable_manager.h"
 #include "chrome/browser/installable/installable_metrics.h"
-#include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
@@ -115,14 +113,11 @@
     install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(
         registrar_.get(), icon_manager_.get());
 
-    install_task_ = std::make_unique<WebAppInstallTask>(
-        profile(), install_finalizer_.get());
-  }
-
-  void CreateTestDataRetriever() {
     auto data_retriever = std::make_unique<TestDataRetriever>();
     data_retriever_ = data_retriever.get();
-    install_task_->SetDataRetrieverForTesting(std::move(data_retriever));
+
+    install_task_ = std::make_unique<WebAppInstallTask>(
+        profile(), install_finalizer_.get(), std::move(data_retriever));
   }
 
   void CreateRendererAppInfo(const GURL& url,
@@ -130,8 +125,6 @@
                              const std::string description,
                              const GURL& scope,
                              base::Optional<SkColor> theme_color) {
-    CreateTestDataRetriever();
-
     auto web_app_info = std::make_unique<WebApplicationInfo>();
 
     web_app_info->app_url = url;
@@ -150,13 +143,6 @@
     CreateRendererAppInfo(url, name, description, GURL(), base::nullopt);
   }
 
-  void CreateDefaultInstallableManager() {
-    InstallableManager::CreateForWebContents(web_contents());
-    // Required by InstallableManager.
-    // Causes eligibility check to return NOT_FROM_SECURE_ORIGIN for GetData.
-    SecurityStateTabHelper::CreateForWebContents(web_contents());
-  }
-
   static base::NullableString16 ToNullableUTF16(const std::string& str) {
     return base::NullableString16(base::UTF8ToUTF16(str), false);
   }
@@ -168,6 +154,23 @@
     install_task_->SetInstallFinalizerForTesting(test_install_finalizer_);
   }
 
+  void CreateDefaultDataToRetrieve(const GURL& url, const GURL& scope) {
+    data_retriever_->SetRendererWebApplicationInfo(
+        std::make_unique<WebApplicationInfo>());
+
+    auto manifest = std::make_unique<blink::Manifest>();
+    manifest->start_url = url;
+    manifest->scope = scope;
+
+    data_retriever_->SetManifest(std::move(manifest), /*is_installable=*/true);
+
+    data_retriever_->SetIcons(IconsMap{});
+  }
+
+  void CreateDefaultDataToRetrieve(const GURL& url) {
+    CreateDefaultDataToRetrieve(url, GURL{});
+  }
+
   TestInstallFinalizer& test_install_finalizer() {
     DCHECK(test_install_finalizer_);
     return *test_install_finalizer_;
@@ -208,9 +211,9 @@
   }
 
   void PrepareTestAppInstall() {
-    CreateRendererAppInfo(GURL("https://example.com/path"), "Name",
-                          "Description");
-    CreateDefaultInstallableManager();
+    const GURL url{"https://example.com/path"};
+    CreateDefaultDataToRetrieve(url);
+    CreateRendererAppInfo(url, "Name", "Description");
 
     SetInstallFinalizerForTesting();
 
@@ -236,7 +239,7 @@
 TEST_F(WebAppInstallTaskTest, InstallFromWebContents) {
   EXPECT_TRUE(AreWebAppsUserInstallable(profile()));
 
-  const GURL url = GURL("https://example.com/path");
+  const GURL url = GURL("https://example.com/scope/path");
   const std::string name = "Name";
   const std::string description = "Description";
   const GURL scope = GURL("https://example.com/scope");
@@ -244,8 +247,8 @@
 
   const AppId app_id = GenerateAppIdFromURL(url);
 
-  CreateRendererAppInfo(url, name, description, scope, theme_color);
-  CreateDefaultInstallableManager();
+  CreateDefaultDataToRetrieve(url, scope);
+  CreateRendererAppInfo(url, name, description, /*scope*/ GURL{}, theme_color);
 
   base::RunLoop run_loop;
   bool callback_called = false;
@@ -283,8 +286,8 @@
 
   const AppId app_id = GenerateAppIdFromURL(url);
 
+  CreateDefaultDataToRetrieve(url);
   CreateRendererAppInfo(url, name, description);
-  CreateDefaultInstallableManager();
 
   const AppId installed_web_app = InstallWebAppFromManifestWithFallback();
   EXPECT_EQ(app_id, installed_web_app);
@@ -312,11 +315,7 @@
 }
 
 TEST_F(WebAppInstallTaskTest, GetWebApplicationInfoFailed) {
-  auto data_retriver = std::make_unique<TestDataRetriever>();
-  // data_retriver with empty info means an error.
-  install_task_->SetDataRetrieverForTesting(std::move(data_retriver));
-
-  CreateDefaultInstallableManager();
+  // data_retriever_ with empty info means an error.
 
   base::RunLoop run_loop;
   bool callback_called = false;
@@ -338,9 +337,9 @@
 }
 
 TEST_F(WebAppInstallTaskTest, WebContentsDestroyed) {
-  CreateRendererAppInfo(GURL("https://example.com/path"), "Name",
-                        "Description");
-  CreateDefaultInstallableManager();
+  const GURL url = GURL("https://example.com/path");
+  CreateDefaultDataToRetrieve(url);
+  CreateRendererAppInfo(url, "Name", "Description");
 
   base::RunLoop run_loop;
   bool callback_called = false;
@@ -422,9 +421,9 @@
 }
 
 TEST_F(WebAppInstallTaskTest, GetIcons) {
-  CreateRendererAppInfo(GURL("https://example.com/path"), "Name",
-                        "Description");
-  CreateDefaultInstallableManager();
+  const GURL url = GURL("https://example.com/path");
+  CreateDefaultDataToRetrieve(url);
+  CreateRendererAppInfo(url, "Name", "Description");
 
   SetInstallFinalizerForTesting();
 
@@ -456,9 +455,9 @@
 }
 
 TEST_F(WebAppInstallTaskTest, GetIcons_NoIconsProvided) {
-  CreateRendererAppInfo(GURL("https://example.com/path"), "Name",
-                        "Description");
-  CreateDefaultInstallableManager();
+  const GURL url = GURL("https://example.com/path");
+  CreateDefaultDataToRetrieve(url);
+  CreateRendererAppInfo(url, "Name", "Description");
 
   SetInstallFinalizerForTesting();
 
@@ -481,9 +480,9 @@
 }
 
 TEST_F(WebAppInstallTaskTest, WriteDataToDisk) {
-  CreateRendererAppInfo(GURL("https://example.com/path"), "Name",
-                        "Description");
-  CreateDefaultInstallableManager();
+  const GURL url = GURL("https://example.com/path");
+  CreateDefaultDataToRetrieve(url);
+  CreateRendererAppInfo(url, "Name", "Description");
 
   // TestingProfile creates temp directory if TestingProfile::path_ is empty
   // (i.e. if TestingProfile::Builder::SetPath was not called by a test fixture)
@@ -551,8 +550,8 @@
 
 TEST_F(WebAppInstallTaskTest, WriteDataToDiskFailed) {
   const GURL app_url = GURL("https://example.com/path");
+  CreateDefaultDataToRetrieve(app_url);
   CreateRendererAppInfo(app_url, "Name", "Description");
-  CreateDefaultInstallableManager();
 
   IconsMap icons_map = GenerateIconsMapWithOneIcon(
       GURL("https://example.com/app.ico"), icon_size::k512, SK_ColorBLUE);
@@ -596,8 +595,8 @@
   const GURL url = GURL("https://example.com/path");
   const AppId app_id = GenerateAppIdFromURL(url);
 
+  CreateDefaultDataToRetrieve(url);
   CreateRendererAppInfo(url, "Name", "Description");
-  CreateDefaultInstallableManager();
 
   base::RunLoop run_loop;
   bool callback_called = false;
@@ -649,8 +648,6 @@
 }
 
 TEST_F(WebAppInstallTaskTest, InstallWebAppFromManifest_Success) {
-  CreateTestDataRetriever();
-
   const GURL url = GURL("https://example.com/path");
   const AppId app_id = GenerateAppIdFromURL(url);
 
@@ -675,7 +672,6 @@
 }
 
 TEST_F(WebAppInstallTaskTest, InstallWebAppFromInfo_Success) {
-  CreateTestDataRetriever();
   SetInstallFinalizerForTesting();
 
   const GURL url = GURL("https://example.com/path");
@@ -710,7 +706,6 @@
 }
 
 TEST_F(WebAppInstallTaskTest, InstallWebAppFromInfo_NoNetworkInstall) {
-  CreateTestDataRetriever();
   SetInstallFinalizerForTesting();
 
   auto web_app_info = std::make_unique<WebApplicationInfo>();
@@ -740,7 +735,6 @@
 }
 
 TEST_F(WebAppInstallTaskTest, InstallWebAppFromInfo_GenerateIcons) {
-  CreateTestDataRetriever();
   SetInstallFinalizerForTesting();
 
   auto web_app_info = std::make_unique<WebApplicationInfo>();
diff --git a/chrome/test/data/android/photo_picker/blue100x100.jpg b/chrome/test/data/android/photo_picker/blue100x100.jpg
new file mode 100644
index 0000000..ecab3cc
--- /dev/null
+++ b/chrome/test/data/android/photo_picker/blue100x100.jpg
Binary files differ
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index e131790..48295b3 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -249,7 +249,6 @@
     "webdata/autofill_wallet_sync_bridge.h",
     "webdata/autofill_wallet_syncable_service.cc",
     "webdata/autofill_wallet_syncable_service.h",
-    "webdata/autofill_webdata.h",
     "webdata/autofill_webdata_backend.h",
     "webdata/autofill_webdata_backend_impl.cc",
     "webdata/autofill_webdata_backend_impl.h",
diff --git a/components/autofill/core/browser/suggestion.cc b/components/autofill/core/browser/suggestion.cc
index 9182bd1..afd78dc 100644
--- a/components/autofill/core/browser/suggestion.cc
+++ b/components/autofill/core/browser/suggestion.cc
@@ -4,40 +4,29 @@
 
 #include "components/autofill/core/browser/suggestion.h"
 
+#include <utility>
+
 #include "base/strings/utf_string_conversions.h"
 
 namespace autofill {
 
-Suggestion::Suggestion()
-    : frontend_id(0), match(PREFIX_MATCH), is_value_secondary(false) {}
+Suggestion::Suggestion() = default;
+Suggestion::Suggestion(const Suggestion& other) = default;
+Suggestion::Suggestion(Suggestion&& other) = default;
 
-Suggestion::Suggestion(const Suggestion& other)
-    : backend_id(other.backend_id),
-      frontend_id(other.frontend_id),
-      value(other.value),
-      label(other.label),
-      additional_label(other.additional_label),
-      custom_icon(other.custom_icon),
-      icon(other.icon),
-      match(other.match),
-      is_value_secondary(other.is_value_secondary) {}
+Suggestion::Suggestion(base::string16 value) : value(std::move(value)) {}
 
-Suggestion::Suggestion(const base::string16& v)
-    : frontend_id(0),
-      value(v),
-      match(PREFIX_MATCH),
-      is_value_secondary(false) {}
+Suggestion::Suggestion(base::StringPiece value,
+                       base::StringPiece label,
+                       std::string icon,
+                       int frontend_id)
+    : frontend_id(frontend_id),
+      value(base::UTF8ToUTF16(value)),
+      label(base::UTF8ToUTF16(label)),
+      icon(std::move(icon)) {}
 
-Suggestion::Suggestion(const std::string& v,
-                       const std::string& l,
-                       const std::string& i,
-                       int fid)
-    : frontend_id(fid),
-      value(base::UTF8ToUTF16(v)),
-      label(base::UTF8ToUTF16(l)),
-      icon(i),
-      match(PREFIX_MATCH),
-      is_value_secondary(false) {}
+Suggestion& Suggestion::operator=(const Suggestion& other) = default;
+Suggestion& Suggestion::operator=(Suggestion&& other) = default;
 
 Suggestion::~Suggestion() = default;
 
diff --git a/components/autofill/core/browser/suggestion.h b/components/autofill/core/browser/suggestion.h
index 0df9efe..fa059555 100644
--- a/components/autofill/core/browser/suggestion.h
+++ b/components/autofill/core/browser/suggestion.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
 #include "ui/gfx/image/image.h"
 
 namespace autofill {
@@ -19,19 +20,20 @@
   };
 
   Suggestion();
-
-  // Copy constructor for STL containers.
   Suggestion(const Suggestion& other);
+  Suggestion(Suggestion&& other);
 
-  explicit Suggestion(const base::string16& value);
-
+  explicit Suggestion(base::string16 value);
   // Constructor for unit tests. It will convert the strings from UTF-8 to
   // UTF-16.
-  Suggestion(const std::string& value,
-             const std::string& label,
-             const std::string& icon,
+  Suggestion(base::StringPiece value,
+             base::StringPiece label,
+             std::string icon,
              int frontend_id);
 
+  Suggestion& operator=(const Suggestion& other);
+  Suggestion& operator=(Suggestion&& other);
+
   ~Suggestion();
 
   // GUID generated by the backend layer. This identifies the exact autofill
@@ -40,9 +42,8 @@
 
   // ID for the frontend to use in identifying the particular result. Positive
   // values are sent over IPC to identify the item selected. Negative values
-  // (see popup_item_ids.h) have special built-in meanings. Default initialized
-  // to 0.
-  int frontend_id;
+  // (see popup_item_ids.h) have special built-in meanings.
+  int frontend_id = 0;
 
   base::string16 value;
   base::string16 label;
@@ -54,8 +55,9 @@
   gfx::Image custom_icon;
   // If |custom_icon| is empty, the name of the fallback built-in icon.
   std::string icon;
-  MatchMode match;
-  bool is_value_secondary;  // |value| should be displayed as secondary text.
+  MatchMode match = PREFIX_MATCH;
+  // |value| should be displayed as secondary text.
+  bool is_value_secondary = false;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_webdata.h b/components/autofill/core/browser/webdata/autofill_webdata.h
deleted file mode 100644
index 725fc5ce..0000000
--- a/components/autofill/core/browser/webdata/autofill_webdata.h
+++ /dev/null
@@ -1,152 +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_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "components/webdata/common/web_data_service_base.h"
-
-namespace base {
-
-class Time;
-
-}  // namespace base
-
-class WebDataServiceConsumer;
-
-namespace autofill {
-
-class AutofillEntry;
-class AutofillProfile;
-class CreditCard;
-struct FormFieldData;
-
-// Pure virtual interface for retrieving Autofill data.  API users
-// should use AutofillWebDataService.
-class AutofillWebData {
- public:
-  virtual ~AutofillWebData() {}
-
-  // Schedules a task to add form fields to the web database.
-  virtual void AddFormFields(
-      const std::vector<FormFieldData>& fields) = 0;
-
-  // Initiates the request for a vector of values which have been entered in
-  // form input fields named |name|.  The method OnWebDataServiceRequestDone of
-  // |consumer| gets called back when the request is finished, with the vector
-  // included in the argument |result|.
-  virtual WebDataServiceBase::Handle GetFormValuesForElementName(
-      const base::string16& name,
-      const base::string16& prefix,
-      int limit,
-      WebDataServiceConsumer* consumer) = 0;
-
-  // Removes form elements recorded for Autocomplete from the database.
-  virtual void RemoveFormElementsAddedBetween(
-      const base::Time& delete_begin, const base::Time& delete_end) = 0;
-
-  virtual void RemoveFormValueForElementName(const base::string16& name,
-                                             const base::string16& value) = 0;
-
-  // Schedules a task to add an Autofill profile to the web database.
-  virtual void AddAutofillProfile(const AutofillProfile& profile) = 0;
-
-  // Schedules a task to update an Autofill profile in the web database.
-  virtual void UpdateAutofillProfile(const AutofillProfile& profile) = 0;
-
-  // Schedules a task to remove an Autofill profile from the web database.
-  // |guid| is the identifier of the profile to remove.
-  virtual void RemoveAutofillProfile(const std::string& guid) = 0;
-
-  // Initiates the request for local/server Autofill profiles.  The method
-  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
-  // finished, with the profiles included in the argument |result|.  The
-  // consumer owns the profiles.
-  virtual WebDataServiceBase::Handle GetAutofillProfiles(
-      WebDataServiceConsumer* consumer) = 0;
-  virtual WebDataServiceBase::Handle GetServerProfiles(
-      WebDataServiceConsumer* consumer) = 0;
-
-  // Schedules a task to convert server profiles to local profiles, comparing
-  // profiles using |app_locale| and filling in |primary_account_email| into
-  // newly converted profiles. The task only converts profiles that have not
-  // been converted before.
-  virtual void ConvertWalletAddressesAndUpdateWalletCards(
-      const std::string& app_locale,
-      const std::string& primary_account_email) = 0;
-
-  // Schedules a task to count the number of unique autofill values contained
-  // in the time interval [|begin|, |end|). |begin| and |end| can be null
-  // to indicate no time limitation.
-  virtual WebDataServiceBase::Handle GetCountOfValuesContainedBetween(
-      const base::Time& begin,
-      const base::Time& end,
-      WebDataServiceConsumer* consumer) = 0;
-
-  // Schedules a task to update autofill entries in the web database.
-  virtual void UpdateAutofillEntries(
-      const std::vector<AutofillEntry>& autofill_entries) = 0;
-
-  // Schedules a task to add credit card to the web database.
-  virtual void AddCreditCard(const CreditCard& credit_card) = 0;
-
-  // Schedules a task to update credit card in the web database.
-  virtual void UpdateCreditCard(const CreditCard& credit_card) = 0;
-
-  // Schedules a task to remove a credit card from the web database.
-  // |guid| is identifier of the credit card to remove.
-  virtual void RemoveCreditCard(const std::string& guid) = 0;
-
-  // Schedules a task to add a full server credit card to the web database.
-  virtual void AddFullServerCreditCard(const CreditCard& credit_card) = 0;
-
-  // Initiates the request for local/server credit cards.  The method
-  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
-  // finished, with the credit cards included in the argument |result|.  The
-  // consumer owns the credit cards.
-  virtual WebDataServiceBase::Handle GetCreditCards(
-      WebDataServiceConsumer* consumer) = 0;
-  virtual WebDataServiceBase::Handle GetServerCreditCards(
-      WebDataServiceConsumer* consumer) = 0;
-
-  // Toggles the record for a server credit card between masked (only last 4
-  // digits) and full (all digits).
-  virtual void UnmaskServerCreditCard(const CreditCard& credit_card,
-                                      const base::string16& full_number) = 0;
-  virtual void MaskServerCreditCard(const std::string& id) = 0;
-
-  // Initiates the request for Payments customer data.  The method
-  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
-  // finished, with the customer data included in the argument |result|. The
-  // consumer owns the data.
-  virtual WebDataServiceBase::Handle GetPaymentsCustomerData(
-      WebDataServiceConsumer* consumer) = 0;
-
-  // Updates the metadata for a server card (masked or not).
-  virtual void UpdateServerCardMetadata(const CreditCard& credit_card) = 0;
-
-  // Updates the metadata for a server address.
-  virtual void UpdateServerAddressMetadata(const AutofillProfile& profile) = 0;
-
-  // Removes Autofill records from the database.
-  virtual void RemoveAutofillDataModifiedBetween(
-      const base::Time& delete_begin, const base::Time& delete_end) = 0;
-
-  // Removes origin URLs associated with Autofill profiles and credit cards from
-  // the database.
-  virtual void RemoveOriginURLsModifiedBetween(
-      const base::Time& delete_begin, const base::Time& delete_end) = 0;
-
-  // Removes the orphan rows in the autofill_profile_names,
-  // autofill_profile_emails and autofill_profile_phones tables.
-  virtual void RemoveOrphanAutofillTableRows() = 0;
-};
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
index 004c8cc..ebd3217 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
@@ -12,7 +12,6 @@
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/observer_list.h"
 #include "base/supports_user_data.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/webdata/common/web_data_results.h"
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_service.h b/components/autofill/core/browser/webdata/autofill_webdata_service.h
index ddd2990..f82e891 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_service.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata_service.h
@@ -13,7 +13,6 @@
 #include "base/observer_list.h"
 #include "base/supports_user_data.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
-#include "components/autofill/core/browser/webdata/autofill_webdata.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/sync/base/model_type.h"
 #include "components/webdata/common/web_data_results.h"
@@ -38,8 +37,7 @@
 class CreditCard;
 
 // API for Autofill web data.
-class AutofillWebDataService : public AutofillWebData,
-                               public WebDataServiceBase {
+class AutofillWebDataService : public WebDataServiceBase {
  public:
   AutofillWebDataService(
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -53,75 +51,122 @@
   // WebDataServiceBase implementation.
   void ShutdownOnUISequence() override;
 
-  // AutofillWebData implementation.
-  void AddFormFields(const std::vector<FormFieldData>& fields) override;
-  WebDataServiceBase::Handle GetFormValuesForElementName(
+  // Schedules a task to add form fields to the web database.
+  virtual void AddFormFields(const std::vector<FormFieldData>& fields);
+
+  // Initiates the request for a vector of values which have been entered in
+  // form input fields named |name|. The method OnWebDataServiceRequestDone of
+  // |consumer| gets called back when the request is finished, with the vector
+  // included in the argument |result|.
+  virtual WebDataServiceBase::Handle GetFormValuesForElementName(
       const base::string16& name,
       const base::string16& prefix,
       int limit,
-      WebDataServiceConsumer* consumer) override;
+      WebDataServiceConsumer* consumer);
 
+  // Removes form elements recorded for Autocomplete from the database.
   void RemoveFormElementsAddedBetween(const base::Time& delete_begin,
-                                      const base::Time& delete_end) override;
+                                      const base::Time& delete_end);
   void RemoveFormValueForElementName(const base::string16& name,
-                                     const base::string16& value) override;
+                                     const base::string16& value);
 
-  // Profiles.
-  void AddAutofillProfile(const AutofillProfile& profile) override;
-  void UpdateAutofillProfile(const AutofillProfile& profile) override;
-  void RemoveAutofillProfile(const std::string& guid) override;
+  // Schedules a task to add an Autofill profile to the web database.
+  void AddAutofillProfile(const AutofillProfile& profile);
+
+  // Schedules a task to update an Autofill profile in the web database.
+  void UpdateAutofillProfile(const AutofillProfile& profile);
+
+  // Schedules a task to remove an Autofill profile from the web database.
+  // |guid| is the identifier of the profile to remove.
+  void RemoveAutofillProfile(const std::string& guid);
+
+  // Initiates the request for local/server Autofill profiles.  The method
+  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+  // finished, with the profiles included in the argument |result|.  The
   WebDataServiceBase::Handle GetAutofillProfiles(
-      WebDataServiceConsumer* consumer) override;
-
-  // Server profiles.
+      WebDataServiceConsumer* consumer);
   WebDataServiceBase::Handle GetServerProfiles(
-      WebDataServiceConsumer* consumer) override;
+      WebDataServiceConsumer* consumer);
+
+  // Schedules a task to convert server profiles to local profiles, comparing
+  // profiles using |app_locale| and filling in |primary_account_email| into
+  // newly converted profiles. The task only converts profiles that have not
+  // been converted before.
   void ConvertWalletAddressesAndUpdateWalletCards(
       const std::string& app_locale,
-      const std::string& primary_account_email) override;
+      const std::string& primary_account_email);
 
+  // Schedules a task to count the number of unique autofill values contained
+  // in the time interval [|begin|, |end|). |begin| and |end| can be null
+  // to indicate no time limitation.
   WebDataServiceBase::Handle GetCountOfValuesContainedBetween(
       const base::Time& begin,
       const base::Time& end,
-      WebDataServiceConsumer* consumer) override;
+      WebDataServiceConsumer* consumer);
+
+  // Schedules a task to update autofill entries in the web database.
   void UpdateAutofillEntries(
-      const std::vector<AutofillEntry>& autofill_entries) override;
+      const std::vector<AutofillEntry>& autofill_entries);
 
   void SetAutofillProfileChangedCallback(
       base::RepeatingCallback<void(const AutofillProfileDeepChange&)>
           change_cb);
 
-  // Credit cards.
-  void AddCreditCard(const CreditCard& credit_card) override;
-  void UpdateCreditCard(const CreditCard& credit_card) override;
-  void RemoveCreditCard(const std::string& guid) override;
-  void AddFullServerCreditCard(const CreditCard& credit_card) override;
-  WebDataServiceBase::Handle GetCreditCards(
-      WebDataServiceConsumer* consumer) override;
+  // Schedules a task to add credit card to the web database.
+  void AddCreditCard(const CreditCard& credit_card);
 
-  // Server cards.
+  // Schedules a task to update credit card in the web database.
+  void UpdateCreditCard(const CreditCard& credit_card);
+
+  // Schedules a task to remove a credit card from the web database.
+  // |guid| is identifier of the credit card to remove.
+  void RemoveCreditCard(const std::string& guid);
+
+  // Schedules a task to add a full server credit card to the web database.
+  void AddFullServerCreditCard(const CreditCard& credit_card);
+
+  // Initiates the request for local/server credit cards.  The method
+  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+  // finished, with the credit cards included in the argument |result|.  The
+  // consumer owns the credit cards.
+  WebDataServiceBase::Handle GetCreditCards(WebDataServiceConsumer* consumer);
   WebDataServiceBase::Handle GetServerCreditCards(
-      WebDataServiceConsumer* consumer) override;
-  void UnmaskServerCreditCard(const CreditCard& card,
-                              const base::string16& full_number) override;
-  void MaskServerCreditCard(const std::string& id) override;
+      WebDataServiceConsumer* consumer);
 
-  // PaymentsCustomerData.
+  // Toggles the record for a server credit card between masked (only last 4
+  // digits) and full (all digits).
+  void UnmaskServerCreditCard(const CreditCard& card,
+                              const base::string16& full_number);
+  void MaskServerCreditCard(const std::string& id);
+
+  // Initiates the request for Payments customer data.  The method
+  // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+  // finished, with the customer data included in the argument |result|. The
+  // consumer owns the data.
   WebDataServiceBase::Handle GetPaymentsCustomerData(
-      WebDataServiceConsumer* consumer) override;
+      WebDataServiceConsumer* consumer);
 
   void ClearAllServerData();
   void ClearAllLocalData();
 
-  void UpdateServerCardMetadata(const CreditCard& credit_card) override;
-  void UpdateServerAddressMetadata(const AutofillProfile& profile) override;
+  // Updates the metadata for a server card (masked or not).
+  void UpdateServerCardMetadata(const CreditCard& credit_card);
 
+  // Updates the metadata for a server address.
+  void UpdateServerAddressMetadata(const AutofillProfile& profile);
+
+  // Removes Autofill records from the database.
   void RemoveAutofillDataModifiedBetween(const base::Time& delete_begin,
-                                         const base::Time& delete_end) override;
-  void RemoveOriginURLsModifiedBetween(const base::Time& delete_begin,
-                                       const base::Time& delete_end) override;
+                                         const base::Time& delete_end);
 
-  void RemoveOrphanAutofillTableRows() override;
+  // Removes origin URLs associated with Autofill profiles and credit cards from
+  // the database.
+  void RemoveOriginURLsModifiedBetween(const base::Time& delete_begin,
+                                       const base::Time& delete_end);
+
+  // Removes the orphan rows in the autofill_profile_names,
+  // autofill_profile_emails and autofill_profile_phones tables.
+  void RemoveOrphanAutofillTableRows();
 
   void AddObserver(AutofillWebDataServiceObserverOnDBSequence* observer);
   void RemoveObserver(AutofillWebDataServiceObserverOnDBSequence* observer);
@@ -153,9 +198,9 @@
  protected:
   ~AutofillWebDataService() override;
 
-  virtual void NotifyAutofillMultipleChangedOnUISequence();
-  virtual void NotifyAutofillAddressConversionCompletedOnUISequence();
-  virtual void NotifySyncStartedOnUISequence(syncer::ModelType model_type);
+  void NotifyAutofillMultipleChangedOnUISequence();
+  void NotifyAutofillAddressConversionCompletedOnUISequence();
+  void NotifySyncStartedOnUISequence(syncer::ModelType model_type);
 
   base::WeakPtr<AutofillWebDataService> AsWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
diff --git a/components/chromeos_camera/BUILD.gn b/components/chromeos_camera/BUILD.gn
new file mode 100644
index 0000000..7c1f0947
--- /dev/null
+++ b/components/chromeos_camera/BUILD.gn
@@ -0,0 +1,216 @@
+# 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("//build/buildflag_header.gni")
+import("//build/config/ui.gni")
+import("//media/gpu/args.gni")
+import("//testing/test.gni")
+
+source_set("jpeg_encode_accelerator") {
+  sources = [
+    "jpeg_encode_accelerator.cc",
+    "jpeg_encode_accelerator.h",
+  ]
+
+  deps = [
+    "//media",
+  ]
+}
+
+source_set("mjpeg_decode_accelerator") {
+  sources = [
+    "mjpeg_decode_accelerator.cc",
+    "mjpeg_decode_accelerator.h",
+  ]
+
+  deps = [
+    "//media",
+  ]
+}
+
+source_set("mojo_mjpeg_decode_accelerator") {
+  visibility = [
+    "//content/browser",
+    "//media/capture:capture_lib",
+  ]
+
+  sources = [
+    "mojo_mjpeg_decode_accelerator.cc",
+    "mojo_mjpeg_decode_accelerator.h",
+  ]
+
+  deps = [
+    ":mjpeg_decode_accelerator",
+    "//base",
+    "//components/chromeos_camera/common",
+  ]
+}
+
+# TODO(crbug.com/960243): Don't compile these codes for build without vaapi/v4l2
+# supported.
+source_set("jpeg_encode_accelerator_service") {
+  sources = [
+    "gpu_jpeg_encode_accelerator_factory.cc",
+    "gpu_jpeg_encode_accelerator_factory.h",
+    "mojo_jpeg_encode_accelerator_service.cc",
+    "mojo_jpeg_encode_accelerator_service.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//media",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//url",
+  ]
+
+  deps = [
+    ":jpeg_encode_accelerator",
+    "//components/chromeos_camera/common",
+    "//media",
+    "//media/gpu:buildflags",
+    "//ui/gl",
+  ]
+
+  if (use_vaapi) {
+    deps += [ "//media/gpu/vaapi" ]
+  }
+
+  if (use_v4l2_codec) {
+    deps += [ "//media/gpu/v4l2" ]
+  }
+}
+
+source_set("mjpeg_decode_accelerator_service") {
+  sources = [
+    "fake_mjpeg_decode_accelerator.cc",
+    "fake_mjpeg_decode_accelerator.h",
+    "gpu_mjpeg_decode_accelerator_factory.cc",
+    "gpu_mjpeg_decode_accelerator_factory.h",
+    "mojo_mjpeg_decode_accelerator_service.cc",
+    "mojo_mjpeg_decode_accelerator_service.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//media",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//url",
+  ]
+
+  deps = [
+    ":mjpeg_decode_accelerator",
+    "//components/chromeos_camera/common",
+    "//media",
+    "//media/gpu:buildflags",
+    "//ui/gl",
+  ]
+
+  if (use_vaapi) {
+    deps += [ "//media/gpu/vaapi" ]
+  }
+
+  if (use_v4l2_codec) {
+    deps += [ "//media/gpu/v4l2" ]
+  }
+}
+
+source_set("mjpeg_decode_accelerator_service_unittest") {
+  testonly = true
+
+  sources = [
+    "mojo_mjpeg_decode_accelerator_service_unittest.cc",
+  ]
+
+  deps = [
+    ":mjpeg_decode_accelerator_service",
+    "//base",
+    "//base/test:test_support",
+    "//components/chromeos_camera/common",
+    "//media/gpu:buildflags",
+    "//testing/gtest",
+  ]
+}
+
+if (use_v4l2_codec || use_vaapi) {
+  test("jpeg_encode_accelerator_unittest") {
+    deps = [
+      ":jpeg_encode_accelerator",
+      ":jpeg_encode_accelerator_service",
+      "//base",
+      "//base/test:test_support",
+      "//media:test_support",
+      "//media/gpu:buildflags",
+      "//media/gpu/test:helpers",
+      "//mojo/core/embedder",
+      "//testing/gtest",
+      "//third_party:jpeg",
+      "//third_party/libyuv",
+      "//ui/base",
+      "//ui/gfx",
+      "//ui/gfx:test_support",
+      "//ui/gfx/geometry",
+      "//ui/gl",
+      "//ui/gl:test_support",
+    ]
+    configs += [ "//third_party/libyuv:libyuv_config" ]
+    sources = [
+      "jpeg_encode_accelerator_unittest.cc",
+    ]
+    if (use_vaapi) {
+      deps += [ "//media/gpu/vaapi" ]
+    }
+    if (use_x11) {
+      deps += [ "//ui/gfx/x" ]
+    }
+    if (use_ozone) {
+      deps += [ "//ui/ozone" ]
+    }
+  }
+}
+
+test("jpeg_decode_accelerator_unittest") {
+  deps = [
+    ":mjpeg_decode_accelerator",
+    ":mjpeg_decode_accelerator_service",
+    "//base",
+    "//media:test_support",
+    "//media/gpu:buildflags",
+    "//media/gpu/test:helpers",
+    "//mojo/core/embedder",
+    "//testing/gtest",
+    "//third_party/libyuv",
+    "//ui/base",
+    "//ui/gfx",
+    "//ui/gfx:test_support",
+    "//ui/gfx/geometry",
+    "//ui/gl",
+    "//ui/gl:test_support",
+  ]
+  configs += [ "//third_party/libyuv:libyuv_config" ]
+  sources = [
+    "mjpeg_decode_accelerator_unittest.cc",
+  ]
+  data = [
+    "//media/test/data/peach_pi-1280x720.jpg",
+    "//media/test/data/peach_pi-40x23.jpg",
+    "//media/test/data/peach_pi-41x22.jpg",
+    "//media/test/data/peach_pi-41x23.jpg",
+  ]
+  if (use_vaapi) {
+    deps += [
+      "//media/gpu/vaapi",
+      "//media/gpu/vaapi:jpeg_decoder_unit_test",
+    ]
+
+    data += [ "//media/test/data/pixel-1280x720.jpg" ]
+  }
+  if (use_x11) {
+    deps += [ "//ui/gfx/x" ]
+  }
+  if (use_ozone) {
+    deps += [ "//ui/ozone" ]
+  }
+}
diff --git a/components/chromeos_camera/DEPS b/components/chromeos_camera/DEPS
index 0a6d396..d649e90a 100644
--- a/components/chromeos_camera/DEPS
+++ b/components/chromeos_camera/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
   "+media",
   "+mojo",
+  "+third_party/libyuv",
+  "+ui/gfx",
 ]
\ No newline at end of file
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
index 6d6b6d7..f5bb1607 100644
--- a/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
+++ b/components/chromeos_camera/common/jpeg_encode_accelerator.mojom
@@ -6,7 +6,7 @@
 
 import "media/mojo/interfaces/media_types.mojom";
 
-// Encode errors (see media/video/jpeg_encode_accelerator.h).
+// Encode errors (see components/chromeos_camera/jpeg_encode_accelerator.h).
 enum EncodeStatus {
   ENCODE_OK,
   HW_JPEG_ENCODE_NOT_SUPPORTED,
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator.typemap b/components/chromeos_camera/common/jpeg_encode_accelerator.typemap
index 543f549..db45a03 100644
--- a/components/chromeos_camera/common/jpeg_encode_accelerator.typemap
+++ b/components/chromeos_camera/common/jpeg_encode_accelerator.typemap
@@ -4,7 +4,7 @@
 
 mojom = "//components/chromeos_camera/common/jpeg_encode_accelerator.mojom"
 
-public_headers = [ "//media/video/jpeg_encode_accelerator.h" ]
+public_headers = [ "//components/chromeos_camera/jpeg_encode_accelerator.h" ]
 
 traits_headers = [
   "//components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.h",
@@ -20,6 +20,4 @@
   "//media/base/ipc",
 ]
 
-type_mappings = [
-  "chromeos_camera.mojom.EncodeStatus=media::JpegEncodeAccelerator::Status",
-]
+type_mappings = [ "chromeos_camera.mojom.EncodeStatus=chromeos_camera::JpegEncodeAccelerator::Status" ]
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.cc b/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.cc
index 1a51bf7..1278acb 100644
--- a/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.cc
+++ b/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.cc
@@ -11,22 +11,22 @@
 // static
 chromeos_camera::mojom::EncodeStatus
 EnumTraits<chromeos_camera::mojom::EncodeStatus,
-           media::JpegEncodeAccelerator::Status>::
-    ToMojom(media::JpegEncodeAccelerator::Status status) {
+           chromeos_camera::JpegEncodeAccelerator::Status>::
+    ToMojom(chromeos_camera::JpegEncodeAccelerator::Status status) {
   switch (status) {
-    case media::JpegEncodeAccelerator::ENCODE_OK:
+    case chromeos_camera::JpegEncodeAccelerator::ENCODE_OK:
       return chromeos_camera::mojom::EncodeStatus::ENCODE_OK;
-    case media::JpegEncodeAccelerator::HW_JPEG_ENCODE_NOT_SUPPORTED:
+    case chromeos_camera::JpegEncodeAccelerator::HW_JPEG_ENCODE_NOT_SUPPORTED:
       return chromeos_camera::mojom::EncodeStatus::HW_JPEG_ENCODE_NOT_SUPPORTED;
-    case media::JpegEncodeAccelerator::THREAD_CREATION_FAILED:
+    case chromeos_camera::JpegEncodeAccelerator::THREAD_CREATION_FAILED:
       return chromeos_camera::mojom::EncodeStatus::THREAD_CREATION_FAILED;
-    case media::JpegEncodeAccelerator::INVALID_ARGUMENT:
+    case chromeos_camera::JpegEncodeAccelerator::INVALID_ARGUMENT:
       return chromeos_camera::mojom::EncodeStatus::INVALID_ARGUMENT;
-    case media::JpegEncodeAccelerator::INACCESSIBLE_OUTPUT_BUFFER:
+    case chromeos_camera::JpegEncodeAccelerator::INACCESSIBLE_OUTPUT_BUFFER:
       return chromeos_camera::mojom::EncodeStatus::INACCESSIBLE_OUTPUT_BUFFER;
-    case media::JpegEncodeAccelerator::PARSE_IMAGE_FAILED:
+    case chromeos_camera::JpegEncodeAccelerator::PARSE_IMAGE_FAILED:
       return chromeos_camera::mojom::EncodeStatus::PARSE_IMAGE_FAILED;
-    case media::JpegEncodeAccelerator::PLATFORM_FAILURE:
+    case chromeos_camera::JpegEncodeAccelerator::PLATFORM_FAILURE:
       return chromeos_camera::mojom::EncodeStatus::PLATFORM_FAILURE;
   }
   NOTREACHED();
@@ -35,30 +35,33 @@
 
 // static
 bool EnumTraits<chromeos_camera::mojom::EncodeStatus,
-                media::JpegEncodeAccelerator::Status>::
+                chromeos_camera::JpegEncodeAccelerator::Status>::
     FromMojom(chromeos_camera::mojom::EncodeStatus status,
-              media::JpegEncodeAccelerator::Status* out) {
+              chromeos_camera::JpegEncodeAccelerator::Status* out) {
   switch (status) {
     case chromeos_camera::mojom::EncodeStatus::ENCODE_OK:
-      *out = media::JpegEncodeAccelerator::Status::ENCODE_OK;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::ENCODE_OK;
       return true;
     case chromeos_camera::mojom::EncodeStatus::HW_JPEG_ENCODE_NOT_SUPPORTED:
-      *out = media::JpegEncodeAccelerator::Status::HW_JPEG_ENCODE_NOT_SUPPORTED;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::
+          HW_JPEG_ENCODE_NOT_SUPPORTED;
       return true;
     case chromeos_camera::mojom::EncodeStatus::THREAD_CREATION_FAILED:
-      *out = media::JpegEncodeAccelerator::Status::THREAD_CREATION_FAILED;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::
+          THREAD_CREATION_FAILED;
       return true;
     case chromeos_camera::mojom::EncodeStatus::INVALID_ARGUMENT:
-      *out = media::JpegEncodeAccelerator::Status::INVALID_ARGUMENT;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::INVALID_ARGUMENT;
       return true;
     case chromeos_camera::mojom::EncodeStatus::INACCESSIBLE_OUTPUT_BUFFER:
-      *out = media::JpegEncodeAccelerator::Status::INACCESSIBLE_OUTPUT_BUFFER;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::
+          INACCESSIBLE_OUTPUT_BUFFER;
       return true;
     case chromeos_camera::mojom::EncodeStatus::PARSE_IMAGE_FAILED:
-      *out = media::JpegEncodeAccelerator::Status::PARSE_IMAGE_FAILED;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::PARSE_IMAGE_FAILED;
       return true;
     case chromeos_camera::mojom::EncodeStatus::PLATFORM_FAILURE:
-      *out = media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE;
+      *out = chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE;
       return true;
   }
   NOTREACHED();
diff --git a/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.h b/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.h
index 528593f..19df6bb 100644
--- a/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.h
+++ b/components/chromeos_camera/common/jpeg_encode_accelerator_mojom_traits.h
@@ -6,18 +6,18 @@
 #define COMPONENTS_CHROMEOS_CAMERA_COMMON_JPEG_ENCODE_ACCELERATOR_MOJOM_TRAITS_H_
 
 #include "components/chromeos_camera/common/jpeg_encode_accelerator.mojom.h"
-#include "media/video/jpeg_encode_accelerator.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 
 namespace mojo {
 
 template <>
 struct EnumTraits<chromeos_camera::mojom::EncodeStatus,
-                  media::JpegEncodeAccelerator::Status> {
+                  chromeos_camera::JpegEncodeAccelerator::Status> {
   static chromeos_camera::mojom::EncodeStatus ToMojom(
-      media::JpegEncodeAccelerator::Status status);
+      chromeos_camera::JpegEncodeAccelerator::Status status);
 
   static bool FromMojom(chromeos_camera::mojom::EncodeStatus input,
-                        media::JpegEncodeAccelerator::Status* out);
+                        chromeos_camera::JpegEncodeAccelerator::Status* out);
 };
 
 }  // namespace mojo
diff --git a/components/chromeos_camera/common/mjpeg_decode_accelerator.typemap b/components/chromeos_camera/common/mjpeg_decode_accelerator.typemap
index 662ceff6..bbfda71 100644
--- a/components/chromeos_camera/common/mjpeg_decode_accelerator.typemap
+++ b/components/chromeos_camera/common/mjpeg_decode_accelerator.typemap
@@ -5,8 +5,8 @@
 mojom = "//components/chromeos_camera/common/mjpeg_decode_accelerator.mojom"
 
 public_headers = [
+  "//components/chromeos_camera/mjpeg_decode_accelerator.h",
   "//media/base/bitstream_buffer.h",
-  "//media/video/mjpeg_decode_accelerator.h",
 ]
 
 traits_headers = [
@@ -25,5 +25,5 @@
 
 type_mappings = [
   "chromeos_camera.mojom.BitstreamBuffer=media::BitstreamBuffer",
-  "chromeos_camera.mojom.DecodeError=media::MjpegDecodeAccelerator::Error",
+  "chromeos_camera.mojom.DecodeError=chromeos_camera::MjpegDecodeAccelerator::Error",
 ]
diff --git a/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc b/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc
index 47e3f98..57d307d 100644
--- a/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc
+++ b/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.cc
@@ -14,20 +14,20 @@
 // static
 chromeos_camera::mojom::DecodeError
 EnumTraits<chromeos_camera::mojom::DecodeError,
-           media::MjpegDecodeAccelerator::Error>::
-    ToMojom(media::MjpegDecodeAccelerator::Error error) {
+           chromeos_camera::MjpegDecodeAccelerator::Error>::
+    ToMojom(chromeos_camera::MjpegDecodeAccelerator::Error error) {
   switch (error) {
-    case media::MjpegDecodeAccelerator::NO_ERRORS:
+    case chromeos_camera::MjpegDecodeAccelerator::NO_ERRORS:
       return chromeos_camera::mojom::DecodeError::NO_ERRORS;
-    case media::MjpegDecodeAccelerator::INVALID_ARGUMENT:
+    case chromeos_camera::MjpegDecodeAccelerator::INVALID_ARGUMENT:
       return chromeos_camera::mojom::DecodeError::INVALID_ARGUMENT;
-    case media::MjpegDecodeAccelerator::UNREADABLE_INPUT:
+    case chromeos_camera::MjpegDecodeAccelerator::UNREADABLE_INPUT:
       return chromeos_camera::mojom::DecodeError::UNREADABLE_INPUT;
-    case media::MjpegDecodeAccelerator::PARSE_JPEG_FAILED:
+    case chromeos_camera::MjpegDecodeAccelerator::PARSE_JPEG_FAILED:
       return chromeos_camera::mojom::DecodeError::PARSE_JPEG_FAILED;
-    case media::MjpegDecodeAccelerator::UNSUPPORTED_JPEG:
+    case chromeos_camera::MjpegDecodeAccelerator::UNSUPPORTED_JPEG:
       return chromeos_camera::mojom::DecodeError::UNSUPPORTED_JPEG;
-    case media::MjpegDecodeAccelerator::PLATFORM_FAILURE:
+    case chromeos_camera::MjpegDecodeAccelerator::PLATFORM_FAILURE:
       return chromeos_camera::mojom::DecodeError::PLATFORM_FAILURE;
   }
   NOTREACHED();
@@ -36,27 +36,27 @@
 
 // static
 bool EnumTraits<chromeos_camera::mojom::DecodeError,
-                media::MjpegDecodeAccelerator::Error>::
+                chromeos_camera::MjpegDecodeAccelerator::Error>::
     FromMojom(chromeos_camera::mojom::DecodeError error,
-              media::MjpegDecodeAccelerator::Error* out) {
+              chromeos_camera::MjpegDecodeAccelerator::Error* out) {
   switch (error) {
     case chromeos_camera::mojom::DecodeError::NO_ERRORS:
-      *out = media::MjpegDecodeAccelerator::Error::NO_ERRORS;
+      *out = chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS;
       return true;
     case chromeos_camera::mojom::DecodeError::INVALID_ARGUMENT:
-      *out = media::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT;
+      *out = chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT;
       return true;
     case chromeos_camera::mojom::DecodeError::UNREADABLE_INPUT:
-      *out = media::MjpegDecodeAccelerator::Error::UNREADABLE_INPUT;
+      *out = chromeos_camera::MjpegDecodeAccelerator::Error::UNREADABLE_INPUT;
       return true;
     case chromeos_camera::mojom::DecodeError::PARSE_JPEG_FAILED:
-      *out = media::MjpegDecodeAccelerator::Error::PARSE_JPEG_FAILED;
+      *out = chromeos_camera::MjpegDecodeAccelerator::Error::PARSE_JPEG_FAILED;
       return true;
     case chromeos_camera::mojom::DecodeError::UNSUPPORTED_JPEG:
-      *out = media::MjpegDecodeAccelerator::Error::UNSUPPORTED_JPEG;
+      *out = chromeos_camera::MjpegDecodeAccelerator::Error::UNSUPPORTED_JPEG;
       return true;
     case chromeos_camera::mojom::DecodeError::PLATFORM_FAILURE:
-      *out = media::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE;
+      *out = chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE;
       return true;
   }
   NOTREACHED();
diff --git a/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.h b/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.h
index 5820d60..c5548dc 100644
--- a/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.h
+++ b/components/chromeos_camera/common/mjpeg_decode_accelerator_mojom_traits.h
@@ -7,19 +7,19 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "components/chromeos_camera/common/mjpeg_decode_accelerator.mojom.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 #include "media/base/bitstream_buffer.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 
 namespace mojo {
 
 template <>
 struct EnumTraits<chromeos_camera::mojom::DecodeError,
-                  media::MjpegDecodeAccelerator::Error> {
+                  chromeos_camera::MjpegDecodeAccelerator::Error> {
   static chromeos_camera::mojom::DecodeError ToMojom(
-      media::MjpegDecodeAccelerator::Error error);
+      chromeos_camera::MjpegDecodeAccelerator::Error error);
 
   static bool FromMojom(chromeos_camera::mojom::DecodeError input,
-                        media::MjpegDecodeAccelerator::Error* out);
+                        chromeos_camera::MjpegDecodeAccelerator::Error* out);
 };
 
 template <>
diff --git a/media/gpu/fake_mjpeg_decode_accelerator.cc b/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc
similarity index 83%
rename from media/gpu/fake_mjpeg_decode_accelerator.cc
rename to components/chromeos_camera/fake_mjpeg_decode_accelerator.cc
index 94a3424e..8c17f449 100644
--- a/media/gpu/fake_mjpeg_decode_accelerator.cc
+++ b/components/chromeos_camera/fake_mjpeg_decode_accelerator.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/gpu/fake_mjpeg_decode_accelerator.h"
+#include "components/chromeos_camera/fake_mjpeg_decode_accelerator.h"
 
 #include "base/bind.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/unaligned_shared_memory.h"
 
-namespace media {
+namespace chromeos_camera {
 
 FakeMjpegDecodeAccelerator::FakeMjpegDecodeAccelerator(
     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
@@ -37,11 +37,11 @@
 }
 
 void FakeMjpegDecodeAccelerator::Decode(
-    const BitstreamBuffer& bitstream_buffer,
-    const scoped_refptr<VideoFrame>& video_frame) {
+    const media::BitstreamBuffer& bitstream_buffer,
+    const scoped_refptr<media::VideoFrame>& video_frame) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
 
-  auto src_shm = std::make_unique<UnalignedSharedMemory>(
+  auto src_shm = std::make_unique<media::UnalignedSharedMemory>(
       bitstream_buffer.handle(), bitstream_buffer.size(),
       false /* read_only */);
   if (!src_shm->MapAt(bitstream_buffer.offset(), bitstream_buffer.size())) {
@@ -60,15 +60,15 @@
 }
 
 void FakeMjpegDecodeAccelerator::DecodeOnDecoderThread(
-    const BitstreamBuffer& bitstream_buffer,
-    const scoped_refptr<VideoFrame>& video_frame,
-    std::unique_ptr<UnalignedSharedMemory> src_shm) {
+    const media::BitstreamBuffer& bitstream_buffer,
+    const scoped_refptr<media::VideoFrame>& video_frame,
+    std::unique_ptr<media::UnalignedSharedMemory> src_shm) {
   DCHECK(decoder_task_runner_->BelongsToCurrentThread());
 
   // Do not actually decode the Jpeg data.
   // Instead, just fill the output buffer with zeros.
-  size_t allocation_size =
-      VideoFrame::AllocationSize(PIXEL_FORMAT_I420, video_frame->coded_size());
+  size_t allocation_size = media::VideoFrame::AllocationSize(
+      media::PIXEL_FORMAT_I420, video_frame->coded_size());
   memset(video_frame->data(0), 0, allocation_size);
 
   client_task_runner_->PostTask(
@@ -102,4 +102,4 @@
   client_->VideoFrameReady(input_buffer_id);
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/gpu/fake_mjpeg_decode_accelerator.h b/components/chromeos_camera/fake_mjpeg_decode_accelerator.h
similarity index 67%
rename from media/gpu/fake_mjpeg_decode_accelerator.h
rename to components/chromeos_camera/fake_mjpeg_decode_accelerator.h
index c6e56f6..e81513ce 100644
--- a/media/gpu/fake_mjpeg_decode_accelerator.h
+++ b/components/chromeos_camera/fake_mjpeg_decode_accelerator.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 MEDIA_GPU_FAKE_MJPEG_DECODE_ACCELERATOR_H_
-#define MEDIA_GPU_FAKE_MJPEG_DECODE_ACCELERATOR_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_FAKE_MJPEG_DECODE_ACCELERATOR_H_
+#define COMPONENTS_CHROMEOS_CAMERA_FAKE_MJPEG_DECODE_ACCELERATOR_H_
 
 #include <stdint.h>
 
@@ -12,21 +12,19 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 #include "media/base/bitstream_buffer.h"
-#include "media/gpu/media_gpu_export.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 
 namespace base {
 class SingleThreadTaskRunner;
 }
 
-namespace media {
+namespace chromeos_camera {
 
 // Uses software-based decoding. The purpose of this class is to enable testing
 // of communication to the MjpegDecodeAccelerator without requiring an actual
 // hardware decoder.
-class MEDIA_GPU_EXPORT FakeMjpegDecodeAccelerator
-    : public MjpegDecodeAccelerator {
+class FakeMjpegDecodeAccelerator : public MjpegDecodeAccelerator {
  public:
   FakeMjpegDecodeAccelerator(
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
@@ -34,14 +32,15 @@
 
   // MjpegDecodeAccelerator implementation.
   bool Initialize(MjpegDecodeAccelerator::Client* client) override;
-  void Decode(const BitstreamBuffer& bitstream_buffer,
-              const scoped_refptr<VideoFrame>& video_frame) override;
+  void Decode(const media::BitstreamBuffer& bitstream_buffer,
+              const scoped_refptr<media::VideoFrame>& video_frame) override;
   bool IsSupported() override;
 
  private:
-  void DecodeOnDecoderThread(const BitstreamBuffer& bitstream_buffer,
-                             const scoped_refptr<VideoFrame>& video_frame,
-                             std::unique_ptr<UnalignedSharedMemory> src_shm);
+  void DecodeOnDecoderThread(
+      const media::BitstreamBuffer& bitstream_buffer,
+      const scoped_refptr<media::VideoFrame>& video_frame,
+      std::unique_ptr<media::UnalignedSharedMemory> src_shm);
   void NotifyError(int32_t bitstream_buffer_id, Error error);
   void NotifyErrorOnClientThread(int32_t bitstream_buffer_id, Error error);
   void OnDecodeDoneOnClientThread(int32_t input_buffer_id);
@@ -62,6 +61,6 @@
   DISALLOW_COPY_AND_ASSIGN(FakeMjpegDecodeAccelerator);
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_GPU_FAKE_MJPEG_DECODE_ACCELERATOR_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_FAKE_MJPEG_DECODE_ACCELERATOR_H_
diff --git a/media/gpu/gpu_jpeg_encode_accelerator_factory.cc b/components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.cc
similarity index 85%
rename from media/gpu/gpu_jpeg_encode_accelerator_factory.cc
rename to components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.cc
index bde032c..6145a80 100644
--- a/media/gpu/gpu_jpeg_encode_accelerator_factory.cc
+++ b/components/chromeos_camera/gpu_jpeg_encode_accelerator_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 "media/gpu/gpu_jpeg_encode_accelerator_factory.h"
+#include "components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h"
 
 #include "base/bind.h"
 #include "base/single_thread_task_runner.h"
@@ -24,21 +24,22 @@
 #include "media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h"
 #endif
 
-namespace media {
+namespace chromeos_camera {
 
 namespace {
 
 #if defined(USE_V4L2_JEA)
 std::unique_ptr<JpegEncodeAccelerator> CreateV4L2JEA(
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-  return std::make_unique<V4L2JpegEncodeAccelerator>(std::move(io_task_runner));
+  return std::make_unique<media::V4L2JpegEncodeAccelerator>(
+      std::move(io_task_runner));
 }
 #endif
 
 #if BUILDFLAG(USE_VAAPI)
 std::unique_ptr<JpegEncodeAccelerator> CreateVaapiJEA(
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-  return std::make_unique<VaapiJpegEncodeAccelerator>(
+  return std::make_unique<media::VaapiJpegEncodeAccelerator>(
       std::move(io_task_runner));
 }
 #endif
@@ -65,4 +66,4 @@
   return result;
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/gpu/gpu_jpeg_encode_accelerator_factory.h b/components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h
similarity index 62%
rename from media/gpu/gpu_jpeg_encode_accelerator_factory.h
rename to components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h
index 765562e..373ca69 100644
--- a/media/gpu/gpu_jpeg_encode_accelerator_factory.h
+++ b/components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h
@@ -2,20 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_GPU_JPEG_ENCODE_ACCELERATOR_FACTORY_H_
-#define MEDIA_GPU_GPU_JPEG_ENCODE_ACCELERATOR_FACTORY_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_GPU_JPEG_ENCODE_ACCELERATOR_FACTORY_H_
+#define COMPONENTS_CHROMEOS_CAMERA_GPU_JPEG_ENCODE_ACCELERATOR_FACTORY_H_
 
 #include "base/memory/ref_counted.h"
-#include "media/gpu/media_gpu_export.h"
-#include "media/video/jpeg_encode_accelerator.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 
 namespace base {
 class SingleThreadTaskRunner;
 }
 
-namespace media {
+namespace chromeos_camera {
 
-class MEDIA_GPU_EXPORT GpuJpegEncodeAcceleratorFactory {
+class GpuJpegEncodeAcceleratorFactory {
  public:
   using CreateAcceleratorCB =
       base::RepeatingCallback<std::unique_ptr<JpegEncodeAccelerator>(
@@ -28,6 +27,6 @@
   static std::vector<CreateAcceleratorCB> GetAcceleratorFactories();
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_GPU_GPU_JPEG_ENCODE_ACCELERATOR_FACTORY_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_GPU_JPEG_ENCODE_ACCELERATOR_FACTORY_H_
diff --git a/media/gpu/gpu_mjpeg_decode_accelerator_factory.cc b/components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.cc
similarity index 85%
rename from media/gpu/gpu_mjpeg_decode_accelerator_factory.cc
rename to components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.cc
index 2fc34021..016d6cd4 100644
--- a/media/gpu/gpu_mjpeg_decode_accelerator_factory.cc
+++ b/components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/gpu/gpu_mjpeg_decode_accelerator_factory.h"
+#include "components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h"
 
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "components/chromeos_camera/fake_mjpeg_decode_accelerator.h"
 #include "media/base/media_switches.h"
 #include "media/gpu/buildflags.h"
-#include "media/gpu/fake_mjpeg_decode_accelerator.h"
 
 #if BUILDFLAG(USE_V4L2_CODEC) && defined(ARCH_CPU_ARM_FAMILY)
 #define USE_V4L2_MJPEG_DECODE_ACCELERATOR
@@ -26,7 +26,7 @@
 #include "media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.h"
 #endif
 
-namespace media {
+namespace chromeos_camera {
 
 namespace {
 
@@ -34,10 +34,10 @@
 std::unique_ptr<MjpegDecodeAccelerator> CreateV4L2MjpegDecodeAccelerator(
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
   std::unique_ptr<MjpegDecodeAccelerator> decoder;
-  scoped_refptr<V4L2Device> device = V4L2Device::Create();
+  scoped_refptr<media::V4L2Device> device = media::V4L2Device::Create();
   if (device) {
-    decoder.reset(
-        new V4L2MjpegDecodeAccelerator(device, std::move(io_task_runner)));
+    decoder.reset(new media::V4L2MjpegDecodeAccelerator(
+        device, std::move(io_task_runner)));
   }
   return decoder;
 }
@@ -46,7 +46,7 @@
 #if BUILDFLAG(USE_VAAPI)
 std::unique_ptr<MjpegDecodeAccelerator> CreateVaapiMjpegDecodeAccelerator(
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-  return std::make_unique<VaapiMjpegDecodeAccelerator>(
+  return std::make_unique<media::VaapiMjpegDecodeAccelerator>(
       std::move(io_task_runner));
 }
 #endif
@@ -90,4 +90,4 @@
   return result;
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/gpu/gpu_mjpeg_decode_accelerator_factory.h b/components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h
similarity index 61%
rename from media/gpu/gpu_mjpeg_decode_accelerator_factory.h
rename to components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h
index c31ac3a..6cd60c8 100644
--- a/media/gpu/gpu_mjpeg_decode_accelerator_factory.h
+++ b/components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h
@@ -2,20 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_GPU_GPU_MJPEG_DECODE_ACCELERATOR_FACTORY_H_
-#define MEDIA_GPU_GPU_MJPEG_DECODE_ACCELERATOR_FACTORY_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_GPU_MJPEG_DECODE_ACCELERATOR_FACTORY_H_
+#define COMPONENTS_CHROMEOS_CAMERA_GPU_MJPEG_DECODE_ACCELERATOR_FACTORY_H_
 
 #include "base/memory/ref_counted.h"
-#include "media/gpu/media_gpu_export.h"
-#include "media/video/mjpeg_decode_accelerator.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 
 namespace base {
 class SingleThreadTaskRunner;
 }
 
-namespace media {
+namespace chromeos_camera {
 
-class MEDIA_GPU_EXPORT GpuMjpegDecodeAcceleratorFactory {
+class GpuMjpegDecodeAcceleratorFactory {
  public:
   using CreateAcceleratorCB =
       base::Callback<std::unique_ptr<MjpegDecodeAccelerator>(
@@ -28,6 +27,6 @@
   static std::vector<CreateAcceleratorCB> GetAcceleratorFactories();
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_GPU_GPU_MJPEG_DECODE_ACCELERATOR_FACTORY_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_GPU_MJPEG_DECODE_ACCELERATOR_FACTORY_H_
diff --git a/media/video/jpeg_encode_accelerator.cc b/components/chromeos_camera/jpeg_encode_accelerator.cc
similarity index 64%
rename from media/video/jpeg_encode_accelerator.cc
rename to components/chromeos_camera/jpeg_encode_accelerator.cc
index 91b2bf2..74ff283 100644
--- a/media/video/jpeg_encode_accelerator.cc
+++ b/components/chromeos_camera/jpeg_encode_accelerator.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/video/jpeg_encode_accelerator.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 
-namespace media {
+namespace chromeos_camera {
 
 JpegEncodeAccelerator::~JpegEncodeAccelerator() = default;
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/video/jpeg_encode_accelerator.h b/components/chromeos_camera/jpeg_encode_accelerator.h
similarity index 87%
rename from media/video/jpeg_encode_accelerator.h
rename to components/chromeos_camera/jpeg_encode_accelerator.h
index 9c2b289..26f6cc52 100644
--- a/media/video/jpeg_encode_accelerator.h
+++ b/components/chromeos_camera/jpeg_encode_accelerator.h
@@ -2,19 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_VIDEO_JPEG_ENCODE_ACCELERATOR_H_
-#define MEDIA_VIDEO_JPEG_ENCODE_ACCELERATOR_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_JPEG_ENCODE_ACCELERATOR_H_
+#define COMPONENTS_CHROMEOS_CAMERA_JPEG_ENCODE_ACCELERATOR_H_
 
 #include <stdint.h>
 
 #include "media/base/bitstream_buffer.h"
-#include "media/base/media_export.h"
 #include "media/base/video_frame.h"
 
-namespace media {
+namespace chromeos_camera {
 
 // JPEG encoder interface.
-class MEDIA_EXPORT JpegEncodeAccelerator {
+class JpegEncodeAccelerator {
  public:
   static constexpr int32_t kInvalidBitstreamBufferId = -1;
   enum Status {
@@ -45,7 +44,7 @@
     LARGEST_ERROR_ENUM = PLATFORM_FAILURE,
   };
 
-  class MEDIA_EXPORT Client {
+  class Client {
    public:
     // Callback called after each successful Encode().
     // Parameters:
@@ -102,8 +101,8 @@
   //  called.
   virtual void Encode(scoped_refptr<media::VideoFrame> video_frame,
                       int quality,
-                      const BitstreamBuffer* exif_buffer,
-                      const BitstreamBuffer& output_buffer) = 0;
+                      const media::BitstreamBuffer* exif_buffer,
+                      const media::BitstreamBuffer& output_buffer) = 0;
 
   // Encodes the given |video_frame| that contains a YUV image. Client will
   // receive the encoded result in Client::VideoFrameReady() callback with the
@@ -116,13 +115,13 @@
   //  quality.
   //  |exif_buffer| contains Exif data to be inserted into JPEG image. If it's
   //  nullptr, the JFIF APP0 segment will be inserted.
-  virtual void EncodeWithDmaBuf(scoped_refptr<VideoFrame> input_frame,
-                                scoped_refptr<VideoFrame> output_frame,
+  virtual void EncodeWithDmaBuf(scoped_refptr<media::VideoFrame> input_frame,
+                                scoped_refptr<media::VideoFrame> output_frame,
                                 int quality,
                                 int32_t buffer_id,
-                                const BitstreamBuffer* exif_buffer) = 0;
+                                const media::BitstreamBuffer* exif_buffer) = 0;
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_VIDEO_JPEG_ENCODE_ACCELERATOR_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_JPEG_ENCODE_ACCELERATOR_H_
diff --git a/media/gpu/jpeg_encode_accelerator_unittest.cc b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
similarity index 95%
rename from media/gpu/jpeg_encode_accelerator_unittest.cc
rename to components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
index f6fe7bcc..543819f 100644
--- a/media/gpu/jpeg_encode_accelerator_unittest.cc
+++ b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
@@ -23,12 +23,12 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 #include "media/base/test_data_util.h"
 #include "media/filters/jpeg_parser.h"
 #include "media/gpu/buildflags.h"
-#include "media/gpu/gpu_jpeg_encode_accelerator_factory.h"
 #include "media/gpu/test/video_accelerator_unittest_helpers.h"
-#include "media/video/jpeg_encode_accelerator.h"
 #include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libyuv/include/libyuv.h"
@@ -38,7 +38,7 @@
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #endif
 
-namespace media {
+namespace chromeos_camera {
 namespace {
 
 // Default test image file.
@@ -232,7 +232,7 @@
     const std::string& name) {
   base::FilePath file_path = base::FilePath(name);
   if (!PathExists(file_path)) {
-    file_path = GetTestDataFilePath(name);
+    file_path = media::GetTestDataFilePath(name);
   }
   VLOG(3) << "Using file path " << file_path.value();
   return file_path;
@@ -296,7 +296,7 @@
   media::test::ClientStateNotification<ClientState>* note_;
 
   // Output buffer prepared for JpegEncodeAccelerator.
-  std::unique_ptr<BitstreamBuffer> encoded_buffer_;
+  std::unique_ptr<media::BitstreamBuffer> encoded_buffer_;
 
   // Mapped memory of input file.
   std::unique_ptr<base::SharedMemory> in_shm_;
@@ -558,13 +558,15 @@
 
   base::SharedMemoryHandle dup_handle;
   dup_handle = base::SharedMemory::DuplicateHandle(hw_out_shm_->handle());
-  encoded_buffer_ = std::make_unique<BitstreamBuffer>(
+  encoded_buffer_ = std::make_unique<media::BitstreamBuffer>(
       bitstream_buffer_id, dup_handle, test_image->output_size);
-  scoped_refptr<VideoFrame> input_frame_ = VideoFrame::WrapExternalSharedMemory(
-      PIXEL_FORMAT_I420, test_image->visible_size,
-      gfx::Rect(test_image->visible_size), test_image->visible_size,
-      static_cast<uint8_t*>(in_shm_->memory()), test_image->image_data.size(),
-      in_shm_->handle(), 0, base::TimeDelta());
+  scoped_refptr<media::VideoFrame> input_frame_ =
+      media::VideoFrame::WrapExternalSharedMemory(
+          media::PIXEL_FORMAT_I420, test_image->visible_size,
+          gfx::Rect(test_image->visible_size), test_image->visible_size,
+          static_cast<uint8_t*>(in_shm_->memory()),
+          test_image->image_data.size(), in_shm_->handle(), 0,
+          base::TimeDelta());
 
   LOG_ASSERT(input_frame_.get());
 
@@ -697,7 +699,7 @@
 }
 
 }  // namespace
-}  // namespace media
+}  // namespace chromeos_camera
 
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
@@ -739,7 +741,7 @@
       continue;
     }
     if (it->first == "save_to_file") {
-      media::g_save_to_file = true;
+      chromeos_camera::g_save_to_file = true;
       continue;
     }
     if (it->first == "v" || it->first == "vmodule")
@@ -753,10 +755,11 @@
   media::VaapiWrapper::PreSandboxInitialization();
 #endif
 
-  media::g_env = reinterpret_cast<media::JpegEncodeAcceleratorTestEnvironment*>(
-      testing::AddGlobalTestEnvironment(
-          new media::JpegEncodeAcceleratorTestEnvironment(yuv_filenames,
-                                                          log_path, repeat)));
+  chromeos_camera::g_env =
+      reinterpret_cast<chromeos_camera::JpegEncodeAcceleratorTestEnvironment*>(
+          testing::AddGlobalTestEnvironment(
+              new chromeos_camera::JpegEncodeAcceleratorTestEnvironment(
+                  yuv_filenames, log_path, repeat)));
 
   return RUN_ALL_TESTS();
 }
diff --git a/media/video/mjpeg_decode_accelerator.cc b/components/chromeos_camera/mjpeg_decode_accelerator.cc
similarity index 64%
rename from media/video/mjpeg_decode_accelerator.cc
rename to components/chromeos_camera/mjpeg_decode_accelerator.cc
index b538ce8..ad8f3ca 100644
--- a/media/video/mjpeg_decode_accelerator.cc
+++ b/components/chromeos_camera/mjpeg_decode_accelerator.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/video/mjpeg_decode_accelerator.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 
-namespace media {
+namespace chromeos_camera {
 
 MjpegDecodeAccelerator::~MjpegDecodeAccelerator() = default;
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/video/mjpeg_decode_accelerator.h b/components/chromeos_camera/mjpeg_decode_accelerator.h
similarity index 93%
rename from media/video/mjpeg_decode_accelerator.h
rename to components/chromeos_camera/mjpeg_decode_accelerator.h
index 6d023f4..ab1275f 100644
--- a/media/video/mjpeg_decode_accelerator.h
+++ b/components/chromeos_camera/mjpeg_decode_accelerator.h
@@ -2,16 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_VIDEO_MJPEG_DECODE_ACCELERATOR_H_
-#define MEDIA_VIDEO_MJPEG_DECODE_ACCELERATOR_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_MJPEG_DECODE_ACCELERATOR_H_
+#define COMPONENTS_CHROMEOS_CAMERA_MJPEG_DECODE_ACCELERATOR_H_
 
 #include <stdint.h>
 
 #include "media/base/bitstream_buffer.h"
-#include "media/base/media_export.h"
 #include "media/base/video_frame.h"
 
-namespace media {
+namespace chromeos_camera {
 
 // MJPEG decoder interface.
 // The input are JPEG images including headers (Huffman tables may be omitted).
@@ -23,7 +22,7 @@
 // from camera capture. It can also be used for normal still JPEG image
 // decoding, but normal JPEG images may use more JPEG features that may not be
 // supported by a particular accelerator implementation and/or platform.
-class MEDIA_EXPORT MjpegDecodeAccelerator {
+class MjpegDecodeAccelerator {
  public:
   // Callback for JPEG decoder initialization.
   typedef base::Callback<void(bool success)> InitCB;
@@ -55,7 +54,7 @@
     MJDA_ERROR_CODE_MAX = PLATFORM_FAILURE,
   };
 
-  class MEDIA_EXPORT Client {
+  class Client {
    public:
     // Callback called after each successful Decode().
     // Parameters:
@@ -115,7 +114,7 @@
   //  client. The client is not allowed to deallocate them before
   //  VideoFrameReady or NotifyError() is invoked for given id of
   //  |bitstream_buffer|, or destructor returns.
-  virtual void Decode(const BitstreamBuffer& bitstream_buffer,
+  virtual void Decode(const media::BitstreamBuffer& bitstream_buffer,
                       const scoped_refptr<media::VideoFrame>& video_frame) = 0;
 
   // Returns true when the JPEG decoder is supported. This can be called before
@@ -123,6 +122,6 @@
   virtual bool IsSupported() = 0;
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_VIDEO_MJPEG_DECODE_ACCELERATOR_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_MJPEG_DECODE_ACCELERATOR_H_
diff --git a/media/gpu/jpeg_decode_accelerator_unittest.cc b/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
similarity index 82%
rename from media/gpu/jpeg_decode_accelerator_unittest.cc
rename to components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
index fffbfd4..c3b7a33 100644
--- a/media/gpu/jpeg_decode_accelerator_unittest.cc
+++ b/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
@@ -23,12 +23,12 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
+#include "components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 #include "media/base/test_data_util.h"
 #include "media/filters/jpeg_parser.h"
 #include "media/gpu/buildflags.h"
-#include "media/gpu/gpu_mjpeg_decode_accelerator_factory.h"
 #include "media/gpu/test/video_accelerator_unittest_helpers.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 #include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libyuv/include/libyuv.h"
@@ -39,7 +39,7 @@
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #endif
 
-namespace media {
+namespace chromeos_camera {
 namespace {
 
 // Default test image file.
@@ -61,8 +61,8 @@
 constexpr double kDecodeSimilarityThreshold = 1.25;
 
 // Environment to create test data for all test cases.
-class JpegDecodeAcceleratorTestEnvironment;
-JpegDecodeAcceleratorTestEnvironment* g_env;
+class MjpegDecodeAcceleratorTestEnvironment;
+MjpegDecodeAcceleratorTestEnvironment* g_env;
 
 // This struct holds a parsed, complete JPEG blob. It can be created from a
 // FilePath or can be simply a black image.
@@ -74,7 +74,7 @@
     LOG_ASSERT(base::ReadFileToString(file_path, &image->data_str))
         << file_path;
 
-    JpegParseResult parse_result;
+    media::JpegParseResult parse_result;
     LOG_ASSERT(ParseJpegPicture(
         reinterpret_cast<const uint8_t*>(image->data_str.data()),
         image->data_str.size(), &parse_result));
@@ -123,12 +123,13 @@
     // building the video frame to hold the result of the decoding, the strides
     // and pointers for the UV planes are computed correctly for JPEGs that
     // require even-sized allocation (see
-    // VideoFrame::RequiresEvenSizeAllocation()) and whose visible size has at
-    // least one odd dimension.
+    // media::VideoFrame::RequiresEvenSizeAllocation()) and whose visible size
+    // has at least one odd dimension.
     coded_size.SetSize((visible_size.width() + 1) & ~1,
                        (visible_size.height() + 1) & ~1);
     // The JPEG decoder will always return the decoded frame in I420 format.
-    output_size = VideoFrame::AllocationSize(PIXEL_FORMAT_I420, coded_size);
+    output_size =
+        media::VideoFrame::AllocationSize(media::PIXEL_FORMAT_I420, coded_size);
   }
 
   const base::FilePath::StringType& filename() const {
@@ -144,9 +145,9 @@
 };
 
 // Global singleton to hold on to common data and other user-defined options.
-class JpegDecodeAcceleratorTestEnvironment : public ::testing::Environment {
+class MjpegDecodeAcceleratorTestEnvironment : public ::testing::Environment {
  public:
-  JpegDecodeAcceleratorTestEnvironment(
+  MjpegDecodeAcceleratorTestEnvironment(
       const base::FilePath::CharType* jpeg_filenames,
       const base::FilePath::CharType* test_data_path,
       int perf_decode_times)
@@ -168,7 +169,7 @@
       return original_file_path;
     if (test_data_path_)
       return base::FilePath(test_data_path_).Append(original_file_path);
-    return GetTestDataFilePath(file_path);
+    return media::GetTestDataFilePath(file_path);
   }
 
   // Used for InputSizeChange test case. The image size should be smaller than
@@ -197,7 +198,7 @@
   const base::FilePath::CharType* test_data_path_;
 };
 
-void JpegDecodeAcceleratorTestEnvironment::SetUp() {
+void MjpegDecodeAcceleratorTestEnvironment::SetUp() {
   image_data_1280x720_black_ = ParsedJpegImage::CreateBlackImage(1280, 720);
   image_data_640x368_black_ = ParsedJpegImage::CreateBlackImage(640, 368);
   image_data_640x360_black_ = ParsedJpegImage::CreateBlackImage(640, 360);
@@ -268,7 +269,7 @@
   // Save a video frame that contains a decoded JPEG. The output is a PNG file.
   // The suffix will be added before the .png extension.
   void SaveToFile(int32_t bitstream_buffer_id,
-                  const scoped_refptr<VideoFrame>& in_frame,
+                  const scoped_refptr<media::VideoFrame>& in_frame,
                   const std::string& suffix = "");
 
   // Calculate mean absolute difference of hardware and software decode results
@@ -291,11 +292,11 @@
   // Mapped memory of output buffer from hardware decoder.
   std::unique_ptr<base::SharedMemory> hw_out_shm_;
   // Video frame corresponding to the output of the hardware decoder.
-  scoped_refptr<VideoFrame> hw_out_frame_;
+  scoped_refptr<media::VideoFrame> hw_out_frame_;
   // Mapped memory of output buffer from software decoder.
   std::unique_ptr<base::SharedMemory> sw_out_shm_;
   // Video frame corresponding to the output of the software decoder.
-  scoped_refptr<VideoFrame> sw_out_frame_;
+  scoped_refptr<media::VideoFrame> sw_out_frame_;
 
   // This should be the first member to get destroyed because |decoder_|
   // potentially uses other members in the JpegClient instance. For example,
@@ -323,7 +324,7 @@
   auto jda_factories =
       GpuMjpegDecodeAcceleratorFactory::GetAcceleratorFactories();
   if (jda_factories.empty()) {
-    LOG(ERROR) << "JpegDecodeAccelerator not supported on this platform.";
+    LOG(ERROR) << "MjpegDecodeAccelerator not supported on this platform.";
     SetState(CS_ERROR);
     return;
   }
@@ -334,13 +335,13 @@
       break;
   }
   if (!decoder_) {
-    LOG(ERROR) << "Failed to create JpegDecodeAccelerator.";
+    LOG(ERROR) << "Failed to create MjpegDecodeAccelerator.";
     SetState(CS_ERROR);
     return;
   }
 
   if (!decoder_->Initialize(this)) {
-    LOG(ERROR) << "JpegDecodeAccelerator::Initialize() failed";
+    LOG(ERROR) << "MjpegDecodeAccelerator::Initialize() failed";
     SetState(CS_ERROR);
     return;
   }
@@ -411,31 +412,32 @@
 }
 
 void JpegClient::SaveToFile(int32_t bitstream_buffer_id,
-                            const scoped_refptr<VideoFrame>& in_frame,
+                            const scoped_refptr<media::VideoFrame>& in_frame,
                             const std::string& suffix) {
   LOG_ASSERT(in_frame.get());
   ParsedJpegImage* image_file = test_image_files_[bitstream_buffer_id];
 
   // First convert to ARGB format. Note that in our case, the coded size and the
   // visible size will be the same.
-  scoped_refptr<VideoFrame> argb_out_frame = VideoFrame::CreateFrame(
-      VideoPixelFormat::PIXEL_FORMAT_ARGB, image_file->visible_size,
-      gfx::Rect(image_file->visible_size), image_file->visible_size,
-      base::TimeDelta());
+  scoped_refptr<media::VideoFrame> argb_out_frame =
+      media::VideoFrame::CreateFrame(
+          media::VideoPixelFormat::PIXEL_FORMAT_ARGB, image_file->visible_size,
+          gfx::Rect(image_file->visible_size), image_file->visible_size,
+          base::TimeDelta());
   LOG_ASSERT(argb_out_frame);
   LOG_ASSERT(in_frame->visible_rect() == argb_out_frame->visible_rect());
 
   // Note that we use J420ToARGB instead of I420ToARGB so that the
   // kYuvJPEGConstants YUV-to-RGB conversion matrix is used.
   const int conversion_status =
-      libyuv::J420ToARGB(in_frame->data(VideoFrame::kYPlane),
-                         in_frame->stride(VideoFrame::kYPlane),
-                         in_frame->data(VideoFrame::kUPlane),
-                         in_frame->stride(VideoFrame::kUPlane),
-                         in_frame->data(VideoFrame::kVPlane),
-                         in_frame->stride(VideoFrame::kVPlane),
-                         argb_out_frame->data(VideoFrame::kARGBPlane),
-                         argb_out_frame->stride(VideoFrame::kARGBPlane),
+      libyuv::J420ToARGB(in_frame->data(media::VideoFrame::kYPlane),
+                         in_frame->stride(media::VideoFrame::kYPlane),
+                         in_frame->data(media::VideoFrame::kUPlane),
+                         in_frame->stride(media::VideoFrame::kUPlane),
+                         in_frame->data(media::VideoFrame::kVPlane),
+                         in_frame->stride(media::VideoFrame::kVPlane),
+                         argb_out_frame->data(media::VideoFrame::kARGBPlane),
+                         argb_out_frame->stride(media::VideoFrame::kARGBPlane),
                          argb_out_frame->visible_rect().width(),
                          argb_out_frame->visible_rect().height());
   LOG_ASSERT(conversion_status == 0);
@@ -443,9 +445,9 @@
   // Save as a PNG.
   std::vector<uint8_t> png_output;
   const bool png_encode_status = gfx::PNGCodec::Encode(
-      argb_out_frame->data(VideoFrame::kARGBPlane), gfx::PNGCodec::FORMAT_BGRA,
-      argb_out_frame->visible_rect().size(),
-      argb_out_frame->stride(VideoFrame::kARGBPlane),
+      argb_out_frame->data(media::VideoFrame::kARGBPlane),
+      gfx::PNGCodec::FORMAT_BGRA, argb_out_frame->visible_rect().size(),
+      argb_out_frame->stride(media::VideoFrame::kARGBPlane),
       true, /* discard_transparency */
       std::vector<gfx::PNGCodec::Comment>(), &png_output);
   LOG_ASSERT(png_encode_status);
@@ -461,16 +463,18 @@
 double JpegClient::GetMeanAbsoluteDifference() {
   double mean_abs_difference = 0;
   size_t num_samples = 0;
-  const size_t planes[] = {VideoFrame::kYPlane, VideoFrame::kUPlane,
-                           VideoFrame::kVPlane};
+  const size_t planes[] = {media::VideoFrame::kYPlane,
+                           media::VideoFrame::kUPlane,
+                           media::VideoFrame::kVPlane};
   for (size_t plane : planes) {
     const uint8_t* hw_data = hw_out_frame_->data(plane);
     const uint8_t* sw_data = sw_out_frame_->data(plane);
     LOG_ASSERT(hw_out_frame_->visible_rect() == sw_out_frame_->visible_rect());
-    const size_t rows = VideoFrame::Rows(
-        plane, PIXEL_FORMAT_I420, hw_out_frame_->visible_rect().height());
-    const size_t columns = VideoFrame::Columns(
-        plane, PIXEL_FORMAT_I420, hw_out_frame_->visible_rect().width());
+    const size_t rows =
+        media::VideoFrame::Rows(plane, media::PIXEL_FORMAT_I420,
+                                hw_out_frame_->visible_rect().height());
+    const size_t columns = media::VideoFrame::Columns(
+        plane, media::PIXEL_FORMAT_I420, hw_out_frame_->visible_rect().width());
     LOG_ASSERT(hw_out_frame_->stride(plane) == sw_out_frame_->stride(plane));
     const int stride = hw_out_frame_->stride(plane);
     for (size_t row = 0; row < rows; ++row) {
@@ -496,11 +500,11 @@
 
   base::SharedMemoryHandle dup_handle;
   dup_handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
-  BitstreamBuffer bitstream_buffer(bitstream_buffer_id, dup_handle,
-                                   image_file->data_str.size());
+  media::BitstreamBuffer bitstream_buffer(bitstream_buffer_id, dup_handle,
+                                          image_file->data_str.size());
 
-  hw_out_frame_ = VideoFrame::WrapExternalSharedMemory(
-      PIXEL_FORMAT_I420, image_file->coded_size,
+  hw_out_frame_ = media::VideoFrame::WrapExternalSharedMemory(
+      media::PIXEL_FORMAT_I420, image_file->coded_size,
       gfx::Rect(image_file->visible_size), image_file->visible_size,
       static_cast<uint8_t*>(hw_out_shm_->memory()), image_file->output_size,
       hw_out_shm_->handle(), 0, base::TimeDelta());
@@ -511,8 +515,8 @@
 
 bool JpegClient::GetSoftwareDecodeResult(int32_t bitstream_buffer_id) {
   ParsedJpegImage* image_file = test_image_files_[bitstream_buffer_id];
-  sw_out_frame_ = VideoFrame::WrapExternalSharedMemory(
-      PIXEL_FORMAT_I420, image_file->coded_size,
+  sw_out_frame_ = media::VideoFrame::WrapExternalSharedMemory(
+      media::PIXEL_FORMAT_I420, image_file->coded_size,
       gfx::Rect(image_file->visible_size), image_file->visible_size,
       static_cast<uint8_t*>(sw_out_shm_->memory()), image_file->output_size,
       sw_out_shm_->handle(), 0, base::TimeDelta());
@@ -520,13 +524,13 @@
 
   if (libyuv::ConvertToI420(static_cast<uint8_t*>(in_shm_->memory()),
                             image_file->data_str.size(),
-                            sw_out_frame_->data(VideoFrame::kYPlane),
-                            sw_out_frame_->stride(VideoFrame::kYPlane),
-                            sw_out_frame_->data(VideoFrame::kUPlane),
-                            sw_out_frame_->stride(VideoFrame::kUPlane),
-                            sw_out_frame_->data(VideoFrame::kVPlane),
-                            sw_out_frame_->stride(VideoFrame::kVPlane), 0, 0,
-                            sw_out_frame_->visible_rect().width(),
+                            sw_out_frame_->data(media::VideoFrame::kYPlane),
+                            sw_out_frame_->stride(media::VideoFrame::kYPlane),
+                            sw_out_frame_->data(media::VideoFrame::kUPlane),
+                            sw_out_frame_->stride(media::VideoFrame::kUPlane),
+                            sw_out_frame_->data(media::VideoFrame::kVPlane),
+                            sw_out_frame_->stride(media::VideoFrame::kVPlane),
+                            0, 0, sw_out_frame_->visible_rect().width(),
                             sw_out_frame_->visible_rect().height(),
                             sw_out_frame_->visible_rect().width(),
                             sw_out_frame_->visible_rect().height(),
@@ -557,9 +561,9 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedJpegClient);
 };
 
-class JpegDecodeAcceleratorTest : public ::testing::Test {
+class MjpegDecodeAcceleratorTest : public ::testing::Test {
  protected:
-  JpegDecodeAcceleratorTest() = default;
+  MjpegDecodeAcceleratorTest() = default;
 
   void TestDecode(const std::vector<ParsedJpegImage*>& images,
                   const std::vector<ClientState>& expected_status,
@@ -570,10 +574,10 @@
                       const std::vector<ParsedJpegImage*>& images);
 
  protected:
-  DISALLOW_COPY_AND_ASSIGN(JpegDecodeAcceleratorTest);
+  DISALLOW_COPY_AND_ASSIGN(MjpegDecodeAcceleratorTest);
 };
 
-void JpegDecodeAcceleratorTest::TestDecode(
+void MjpegDecodeAcceleratorTest::TestDecode(
     const std::vector<ParsedJpegImage*>& images,
     const std::vector<ClientState>& expected_status,
     size_t num_concurrent_decoders) {
@@ -614,7 +618,7 @@
   }
 }
 
-void JpegDecodeAcceleratorTest::PerfDecodeByJDA(
+void MjpegDecodeAcceleratorTest::PerfDecodeByJDA(
     int decode_times,
     const std::vector<ParsedJpegImage*>& images) {
   LOG_ASSERT(images.size() == 1);
@@ -651,7 +655,7 @@
             << images[0]->filename();
 }
 
-void JpegDecodeAcceleratorTest::PerfDecodeBySW(
+void MjpegDecodeAcceleratorTest::PerfDecodeBySW(
     int decode_times,
     const std::vector<ParsedJpegImage*>& images) {
   LOG_ASSERT(images.size() == 1);
@@ -674,21 +678,22 @@
             << images[0]->filename();
 }
 
-// Returns a VideoFrame that contains YUV data using 4:2:0 subsampling. The
-// visible size is 3x3, and the coded size is 4x4 which is 3x3 rounded up to the
-// next even dimensions.
-scoped_refptr<VideoFrame> GetTestDecodedData() {
-  scoped_refptr<VideoFrame> frame = VideoFrame::CreateZeroInitializedFrame(
-      PIXEL_FORMAT_I420, gfx::Size(4, 4) /* coded_size */,
-      gfx::Rect(3, 3) /* visible_rect */, gfx::Size(3, 3) /* natural_size */,
-      base::TimeDelta());
+// Returns a media::VideoFrame that contains YUV data using 4:2:0 subsampling.
+// The visible size is 3x3, and the coded size is 4x4 which is 3x3 rounded up to
+// the next even dimensions.
+scoped_refptr<media::VideoFrame> GetTestDecodedData() {
+  scoped_refptr<media::VideoFrame> frame =
+      media::VideoFrame::CreateZeroInitializedFrame(
+          media::PIXEL_FORMAT_I420, gfx::Size(4, 4) /* coded_size */,
+          gfx::Rect(3, 3) /* visible_rect */,
+          gfx::Size(3, 3) /* natural_size */, base::TimeDelta());
   LOG_ASSERT(frame.get());
-  uint8_t* y_data = frame->data(VideoFrame::kYPlane);
-  int y_stride = frame->stride(VideoFrame::kYPlane);
-  uint8_t* u_data = frame->data(VideoFrame::kUPlane);
-  int u_stride = frame->stride(VideoFrame::kUPlane);
-  uint8_t* v_data = frame->data(VideoFrame::kVPlane);
-  int v_stride = frame->stride(VideoFrame::kVPlane);
+  uint8_t* y_data = frame->data(media::VideoFrame::kYPlane);
+  int y_stride = frame->stride(media::VideoFrame::kYPlane);
+  uint8_t* u_data = frame->data(media::VideoFrame::kUPlane);
+  int u_stride = frame->stride(media::VideoFrame::kUPlane);
+  uint8_t* v_data = frame->data(media::VideoFrame::kVPlane);
+  int v_stride = frame->stride(media::VideoFrame::kVPlane);
 
   // Data for the Y plane.
   memcpy(&y_data[0 * y_stride], "\x01\x02\x03", 3);
@@ -711,12 +716,12 @@
   client.hw_out_frame_ = GetTestDecodedData();
   client.sw_out_frame_ = GetTestDecodedData();
 
-  uint8_t* y_data = client.sw_out_frame_->data(VideoFrame::kYPlane);
-  const int y_stride = client.sw_out_frame_->stride(VideoFrame::kYPlane);
-  uint8_t* u_data = client.sw_out_frame_->data(VideoFrame::kUPlane);
-  const int u_stride = client.sw_out_frame_->stride(VideoFrame::kUPlane);
-  uint8_t* v_data = client.sw_out_frame_->data(VideoFrame::kVPlane);
-  const int v_stride = client.sw_out_frame_->stride(VideoFrame::kVPlane);
+  uint8_t* y_data = client.sw_out_frame_->data(media::VideoFrame::kYPlane);
+  const int y_stride = client.sw_out_frame_->stride(media::VideoFrame::kYPlane);
+  uint8_t* u_data = client.sw_out_frame_->data(media::VideoFrame::kUPlane);
+  const int u_stride = client.sw_out_frame_->stride(media::VideoFrame::kUPlane);
+  uint8_t* v_data = client.sw_out_frame_->data(media::VideoFrame::kVPlane);
+  const int v_stride = client.sw_out_frame_->stride(media::VideoFrame::kVPlane);
 
   // Change some visible data in the software decoding result.
   double expected_abs_mean_diff = 0;
@@ -741,7 +746,7 @@
               kMaxAllowedDifference);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, SimpleDecode) {
+TEST_F(MjpegDecodeAcceleratorTest, SimpleDecode) {
   std::vector<ParsedJpegImage*> images;
   for (auto& image : g_env->image_data_user_)
     images.push_back(image.get());
@@ -749,7 +754,7 @@
   TestDecode(images, expected_status);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, MultipleDecoders) {
+TEST_F(MjpegDecodeAcceleratorTest, MultipleDecoders) {
   std::vector<ParsedJpegImage*> images;
   for (auto& image : g_env->image_data_user_)
     images.push_back(image.get());
@@ -759,7 +764,7 @@
 
 #if !(BUILDFLAG(USE_V4L2_CODEC) && defined(ARCH_CPU_ARM_FAMILY))
 // TODO(andrescj): re-enable for ARM devices when crbug.com/852236 is fixed.
-TEST_F(JpegDecodeAcceleratorTest, OddDimensions) {
+TEST_F(MjpegDecodeAcceleratorTest, OddDimensions) {
   std::vector<ParsedJpegImage*> images;
   for (auto& image : g_env->image_data_odd_)
     images.push_back(image.get());
@@ -768,7 +773,7 @@
 }
 #endif
 
-TEST_F(JpegDecodeAcceleratorTest, InputSizeChange) {
+TEST_F(MjpegDecodeAcceleratorTest, InputSizeChange) {
   // The size of |image_data_1280x720_black_| is smaller than
   // |image_data_1280x720_default_|.
   const std::vector<ParsedJpegImage*> images = {
@@ -779,7 +784,7 @@
   TestDecode(images, expected_status);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, ResolutionChange) {
+TEST_F(MjpegDecodeAcceleratorTest, ResolutionChange) {
   const std::vector<ParsedJpegImage*> images = {
       g_env->image_data_640x368_black_.get(),
       g_env->image_data_1280x720_default_.get(),
@@ -788,7 +793,7 @@
   TestDecode(images, expected_status);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, CodedSizeAlignment) {
+TEST_F(MjpegDecodeAcceleratorTest, CodedSizeAlignment) {
   const std::vector<ParsedJpegImage*> images = {
       g_env->image_data_640x360_black_.get()};
   const std::vector<ClientState> expected_status = {CS_DECODE_PASS};
@@ -796,7 +801,7 @@
 }
 
 // Tests whether different JPEG sampling formats will be decoded correctly.
-TEST_F(JpegDecodeAcceleratorTest, SamplingFormatChange) {
+TEST_F(MjpegDecodeAcceleratorTest, SamplingFormatChange) {
   const std::vector<ParsedJpegImage*> images = {
       g_env->image_data_640x368_black_.get(),
       g_env->image_data_640x368_422_black_.get()};
@@ -804,14 +809,14 @@
   TestDecode(images, expected_status);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, FailureJpeg) {
+TEST_F(MjpegDecodeAcceleratorTest, FailureJpeg) {
   const std::vector<ParsedJpegImage*> images = {
       g_env->image_data_invalid_.get()};
   const std::vector<ClientState> expected_status = {CS_ERROR};
   TestDecode(images, expected_status);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, KeepDecodeAfterFailure) {
+TEST_F(MjpegDecodeAcceleratorTest, KeepDecodeAfterFailure) {
   const std::vector<ParsedJpegImage*> images = {
       g_env->image_data_invalid_.get(),
       g_env->image_data_1280x720_default_.get()};
@@ -819,7 +824,7 @@
   TestDecode(images, expected_status);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, Abort) {
+TEST_F(MjpegDecodeAcceleratorTest, Abort) {
   constexpr size_t kNumOfJpegToDecode = 5;
   const std::vector<ParsedJpegImage*> images(
       kNumOfJpegToDecode, g_env->image_data_1280x720_default_.get());
@@ -830,7 +835,7 @@
   TestDecode(images, expected_status, 2 /* num_concurrent_decoders */);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, PerfJDA) {
+TEST_F(MjpegDecodeAcceleratorTest, PerfJDA) {
   // Only the first image will be used for perf testing.
   ASSERT_GE(g_env->image_data_user_.size(), 1u);
   const std::vector<ParsedJpegImage*> images = {
@@ -838,7 +843,7 @@
   PerfDecodeByJDA(g_env->perf_decode_times_, images);
 }
 
-TEST_F(JpegDecodeAcceleratorTest, PerfSW) {
+TEST_F(MjpegDecodeAcceleratorTest, PerfSW) {
   // Only the first image will be used for perf testing.
   ASSERT_GE(g_env->image_data_user_.size(), 1u);
   const std::vector<ParsedJpegImage*> images = {
@@ -847,7 +852,7 @@
 }
 
 }  // namespace
-}  // namespace media
+}  // namespace chromeos_camera
 
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
@@ -883,7 +888,7 @@
       continue;
     }
     if (it->first == "save_to_file") {
-      media::g_save_to_file = true;
+      chromeos_camera::g_save_to_file = true;
       continue;
     }
     if (it->first == "v" || it->first == "vmodule")
@@ -897,10 +902,11 @@
   media::VaapiWrapper::PreSandboxInitialization();
 #endif
 
-  media::g_env = reinterpret_cast<media::JpegDecodeAcceleratorTestEnvironment*>(
-      testing::AddGlobalTestEnvironment(
-          new media::JpegDecodeAcceleratorTestEnvironment(
-              jpeg_filenames, test_data_path, perf_decode_times)));
+  chromeos_camera::g_env =
+      reinterpret_cast<chromeos_camera::MjpegDecodeAcceleratorTestEnvironment*>(
+          testing::AddGlobalTestEnvironment(
+              new chromeos_camera::MjpegDecodeAcceleratorTestEnvironment(
+                  jpeg_filenames, test_data_path, perf_decode_times)));
 
   return RUN_ALL_TESTS();
 }
diff --git a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
similarity index 71%
rename from media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc
rename to components/chromeos_camera/mojo_jpeg_encode_accelerator_service.cc
index 9574313..1f42774e 100644
--- a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.cc
+++ b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.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 "media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h"
+#include "components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h"
 
 #include <linux/videodev2.h>
 #include <stdint.h>
@@ -19,12 +19,13 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "media/base/bind_to_current_loop.h"
+#include "media/base/video_frame.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/linux/native_pixmap_dmabuf.h"
 
-namespace media {
+namespace chromeos_camera {
 
 namespace {
 
@@ -32,7 +33,7 @@
 
 scoped_refptr<media::VideoFrame> ConstructVideoFrame(
     std::vector<chromeos_camera::mojom::DmaBufPlanePtr> dma_buf_planes,
-    VideoPixelFormat pixel_format,
+    media::VideoPixelFormat pixel_format,
     int32_t width,
     int32_t height) {
   size_t num_planes = media::VideoFrame::NumPlanes(pixel_format);
@@ -49,7 +50,7 @@
 
   std::vector<base::ScopedFD> dma_buf_fds(num_planes);
   std::vector<size_t> buffer_sizes(num_planes);
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
 
   for (size_t i = 0; i < num_planes; ++i) {
     dma_buf_fds[i] =
@@ -59,80 +60,81 @@
     planes[i].offset = dma_buf_planes[i]->offset;
     buffer_sizes[i] = dma_buf_planes[i]->size;
   }
-  auto layout = VideoFrameLayout::CreateWithPlanes(
+  auto layout = media::VideoFrameLayout::CreateWithPlanes(
       pixel_format, coded_size, std::move(planes), std::move(buffer_sizes));
 
-  return VideoFrame::WrapExternalDmabufs(*layout,       // layout
-                                         visible_rect,  // visible_rect
-                                         coded_size,    // natural_size
-                                         std::move(dma_buf_fds),  // dmabuf_fds
-                                         base::TimeDelta());      // timestamp
+  return media::VideoFrame::WrapExternalDmabufs(
+      *layout,                 // layout
+      visible_rect,            // visible_rect
+      coded_size,              // natural_size
+      std::move(dma_buf_fds),  // dmabuf_fds
+      base::TimeDelta());      // timestamp
 }
 
-VideoPixelFormat ToVideoPixelFormat(uint32_t fourcc_fmt) {
+media::VideoPixelFormat ToVideoPixelFormat(uint32_t fourcc_fmt) {
   switch (fourcc_fmt) {
     case V4L2_PIX_FMT_NV12:
     case V4L2_PIX_FMT_NV12M:
-      return PIXEL_FORMAT_NV12;
+      return media::PIXEL_FORMAT_NV12;
 
     case V4L2_PIX_FMT_YUV420:
     case V4L2_PIX_FMT_YUV420M:
-      return PIXEL_FORMAT_I420;
+      return media::PIXEL_FORMAT_I420;
 
     case V4L2_PIX_FMT_RGB32:
-      return PIXEL_FORMAT_ARGB;
+      return media::PIXEL_FORMAT_ARGB;
 
     default:
-      return PIXEL_FORMAT_UNKNOWN;
+      return media::PIXEL_FORMAT_UNKNOWN;
   }
 }
 
 }  // namespace
 
 // static
-void CrOSMojoJpegEncodeAcceleratorService::Create(
+void MojoJpegEncodeAcceleratorService::Create(
     chromeos_camera::mojom::JpegEncodeAcceleratorRequest request) {
-  auto* jpeg_encoder = new CrOSMojoJpegEncodeAcceleratorService();
+  auto* jpeg_encoder = new MojoJpegEncodeAcceleratorService();
   mojo::MakeStrongBinding(base::WrapUnique(jpeg_encoder), std::move(request));
 }
 
-CrOSMojoJpegEncodeAcceleratorService::CrOSMojoJpegEncodeAcceleratorService()
+MojoJpegEncodeAcceleratorService::MojoJpegEncodeAcceleratorService()
     : accelerator_factory_functions_(
           GpuJpegEncodeAcceleratorFactory::GetAcceleratorFactories()) {}
 
-CrOSMojoJpegEncodeAcceleratorService::~CrOSMojoJpegEncodeAcceleratorService() {
+MojoJpegEncodeAcceleratorService::~MojoJpegEncodeAcceleratorService() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
-void CrOSMojoJpegEncodeAcceleratorService::VideoFrameReady(
+void MojoJpegEncodeAcceleratorService::VideoFrameReady(
     int32_t bitstream_buffer_id,
     size_t encoded_picture_size) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  NotifyEncodeStatus(bitstream_buffer_id, encoded_picture_size,
-                     ::media::JpegEncodeAccelerator::Status::ENCODE_OK);
+  NotifyEncodeStatus(
+      bitstream_buffer_id, encoded_picture_size,
+      ::chromeos_camera::JpegEncodeAccelerator::Status::ENCODE_OK);
 }
 
-void CrOSMojoJpegEncodeAcceleratorService::NotifyError(
+void MojoJpegEncodeAcceleratorService::NotifyError(
     int32_t bitstream_buffer_id,
-    ::media::JpegEncodeAccelerator::Status error) {
+    ::chromeos_camera::JpegEncodeAccelerator::Status error) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NotifyEncodeStatus(bitstream_buffer_id, 0, error);
 }
 
-void CrOSMojoJpegEncodeAcceleratorService::Initialize(
-    InitializeCallback callback) {
+void MojoJpegEncodeAcceleratorService::Initialize(InitializeCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // When adding non-chromeos platforms, VideoCaptureGpuJpegEncoder::Initialize
   // needs to be updated.
 
-  std::unique_ptr<::media::JpegEncodeAccelerator> accelerator;
+  std::unique_ptr<::chromeos_camera::JpegEncodeAccelerator> accelerator;
   for (const auto& create_jea_function : accelerator_factory_functions_) {
-    std::unique_ptr<::media::JpegEncodeAccelerator> tmp_accelerator =
+    std::unique_ptr<::chromeos_camera::JpegEncodeAccelerator> tmp_accelerator =
         create_jea_function.Run(base::ThreadTaskRunnerHandle::Get());
     if (tmp_accelerator &&
         tmp_accelerator->Initialize(this) ==
-            ::media::JpegEncodeAccelerator::Status::ENCODE_OK) {
+            ::chromeos_camera::JpegEncodeAccelerator::Status::ENCODE_OK) {
       accelerator = std::move(tmp_accelerator);
       break;
     }
@@ -148,7 +150,7 @@
   std::move(callback).Run(true);
 }
 
-void CrOSMojoJpegEncodeAcceleratorService::EncodeWithFD(
+void MojoJpegEncodeAcceleratorService::EncodeWithFD(
     int32_t buffer_id,
     mojo::ScopedHandle input_handle,
     uint32_t input_buffer_size,
@@ -167,28 +169,32 @@
 
   if (coded_size_width <= 0 || coded_size_height <= 0) {
     std::move(callback).Run(
-        buffer_id, 0, ::media::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
+        buffer_id, 0,
+        ::chromeos_camera::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
     return;
   }
 
   result = mojo::UnwrapPlatformFile(std::move(input_handle), &input_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        buffer_id, 0,
+        ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
 
   result = mojo::UnwrapPlatformFile(std::move(exif_handle), &exif_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        buffer_id, 0,
+        ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
 
   result = mojo::UnwrapPlatformFile(std::move(output_handle), &output_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        buffer_id, 0,
+        ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
 
@@ -218,7 +224,7 @@
   auto wrapped_callback = base::BindOnce(
       [](int32_t buffer_id, EncodeWithFDCallback callback,
          uint32_t encoded_picture_size,
-         media::JpegEncodeAccelerator::Status error) {
+         ::chromeos_camera::JpegEncodeAccelerator::Status error) {
         std::move(callback).Run(buffer_id, encoded_picture_size, error);
       },
       buffer_id, std::move(callback));
@@ -229,25 +235,28 @@
     DLOG(ERROR) << "Could not map input shared memory for buffer id "
                 << buffer_id;
     NotifyEncodeStatus(
-        buffer_id, 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        buffer_id, 0,
+        ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
 
   uint8_t* input_shm_memory = static_cast<uint8_t*>(input_shm->memory());
-  scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalSharedMemory(
-      PIXEL_FORMAT_I420,      // format
-      coded_size,             // coded_size
-      gfx::Rect(coded_size),  // visible_rect
-      coded_size,             // natural_size
-      input_shm_memory,       // data
-      input_buffer_size,      // data_size
-      input_shm_handle,       // handle
-      0,                      // data_offset
-      base::TimeDelta());     // timestamp
+  scoped_refptr<media::VideoFrame> frame =
+      media::VideoFrame::WrapExternalSharedMemory(
+          media::PIXEL_FORMAT_I420,  // format
+          coded_size,                // coded_size
+          gfx::Rect(coded_size),     // visible_rect
+          coded_size,                // natural_size
+          input_shm_memory,          // data
+          input_buffer_size,         // data_size
+          input_shm_handle,          // handle
+          0,                         // data_offset
+          base::TimeDelta());        // timestamp
   if (!frame.get()) {
     LOG(ERROR) << "Could not create VideoFrame for buffer id " << buffer_id;
     NotifyEncodeStatus(
-        buffer_id, 0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        buffer_id, 0,
+        ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
   // Keep |input_shm| referenced until |frame| is destructed.
@@ -259,7 +268,7 @@
   accelerator_->Encode(frame, kJpegQuality, exif_buffer.get(), output_buffer);
 }
 
-void CrOSMojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
+void MojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
     int32_t buffer_id,
     uint32_t input_format,
     std::vector<chromeos_camera::mojom::DmaBufPlanePtr> input_planes,
@@ -272,7 +281,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (coded_size_width <= 0 || coded_size_height <= 0) {
     std::move(callback).Run(
-        0, ::media::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
+        0, ::chromeos_camera::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
     return;
   }
   if (encode_cb_map_.find(buffer_id) != encode_cb_map_.end()) {
@@ -284,7 +293,7 @@
   auto result = mojo::UnwrapPlatformFile(std::move(exif_handle), &exif_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        0, ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
 
@@ -293,15 +302,15 @@
       coded_size_width, coded_size_height);
   if (!input_video_frame) {
     std::move(callback).Run(
-        0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        0, ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
   auto output_video_frame =
-      ConstructVideoFrame(std::move(output_planes), PIXEL_FORMAT_MJPEG,
+      ConstructVideoFrame(std::move(output_planes), media::PIXEL_FORMAT_MJPEG,
                           coded_size_width, coded_size_height);
   if (!output_video_frame) {
     std::move(callback).Run(
-        0, ::media::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
+        0, ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
     return;
   }
   base::UnguessableToken exif_guid = base::UnguessableToken::Create();
@@ -319,10 +328,10 @@
                                  kJpegQuality, buffer_id, exif_buffer.get());
 }
 
-void CrOSMojoJpegEncodeAcceleratorService::NotifyEncodeStatus(
+void MojoJpegEncodeAcceleratorService::NotifyEncodeStatus(
     int32_t bitstream_buffer_id,
     size_t encoded_picture_size,
-    ::media::JpegEncodeAccelerator::Status error) {
+    ::chromeos_camera::JpegEncodeAccelerator::Status error) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   auto iter = encode_cb_map_.find(bitstream_buffer_id);
@@ -332,4 +341,4 @@
   std::move(encode_cb).Run(encoded_picture_size, error);
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h
similarity index 68%
rename from media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h
rename to components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h
index 4374eaa..147de0d8 100644
--- a/media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h
+++ b/components/chromeos_camera/mojo_jpeg_encode_accelerator_service.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 MEDIA_MOJO_SERVICES_CROS_MOJO_JPEG_ENCODE_ACCELERATOR_SERVICE_H_
-#define MEDIA_MOJO_SERVICES_CROS_MOJO_JPEG_ENCODE_ACCELERATOR_SERVICE_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_MOJO_JPEG_ENCODE_ACCELERATOR_SERVICE_H_
+#define COMPONENTS_CHROMEOS_CAMERA_MOJO_JPEG_ENCODE_ACCELERATOR_SERVICE_H_
 
 #include <stdint.h>
 
@@ -13,36 +13,36 @@
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "components/chromeos_camera/common/jpeg_encode_accelerator.mojom.h"
-#include "media/gpu/gpu_jpeg_encode_accelerator_factory.h"
-#include "media/mojo/services/media_mojo_export.h"
-#include "media/video/jpeg_encode_accelerator.h"
+#include "components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 
-namespace media {
+namespace chromeos_camera {
 
 // Implementation of a chromeos_camera::mojom::JpegEncodeAccelerator which runs
 // in the GPU process, and wraps a JpegEncodeAccelerator.
-class MEDIA_MOJO_EXPORT CrOSMojoJpegEncodeAcceleratorService
+class MojoJpegEncodeAcceleratorService
     : public chromeos_camera::mojom::JpegEncodeAccelerator,
       public JpegEncodeAccelerator::Client {
  public:
   static void Create(
       chromeos_camera::mojom::JpegEncodeAcceleratorRequest request);
 
-  ~CrOSMojoJpegEncodeAcceleratorService() override;
+  ~MojoJpegEncodeAcceleratorService() override;
 
   // JpegEncodeAccelerator::Client implementation.
   void VideoFrameReady(int32_t buffer_id, size_t encoded_picture_size) override;
-  void NotifyError(int32_t buffer_id,
-                   ::media::JpegEncodeAccelerator::Status status) override;
+  void NotifyError(
+      int32_t buffer_id,
+      ::chromeos_camera::JpegEncodeAccelerator::Status status) override;
 
  private:
   using EncodeCallbackMap =
       std::unordered_map<int32_t, EncodeWithDmaBufCallback>;
 
   // This constructor internally calls
-  // GpuJpegEncodeAcceleratorFactory::GetAcceleratorFactories() to
+  // media::GpuJpegEncodeAcceleratorFactory::GetAcceleratorFactories() to
   // fill |accelerator_factory_functions_|.
-  CrOSMojoJpegEncodeAcceleratorService();
+  MojoJpegEncodeAcceleratorService();
 
   // chromeos_camera::mojom::JpegEncodeAccelerator implementation.
   void Initialize(InitializeCallback callback) override;
@@ -70,9 +70,10 @@
       int32_t coded_size_height,
       EncodeWithDmaBufCallback callback) override;
 
-  void NotifyEncodeStatus(int32_t bitstream_buffer_id,
-                          size_t encoded_picture_size,
-                          ::media::JpegEncodeAccelerator::Status status);
+  void NotifyEncodeStatus(
+      int32_t bitstream_buffer_id,
+      size_t encoded_picture_size,
+      ::chromeos_camera::JpegEncodeAccelerator::Status status);
 
   const std::vector<GpuJpegEncodeAcceleratorFactory::CreateAcceleratorCB>
       accelerator_factory_functions_;
@@ -80,13 +81,13 @@
   // A map from bitstream_buffer_id to EncodeCallback.
   EncodeCallbackMap encode_cb_map_;
 
-  std::unique_ptr<::media::JpegEncodeAccelerator> accelerator_;
+  std::unique_ptr<::chromeos_camera::JpegEncodeAccelerator> accelerator_;
 
   THREAD_CHECKER(thread_checker_);
 
-  DISALLOW_COPY_AND_ASSIGN(CrOSMojoJpegEncodeAcceleratorService);
+  DISALLOW_COPY_AND_ASSIGN(MojoJpegEncodeAcceleratorService);
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_MOJO_SERVICES_CROS_MOJO_JPEG_ENCODE_ACCELERATOR_SERVICE_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_MOJO_JPEG_ENCODE_ACCELERATOR_SERVICE_H_
diff --git a/media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.cc b/components/chromeos_camera/mojo_mjpeg_decode_accelerator.cc
similarity index 68%
rename from media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.cc
rename to components/chromeos_camera/mojo_mjpeg_decode_accelerator.cc
index 558aa62..1435f4fd 100644
--- a/media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.cc
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator.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 "media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.h"
+#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator.h"
 
 #include <stddef.h>
 
@@ -14,42 +14,42 @@
 #include "build/build_config.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
-namespace media {
+namespace chromeos_camera {
 
-CrOSMojoMjpegDecodeAccelerator::CrOSMojoMjpegDecodeAccelerator(
+MojoMjpegDecodeAccelerator::MojoMjpegDecodeAccelerator(
     scoped_refptr<base::SequencedTaskRunner> io_task_runner,
     chromeos_camera::mojom::MjpegDecodeAcceleratorPtrInfo jpeg_decoder)
     : io_task_runner_(std::move(io_task_runner)),
       jpeg_decoder_info_(std::move(jpeg_decoder)) {}
 
-CrOSMojoMjpegDecodeAccelerator::~CrOSMojoMjpegDecodeAccelerator() {
+MojoMjpegDecodeAccelerator::~MojoMjpegDecodeAccelerator() {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
 }
 
-bool CrOSMojoMjpegDecodeAccelerator::Initialize(
+bool MojoMjpegDecodeAccelerator::Initialize(
     MjpegDecodeAccelerator::Client* /*client*/) {
   NOTIMPLEMENTED();
   return false;
 }
 
-void CrOSMojoMjpegDecodeAccelerator::InitializeAsync(Client* client,
-                                                     InitCB init_cb) {
+void MojoMjpegDecodeAccelerator::InitializeAsync(Client* client,
+                                                 InitCB init_cb) {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
 
   jpeg_decoder_.Bind(std::move(jpeg_decoder_info_));
 
   // base::Unretained is safe because |this| owns |jpeg_decoder_|.
   jpeg_decoder_.set_connection_error_handler(
-      base::Bind(&CrOSMojoMjpegDecodeAccelerator::OnLostConnectionToJpegDecoder,
+      base::Bind(&MojoMjpegDecodeAccelerator::OnLostConnectionToJpegDecoder,
                  base::Unretained(this)));
   jpeg_decoder_->Initialize(
-      base::Bind(&CrOSMojoMjpegDecodeAccelerator::OnInitializeDone,
+      base::Bind(&MojoMjpegDecodeAccelerator::OnInitializeDone,
                  base::Unretained(this), std::move(init_cb), client));
 }
 
-void CrOSMojoMjpegDecodeAccelerator::Decode(
-    const BitstreamBuffer& bitstream_buffer,
-    const scoped_refptr<VideoFrame>& video_frame) {
+void MojoMjpegDecodeAccelerator::Decode(
+    const media::BitstreamBuffer& bitstream_buffer,
+    const scoped_refptr<media::VideoFrame>& video_frame) {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(jpeg_decoder_.is_bound());
 
@@ -63,7 +63,7 @@
     return;
   }
 
-  size_t output_buffer_size = VideoFrame::AllocationSize(
+  size_t output_buffer_size = media::VideoFrame::AllocationSize(
       video_frame->format(), video_frame->coded_size());
   mojo::ScopedSharedBufferHandle output_frame_handle =
       mojo::WrapSharedMemoryHandle(
@@ -74,15 +74,15 @@
   jpeg_decoder_->Decode(bitstream_buffer, video_frame->coded_size(),
                         std::move(output_frame_handle),
                         base::checked_cast<uint32_t>(output_buffer_size),
-                        base::Bind(&CrOSMojoMjpegDecodeAccelerator::OnDecodeAck,
+                        base::Bind(&MojoMjpegDecodeAccelerator::OnDecodeAck,
                                    base::Unretained(this)));
 }
 
-bool CrOSMojoMjpegDecodeAccelerator::IsSupported() {
+bool MojoMjpegDecodeAccelerator::IsSupported() {
   return true;
 }
 
-void CrOSMojoMjpegDecodeAccelerator::OnInitializeDone(
+void MojoMjpegDecodeAccelerator::OnInitializeDone(
     InitCB init_cb,
     MjpegDecodeAccelerator::Client* client,
     bool success) {
@@ -94,15 +94,15 @@
   std::move(init_cb).Run(success);
 }
 
-void CrOSMojoMjpegDecodeAccelerator::OnDecodeAck(
+void MojoMjpegDecodeAccelerator::OnDecodeAck(
     int32_t bitstream_buffer_id,
-    ::media::MjpegDecodeAccelerator::Error error) {
+    ::chromeos_camera::MjpegDecodeAccelerator::Error error) {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
 
   if (!client_)
     return;
 
-  if (error == ::media::MjpegDecodeAccelerator::Error::NO_ERRORS) {
+  if (error == ::chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS) {
     client_->VideoFrameReady(bitstream_buffer_id);
     return;
   }
@@ -115,10 +115,11 @@
   client->NotifyError(bitstream_buffer_id, error);
 }
 
-void CrOSMojoMjpegDecodeAccelerator::OnLostConnectionToJpegDecoder() {
+void MojoMjpegDecodeAccelerator::OnLostConnectionToJpegDecoder() {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-  OnDecodeAck(kInvalidBitstreamBufferId,
-              ::media::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
+  OnDecodeAck(
+      kInvalidBitstreamBufferId,
+      ::chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.h b/components/chromeos_camera/mojo_mjpeg_decode_accelerator.h
similarity index 68%
rename from media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.h
rename to components/chromeos_camera/mojo_mjpeg_decode_accelerator.h
index 3edfe68..3f524c0 100644
--- a/media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.h
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator.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 MEDIA_MOJO_CLIENTS_CROS_MOJO_MJPEG_DECODE_ACCELERATOR_H_
-#define MEDIA_MOJO_CLIENTS_CROS_MOJO_MJPEG_DECODE_ACCELERATOR_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_MOJO_MJPEG_DECODE_ACCELERATOR_H_
+#define COMPONENTS_CHROMEOS_CAMERA_MOJO_MJPEG_DECODE_ACCELERATOR_H_
 
 #include <stdint.h>
 
@@ -11,31 +11,31 @@
 
 #include "base/macros.h"
 #include "components/chromeos_camera/common/mjpeg_decode_accelerator.mojom.h"
-#include "media/video/mjpeg_decode_accelerator.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 
 namespace base {
 class SequencedTaskRunner;
 }
 
-namespace media {
+namespace chromeos_camera {
 
 // A MjpegDecodeAccelerator, for use in the browser process, that proxies to a
 // chromeos_camera::mojom::MjpegDecodeAccelerator. Created on the owner's
 // thread, otherwise operating and deleted on |io_task_runner|.
-class CrOSMojoMjpegDecodeAccelerator : public MjpegDecodeAccelerator {
+class MojoMjpegDecodeAccelerator : public MjpegDecodeAccelerator {
  public:
-  CrOSMojoMjpegDecodeAccelerator(
+  MojoMjpegDecodeAccelerator(
       scoped_refptr<base::SequencedTaskRunner> io_task_runner,
       chromeos_camera::mojom::MjpegDecodeAcceleratorPtrInfo jpeg_decoder);
-  ~CrOSMojoMjpegDecodeAccelerator() override;
+  ~MojoMjpegDecodeAccelerator() override;
 
   // MjpegDecodeAccelerator implementation.
   // |client| is called on the IO thread, but is never called into after the
-  // CrOSMojoMjpegDecodeAccelerator is destroyed.
+  // MojoMjpegDecodeAccelerator is destroyed.
   bool Initialize(Client* client) override;
   void InitializeAsync(Client* client, InitCB init_cb) override;
-  void Decode(const BitstreamBuffer& bitstream_buffer,
-              const scoped_refptr<VideoFrame>& video_frame) override;
+  void Decode(const media::BitstreamBuffer& bitstream_buffer,
+              const scoped_refptr<media::VideoFrame>& video_frame) override;
   bool IsSupported() override;
 
  private:
@@ -43,7 +43,7 @@
                         MjpegDecodeAccelerator::Client* client,
                         bool success);
   void OnDecodeAck(int32_t bitstream_buffer_id,
-                   ::media::MjpegDecodeAccelerator::Error error);
+                   MjpegDecodeAccelerator::Error error);
   void OnLostConnectionToJpegDecoder();
 
   scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
@@ -58,9 +58,9 @@
 
   chromeos_camera::mojom::MjpegDecodeAcceleratorPtr jpeg_decoder_;
 
-  DISALLOW_COPY_AND_ASSIGN(CrOSMojoMjpegDecodeAccelerator);
+  DISALLOW_COPY_AND_ASSIGN(MojoMjpegDecodeAccelerator);
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_MOJO_CLIENTS_CROS_MOJO_MJPEG_DECODE_ACCELERATOR_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_MOJO_MJPEG_DECODE_ACCELERATOR_H_
diff --git a/media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.cc b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
similarity index 73%
rename from media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.cc
rename to components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.cc
index 650bec1..47a3f968 100644
--- a/media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.cc
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.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 "media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.h"
+#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.h"
 
 #include <stdint.h>
 
@@ -56,48 +56,48 @@
 
 }  // namespace
 
-namespace media {
+namespace chromeos_camera {
 
 // static
-void CrOSMojoMjpegDecodeAcceleratorService::Create(
+void MojoMjpegDecodeAcceleratorService::Create(
     chromeos_camera::mojom::MjpegDecodeAcceleratorRequest request) {
-  auto* jpeg_decoder = new CrOSMojoMjpegDecodeAcceleratorService();
+  auto* jpeg_decoder = new MojoMjpegDecodeAcceleratorService();
   mojo::MakeStrongBinding(base::WrapUnique(jpeg_decoder), std::move(request));
 }
 
-CrOSMojoMjpegDecodeAcceleratorService::CrOSMojoMjpegDecodeAcceleratorService()
+MojoMjpegDecodeAcceleratorService::MojoMjpegDecodeAcceleratorService()
     : accelerator_factory_functions_(
           GpuMjpegDecodeAcceleratorFactory::GetAcceleratorFactories()) {}
 
-CrOSMojoMjpegDecodeAcceleratorService::
-    ~CrOSMojoMjpegDecodeAcceleratorService() {
+MojoMjpegDecodeAcceleratorService::~MojoMjpegDecodeAcceleratorService() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::VideoFrameReady(
+void MojoMjpegDecodeAcceleratorService::VideoFrameReady(
     int32_t bitstream_buffer_id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  NotifyDecodeStatus(bitstream_buffer_id,
-                     ::media::MjpegDecodeAccelerator::Error::NO_ERRORS);
+  NotifyDecodeStatus(
+      bitstream_buffer_id,
+      ::chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS);
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::NotifyError(
+void MojoMjpegDecodeAcceleratorService::NotifyError(
     int32_t bitstream_buffer_id,
-    ::media::MjpegDecodeAccelerator::Error error) {
+    ::chromeos_camera::MjpegDecodeAccelerator::Error error) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   NotifyDecodeStatus(bitstream_buffer_id, error);
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::Initialize(
+void MojoMjpegDecodeAcceleratorService::Initialize(
     InitializeCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // When adding non-chromeos platforms, VideoCaptureGpuJpegDecoder::Initialize
   // needs to be updated.
 
-  std::unique_ptr<::media::MjpegDecodeAccelerator> accelerator;
+  std::unique_ptr<::chromeos_camera::MjpegDecodeAccelerator> accelerator;
   for (const auto& create_jda_function : accelerator_factory_functions_) {
-    std::unique_ptr<::media::MjpegDecodeAccelerator> tmp_accelerator =
+    std::unique_ptr<::chromeos_camera::MjpegDecodeAccelerator> tmp_accelerator =
         create_jda_function.Run(base::ThreadTaskRunnerHandle::Get());
     if (tmp_accelerator && tmp_accelerator->Initialize(this)) {
       accelerator = std::move(tmp_accelerator);
@@ -115,14 +115,14 @@
   std::move(callback).Run(true);
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::Decode(
-    const BitstreamBuffer& input_buffer,
+void MojoMjpegDecodeAcceleratorService::Decode(
+    const media::BitstreamBuffer& input_buffer,
     const gfx::Size& coded_size,
     mojo::ScopedSharedBufferHandle output_handle,
     uint32_t output_buffer_size,
     DecodeCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  TRACE_EVENT0("jpeg", "CrOSMojoMjpegDecodeAcceleratorService::Decode");
+  TRACE_EVENT0("jpeg", "MojoMjpegDecodeAcceleratorService::Decode");
 
   DCHECK_EQ(decode_cb_map_.count(input_buffer.id()), 0u);
   decode_cb_map_[input_buffer.id()] = std::move(callback);
@@ -130,7 +130,7 @@
   if (!VerifyDecodeParams(coded_size, &output_handle, output_buffer_size)) {
     NotifyDecodeStatus(
         input_buffer.id(),
-        ::media::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
+        ::chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
     return;
   }
 
@@ -146,27 +146,28 @@
                << input_buffer.id();
     NotifyDecodeStatus(
         input_buffer.id(),
-        ::media::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
+        ::chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
     return;
   }
 
   uint8_t* shm_memory = static_cast<uint8_t*>(output_shm->memory());
-  scoped_refptr<VideoFrame> frame = VideoFrame::WrapExternalSharedMemory(
-      PIXEL_FORMAT_I420,      // format
-      coded_size,             // coded_size
-      gfx::Rect(coded_size),  // visible_rect
-      coded_size,             // natural_size
-      shm_memory,             // data
-      output_buffer_size,     // data_size
-      memory_handle,          // handle
-      0,                      // data_offset
-      base::TimeDelta());     // timestamp
+  scoped_refptr<media::VideoFrame> frame =
+      media::VideoFrame::WrapExternalSharedMemory(
+          media::PIXEL_FORMAT_I420,  // format
+          coded_size,                // coded_size
+          gfx::Rect(coded_size),     // visible_rect
+          coded_size,                // natural_size
+          shm_memory,                // data
+          output_buffer_size,        // data_size
+          memory_handle,             // handle
+          0,                         // data_offset
+          base::TimeDelta());        // timestamp
   if (!frame.get()) {
     LOG(ERROR) << "Could not create VideoFrame for input buffer id "
                << input_buffer.id();
     NotifyDecodeStatus(
         input_buffer.id(),
-        ::media::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
+        ::chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
     return;
   }
   frame->AddDestructionObserver(
@@ -176,7 +177,7 @@
   accelerator_->Decode(input_buffer, frame);
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::DecodeWithFD(
+void MojoMjpegDecodeAcceleratorService::DecodeWithFD(
     int32_t buffer_id,
     mojo::ScopedHandle input_handle,
     uint32_t input_buffer_size,
@@ -194,14 +195,16 @@
   result = mojo::UnwrapPlatformFile(std::move(input_handle), &input_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, ::media::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
+        buffer_id,
+        ::chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
     return;
   }
 
   result = mojo::UnwrapPlatformFile(std::move(output_handle), &output_fd);
   if (result != MOJO_RESULT_OK) {
     std::move(callback).Run(
-        buffer_id, ::media::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
+        buffer_id,
+        ::chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE);
     return;
   }
 
@@ -227,14 +230,14 @@
 #endif
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::Uninitialize() {
+void MojoMjpegDecodeAcceleratorService::Uninitialize() {
   // TODO(c.padhi): see http://crbug.com/699255.
   NOTIMPLEMENTED();
 }
 
-void CrOSMojoMjpegDecodeAcceleratorService::NotifyDecodeStatus(
+void MojoMjpegDecodeAcceleratorService::NotifyDecodeStatus(
     int32_t bitstream_buffer_id,
-    ::media::MjpegDecodeAccelerator::Error error) {
+    ::chromeos_camera::MjpegDecodeAccelerator::Error error) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   auto iter = decode_cb_map_.find(bitstream_buffer_id);
@@ -244,4 +247,4 @@
   std::move(decode_cb).Run(bitstream_buffer_id, error);
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.h b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.h
similarity index 67%
rename from media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.h
rename to components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.h
index aa17435..b9e5666 100644
--- a/media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.h
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.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 MEDIA_MOJO_SERVICES_CROS_MOJO_MJPEG_DECODE_ACCELERATOR_SERVICE_H_
-#define MEDIA_MOJO_SERVICES_CROS_MOJO_MJPEG_DECODE_ACCELERATOR_SERVICE_H_
+#ifndef COMPONENTS_CHROMEOS_CAMERA_MOJO_MJPEG_DECODE_ACCELERATOR_SERVICE_H_
+#define COMPONENTS_CHROMEOS_CAMERA_MOJO_MJPEG_DECODE_ACCELERATOR_SERVICE_H_
 
 #include <stdint.h>
 
@@ -13,27 +13,27 @@
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "components/chromeos_camera/common/mjpeg_decode_accelerator.mojom.h"
-#include "media/gpu/gpu_mjpeg_decode_accelerator_factory.h"
-#include "media/mojo/services/media_mojo_export.h"
-#include "media/video/mjpeg_decode_accelerator.h"
+#include "components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 
-namespace media {
+namespace chromeos_camera {
 
 // Implementation of a chromeos_camera::mojom::MjpegDecodeAccelerator which runs
 // in the GPU process, and wraps a JpegDecodeAccelerator.
-class MEDIA_MOJO_EXPORT CrOSMojoMjpegDecodeAcceleratorService
+class MojoMjpegDecodeAcceleratorService
     : public chromeos_camera::mojom::MjpegDecodeAccelerator,
       public MjpegDecodeAccelerator::Client {
  public:
   static void Create(
       chromeos_camera::mojom::MjpegDecodeAcceleratorRequest request);
 
-  ~CrOSMojoMjpegDecodeAcceleratorService() override;
+  ~MojoMjpegDecodeAcceleratorService() override;
 
   // MjpegDecodeAccelerator::Client implementation.
   void VideoFrameReady(int32_t buffer_id) override;
-  void NotifyError(int32_t buffer_id,
-                   ::media::MjpegDecodeAccelerator::Error error) override;
+  void NotifyError(
+      int32_t buffer_id,
+      ::chromeos_camera::MjpegDecodeAccelerator::Error error) override;
 
  private:
   using DecodeCallbackMap = std::unordered_map<int32_t, DecodeCallback>;
@@ -41,11 +41,11 @@
   // This constructor internally calls
   // GpuMjpegDecodeAcceleratorFactory::GetAcceleratorFactories() to
   // fill |accelerator_factory_functions_|.
-  CrOSMojoMjpegDecodeAcceleratorService();
+  MojoMjpegDecodeAcceleratorService();
 
   // chromeos_camera::mojom::MjpegDecodeAccelerator implementation.
   void Initialize(InitializeCallback callback) override;
-  void Decode(const BitstreamBuffer& input_buffer,
+  void Decode(const media::BitstreamBuffer& input_buffer,
               const gfx::Size& coded_size,
               mojo::ScopedSharedBufferHandle output_handle,
               uint32_t output_buffer_size,
@@ -60,8 +60,9 @@
                     DecodeWithFDCallback callback) override;
   void Uninitialize() override;
 
-  void NotifyDecodeStatus(int32_t bitstream_buffer_id,
-                          ::media::MjpegDecodeAccelerator::Error error);
+  void NotifyDecodeStatus(
+      int32_t bitstream_buffer_id,
+      ::chromeos_camera::MjpegDecodeAccelerator::Error error);
 
   const std::vector<GpuMjpegDecodeAcceleratorFactory::CreateAcceleratorCB>
       accelerator_factory_functions_;
@@ -69,13 +70,13 @@
   // A map from bitstream_buffer_id to DecodeCallback.
   DecodeCallbackMap decode_cb_map_;
 
-  std::unique_ptr<::media::MjpegDecodeAccelerator> accelerator_;
+  std::unique_ptr<::chromeos_camera::MjpegDecodeAccelerator> accelerator_;
 
   THREAD_CHECKER(thread_checker_);
 
-  DISALLOW_COPY_AND_ASSIGN(CrOSMojoMjpegDecodeAcceleratorService);
+  DISALLOW_COPY_AND_ASSIGN(MojoMjpegDecodeAcceleratorService);
 };
 
-}  // namespace media
+}  // namespace chromeos_camera
 
-#endif  // MEDIA_MOJO_SERVICES_CROS_MOJO_MJPEG_DECODE_ACCELERATOR_SERVICE_H_
+#endif  // COMPONENTS_CHROMEOS_CAMERA_MOJO_MJPEG_DECODE_ACCELERATOR_SERVICE_H_
diff --git a/media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service_unittest.cc b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
similarity index 73%
rename from media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service_unittest.cc
rename to components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
index 0486553..6340ad7 100644
--- a/media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service_unittest.cc
+++ b/components/chromeos_camera/mojo_mjpeg_decode_accelerator_service_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.h"
+#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.h"
 
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -13,17 +13,17 @@
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace media {
+namespace chromeos_camera {
 
 static const int32_t kArbitraryBitstreamBufferId = 123;
 
 // Test fixture for the unit that is created via the mojom interface for
-// class CrOSMojoMjpegDecodeAcceleratorService. Uses a FakeJpegDecodeAccelerator
+// class MojoMjpegDecodeAcceleratorService. Uses a FakeJpegDecodeAccelerator
 // to simulate the actual decoding without the need for special hardware.
-class CrOSMojoMjpegDecodeAcceleratorServiceTest : public ::testing::Test {
+class MojoMjpegDecodeAcceleratorServiceTest : public ::testing::Test {
  public:
-  CrOSMojoMjpegDecodeAcceleratorServiceTest() = default;
-  ~CrOSMojoMjpegDecodeAcceleratorServiceTest() override = default;
+  MojoMjpegDecodeAcceleratorServiceTest() = default;
+  ~MojoMjpegDecodeAcceleratorServiceTest() override = default;
 
   void SetUp() override {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -48,14 +48,13 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
-TEST_F(CrOSMojoMjpegDecodeAcceleratorServiceTest, InitializeAndDecode) {
+TEST_F(MojoMjpegDecodeAcceleratorServiceTest, InitializeAndDecode) {
   chromeos_camera::mojom::MjpegDecodeAcceleratorPtr jpeg_decoder;
-  CrOSMojoMjpegDecodeAcceleratorService::Create(
-      mojo::MakeRequest(&jpeg_decoder));
+  MojoMjpegDecodeAcceleratorService::Create(mojo::MakeRequest(&jpeg_decoder));
 
   base::RunLoop run_loop;
   jpeg_decoder->Initialize(
-      base::Bind(&CrOSMojoMjpegDecodeAcceleratorServiceTest::OnInitializeDone,
+      base::Bind(&MojoMjpegDecodeAcceleratorServiceTest::OnInitializeDone,
                  base::Unretained(this), run_loop.QuitClosure()));
   run_loop.Run();
 
@@ -64,9 +63,9 @@
   const gfx::Size kDummyFrameCodedSize(10, 10);
   const char kKeyId[] = "key id";
   const char kIv[] = "0123456789abcdef";
-  std::vector<SubsampleEntry> subsamples;
-  subsamples.push_back(SubsampleEntry(10, 5));
-  subsamples.push_back(SubsampleEntry(15, 7));
+  std::vector<media::SubsampleEntry> subsamples;
+  subsamples.push_back(media::SubsampleEntry(10, 5));
+  subsamples.push_back(media::SubsampleEntry(15, 7));
 
   base::RunLoop run_loop2;
   base::SharedMemory shm;
@@ -75,7 +74,7 @@
   mojo::ScopedSharedBufferHandle output_frame_handle =
       mojo::SharedBufferHandle::Create(kOutputFrameSizeInBytes);
 
-  BitstreamBuffer bitstream_buffer(
+  media::BitstreamBuffer bitstream_buffer(
       kArbitraryBitstreamBufferId,
       base::SharedMemory::DuplicateHandle(shm.handle()),
       kInputBufferSizeInBytes);
@@ -84,9 +83,9 @@
   jpeg_decoder->Decode(
       bitstream_buffer, kDummyFrameCodedSize, std::move(output_frame_handle),
       base::checked_cast<uint32_t>(kOutputFrameSizeInBytes),
-      base::Bind(&CrOSMojoMjpegDecodeAcceleratorServiceTest::OnDecodeAck,
+      base::Bind(&MojoMjpegDecodeAcceleratorServiceTest::OnDecodeAck,
                  base::Unretained(this), run_loop2.QuitClosure()));
   run_loop2.Run();
 }
 
-}  // namespace media
+}  // namespace chromeos_camera
diff --git a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java
index f4ce6a6d..4202c3d 100644
--- a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java
+++ b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java
@@ -146,8 +146,8 @@
         }
 
         sInstance.nativeOnMessageReceived(sInstance.mNativeGCMDriverAndroid, message.getAppId(),
-                message.getSenderId(), message.getCollapseKey(), message.getRawData(),
-                message.getDataKeysAndValuesArray());
+                message.getSenderId(), message.getMessageId(), message.getCollapseKey(),
+                message.getRawData(), message.getDataKeysAndValuesArray());
     }
 
     @VisibleForTesting
@@ -162,5 +162,6 @@
     private native void nativeOnUnregisterFinished(long nativeGCMDriverAndroid, String appId,
             boolean success);
     private native void nativeOnMessageReceived(long nativeGCMDriverAndroid, String appId,
-            String senderId, String collapseKey, byte[] rawData, String[] dataKeysAndValues);
+            String senderId, String messageId, String collapseKey, byte[] rawData,
+            String[] dataKeysAndValues);
 }
diff --git a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java
index 80fc264c..2a707db 100644
--- a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java
+++ b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java
@@ -42,11 +42,15 @@
     private static final String KEY_RAW_DATA = "rawData";
     private static final String KEY_SENDER_ID = "senderId";
     private static final String KEY_ORIGINAL_PRIORITY = "originalPriority";
+    private static final String KEY_MESSAGE_ID = "messageId";
 
     private final String mSenderId;
     private final String mAppId;
 
     @Nullable
+    private final String mMessageId;
+
+    @Nullable
     private final String mCollapseKey;
     @Nullable
     private final byte[] mRawData;
@@ -84,6 +88,7 @@
         String bundleSenderId = "from";
         String bundleSubtype = "subtype";
         String bundleOriginalPriority = "google.original_priority";
+        String bundleMessageId = "google.message_id";
 
         if (!extras.containsKey(bundleSubtype)) {
             throw new IllegalArgumentException("Received push message with no subtype");
@@ -95,12 +100,14 @@
         mCollapseKey = extras.getString(bundleCollapseKey); // May be null.
         mRawData = extras.getByteArray(bundleRawData); // May be null.
         mOriginalPriority = extras.getString(bundleOriginalPriority); // May be null.
+        mMessageId = extras.getString(bundleMessageId); // May be null.
 
         List<String> dataKeysAndValues = new ArrayList<String>();
         for (String key : extras.keySet()) {
             if (key.equals(bundleSubtype) || key.equals(bundleSenderId)
                     || key.equals(bundleCollapseKey) || key.equals(bundleRawData)
-                    || key.equals(bundleOriginalPriority) || key.startsWith(bundleGcmplex)) {
+                    || key.equals(bundleOriginalPriority) || key.startsWith(bundleGcmplex)
+                    || key.equals(bundleMessageId)) {
                 continue;
             }
 
@@ -155,7 +162,8 @@
     private static <T> boolean validate(T in, Reader<T> reader) {
         return reader.hasKey(in, KEY_APP_ID) && reader.hasKey(in, KEY_COLLAPSE_KEY)
                 && reader.hasKey(in, KEY_DATA) && reader.hasKey(in, KEY_RAW_DATA)
-                && reader.hasKey(in, KEY_SENDER_ID) && reader.hasKey(in, KEY_ORIGINAL_PRIORITY);
+                && reader.hasKey(in, KEY_SENDER_ID) && reader.hasKey(in, KEY_ORIGINAL_PRIORITY)
+                && reader.hasKey(in, KEY_MESSAGE_ID);
     }
 
     private <T> GCMMessage(T source, Reader<T> reader) {
@@ -163,6 +171,7 @@
         mAppId = reader.readString(source, KEY_APP_ID);
         mCollapseKey = reader.readString(source, KEY_COLLAPSE_KEY);
         mOriginalPriority = reader.readString(source, KEY_ORIGINAL_PRIORITY);
+        mMessageId = reader.readString(source, KEY_MESSAGE_ID);
         // The rawData field needs to distinguish between {not set, set but empty, set with data}.
         String rawDataString = reader.readString(source, KEY_RAW_DATA);
         if (rawDataString != null) {
@@ -187,6 +196,11 @@
     }
 
     @Nullable
+    public String getMessageId() {
+        return mMessageId;
+    }
+
+    @Nullable
     public String getCollapseKey() {
         return mCollapseKey;
     }
@@ -268,6 +282,7 @@
         writer.writeString(out, KEY_APP_ID, mAppId);
         writer.writeString(out, KEY_COLLAPSE_KEY, mCollapseKey);
         writer.writeString(out, KEY_ORIGINAL_PRIORITY, mOriginalPriority);
+        writer.writeString(out, KEY_MESSAGE_ID, mMessageId);
 
         // The rawData field needs to distinguish between {not set, set but empty, set with data}.
         if (mRawData != null) {
diff --git a/components/gcm_driver/common/gcm_message.h b/components/gcm_driver/common/gcm_message.h
index 6593db3..8172e4c 100644
--- a/components/gcm_driver/common/gcm_message.h
+++ b/components/gcm_driver/common/gcm_message.h
@@ -39,6 +39,7 @@
   MessageData data;
   std::string collapse_key;
   std::string sender_id;
+  std::string message_id;
   std::string raw_data;
 
   // Whether the contents of the message have been decrypted, and are
diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc
index a3e6564..f458032 100644
--- a/components/gcm_driver/gcm_client_impl.cc
+++ b/components/gcm_driver/gcm_client_impl.cc
@@ -101,7 +101,6 @@
 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
 const char kMessageTypeKey[] = "message_type";
 const char kMessageTypeSendErrorKey[] = "send_error";
-const char kSendErrorMessageIdKey[] = "google.message_id";
 const char kSubtypeKey[] = "subtype";
 const char kSendMessageFromValue[] = "gcm@chrome.com";
 const int64_t kDefaultUserSerialNumber = 0LL;
@@ -1356,8 +1355,7 @@
   DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
 
   // Copy all the data from the stanza to a MessageData object. When present,
-  // keys like kSubtypeKey, kMessageTypeKey or kSendErrorMessageIdKey will be
-  // filtered out later.
+  // keys like kSubtypeKey or kMessageTypeKey will be filtered out later.
   MessageData message_data;
   for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
     std::string key = data_message_stanza.app_data(i).key();
@@ -1435,6 +1433,7 @@
 
   IncomingMessage incoming_message;
   incoming_message.sender_id = data_message_stanza.from();
+  incoming_message.message_id = data_message_stanza.persistent_id();
   if (data_message_stanza.has_token())
     incoming_message.collapse_key = data_message_stanza.token();
   incoming_message.data = message_data;
@@ -1468,12 +1467,7 @@
   SendErrorDetails send_error_details;
   send_error_details.additional_data = message_data;
   send_error_details.result = SERVER_ERROR;
-
-  auto iter = send_error_details.additional_data.find(kSendErrorMessageIdKey);
-  if (iter != send_error_details.additional_data.end()) {
-    send_error_details.message_id = iter->second;
-    send_error_details.additional_data.erase(iter);
-  }
+  send_error_details.message_id = data_message_stanza.persistent_id();
 
   recorder_.RecordIncomingSendError(app_id, data_message_stanza.to(),
                                     data_message_stanza.id());
diff --git a/components/gcm_driver/gcm_client_impl_unittest.cc b/components/gcm_driver/gcm_client_impl_unittest.cc
index 45ab3b9..0d8beaa 100644
--- a/components/gcm_driver/gcm_client_impl_unittest.cc
+++ b/components/gcm_driver/gcm_client_impl_unittest.cc
@@ -84,6 +84,7 @@
 const int kTestTokenInvalidationPeriod = 5;
 const char kGroupName[] = "Enabled";
 const char kInvalidateTokenTrialName[] = "InvalidateTokenTrial";
+const char kMessageId[] = "0:12345%5678";
 
 const char kRegisterUrl[] = "https://android.clients.google.com/c2dm/register3";
 
@@ -108,6 +109,7 @@
     app_data->set_value(subtype);
   }
   data_message.set_raw_data(raw_data);
+  data_message.set_persistent_id(kMessageId);
   return MCSMessage(kDataMessageStanzaTag, data_message);
 }
 
@@ -1115,10 +1117,9 @@
 }
 
 TEST_F(GCMClientImplTest, DispatchDownstreamMessageSendError) {
-  std::map<std::string, std::string> expected_data;
-  expected_data["message_type"] = "send_error";
-  expected_data["google.message_id"] = "007";
-  expected_data["error_details"] = "some details";
+  std::map<std::string, std::string> expected_data = {
+      {"message_type", "send_error"}, {"error_details", "some details"}};
+
   MCSMessage message(BuildDownstreamMessage(
       kSender, kExtensionAppId, std::string() /* subtype */, expected_data,
       std::string() /* raw_data */));
@@ -1127,7 +1128,7 @@
 
   EXPECT_EQ(MESSAGE_SEND_ERROR, last_event());
   EXPECT_EQ(kExtensionAppId, last_app_id());
-  EXPECT_EQ("007", last_error_details().message_id);
+  EXPECT_EQ(kMessageId, last_error_details().message_id);
   EXPECT_EQ(1UL, last_error_details().additional_data.size());
   auto iter = last_error_details().additional_data.find("error_details");
   EXPECT_TRUE(iter != last_error_details().additional_data.end());
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc
index c24f5f27..7fc1e6f 100644
--- a/components/gcm_driver/gcm_driver_android.cc
+++ b/components/gcm_driver/gcm_driver_android.cc
@@ -73,6 +73,7 @@
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& j_app_id,
     const JavaParamRef<jstring>& j_sender_id,
+    const JavaParamRef<jstring>& j_message_id,
     const JavaParamRef<jstring>& j_collapse_key,
     const JavaParamRef<jbyteArray>& j_raw_data,
     const JavaParamRef<jobjectArray>& j_data_keys_and_values) {
@@ -82,6 +83,9 @@
 
   IncomingMessage message;
   message.sender_id = ConvertJavaStringToUTF8(env, j_sender_id);
+
+  if (!j_message_id.is_null())
+    ConvertJavaStringToUTF8(env, j_collapse_key, &message.message_id);
   if (!j_collapse_key.is_null())
     ConvertJavaStringToUTF8(env, j_collapse_key, &message.collapse_key);
 
diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h
index 1f46ba85..cbb137a 100644
--- a/components/gcm_driver/gcm_driver_android.h
+++ b/components/gcm_driver/gcm_driver_android.h
@@ -47,6 +47,7 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& app_id,
       const base::android::JavaParamRef<jstring>& sender_id,
+      const base::android::JavaParamRef<jstring>& j_message_id,
       const base::android::JavaParamRef<jstring>& collapse_key,
       const base::android::JavaParamRef<jbyteArray>& raw_data,
       const base::android::JavaParamRef<jobjectArray>& data_keys_and_values);
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index a677a974..4485cf9 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -298,14 +298,13 @@
 
   if (ShouldShowManualFallbackForPreLollipop(
           autofill_client_->GetSyncService())) {
-      autofill::Suggestion suggestion(
-          l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS),
-          std::string(), std::string(),
-          autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
-      suggestions.push_back(suggestion);
+    autofill::Suggestion suggestion(
+        l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS));
+    suggestion.frontend_id = autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY;
+    suggestions.push_back(suggestion);
 
-      metrics_util::LogContextOfShowAllSavedPasswordsShown(
-          metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD);
+    metrics_util::LogContextOfShowAllSavedPasswordsShown(
+        metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD);
   }
 
   metrics_util::LogPasswordDropdownShown(
@@ -338,18 +337,17 @@
   // Add 'Generation' option.
   // The UI code will pick up an icon from the resources based on the string.
   autofill::Suggestion suggestion(
-      l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD),
-      std::string(), std::string("keyIcon"),
-      autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY);
+      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD));
+  suggestion.icon = "keyIcon";
+  suggestion.frontend_id = autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY;
   suggestions.push_back(suggestion);
 
   // Add "Manage passwords".
   if (ShouldShowManualFallbackForPreLollipop(
           autofill_client_->GetSyncService())) {
     autofill::Suggestion suggestion(
-        l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS),
-        std::string(), std::string(),
-        autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
+        l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS));
+    suggestion.frontend_id = autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY;
     suggestions.push_back(suggestion);
 
     metrics_util::LogContextOfShowAllSavedPasswordsShown(
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 3884dc5..3c474ed 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -12909,15 +12909,15 @@
       'id': 405,
       'caption': '''Force enable spellcheck languages''',
       'tags': [],
-      'desc': '''Force-enables spellcheck languages. Unrecognized languages in that list will be ignored.
+      'desc': '''Force-enables spellcheck languages. Unrecognized languages in the list will be ignored.
 
       If you enable this policy, spellcheck will be enabled for the languages specified, in addition to the languages for which the user has enabled spellcheck.
 
       If you do not set this policy, or disable it, there will be no change to the user's spellcheck preferences.
 
-      If the SpellcheckEnabled policy is set to disabled, this policy will have no effect.
+      If the <ph name="SPELLCHECK_ENABLED_POLICY_NAME">SpellcheckEnabled</ph> policy is set to false, this policy will have no effect.
 
-      If a language is included in both this policy and the SpellcheckLanguageBlacklist policy, this policy is prioritized and the spellcheck language is enabled.
+      If a language is included in both this policy and the <ph name="SPELLCHECK_LANGUAGE_BLACKLIST_POLICY_NAME">SpellcheckLanguageBlacklist</ph> policy, this policy is prioritized and the spellcheck language is enabled.
 
       The currently supported languages are: af, bg, ca, cs, da, de, el, en-AU, en-CA, en-GB, en-US, es, es-419, es-AR, es-ES, es-MX, es-US, et, fa, fo, fr, he, hi, hr, hu, id, it, ko, lt, lv, nb, nl, pl, pt-BR, pt-PT, ro, ru, sh, sk, sl, sq, sr, sv, ta, tg, tr, uk, vi.'''
     },
@@ -12945,9 +12945,9 @@
 
       If you do not set this policy, or disable it, there will be no change to the user's spellcheck preferences.
 
-      If the SpellcheckEnabled policy is set to disabled, this policy will have no effect.
+      If the <ph name="SPELLCHECK_ENABLED_POLICY_NAME">SpellcheckEnabled</ph> policy is set to false, this policy will have no effect.
 
-      If a language is included in both this policy and the SpellcheckLanguage policy, the SpellcheckLanguage policy is prioritized and the spellcheck language will be enabled.
+      If a language is included in both this policy and the <ph name="SPELLCHECK_LANGUAGE_POLICY_NAME">SpellcheckLanguage</ph> policy, the latter is prioritized and the spellcheck language will be enabled.
 
       The currently supported languages are: af, bg, ca, cs, da, de, el, en-AU, en-CA, en-GB, en-US, es, es-419, es-AR, es-ES, es-MX, es-US, et, fa, fo, fr, he, hi, hr, hu, id, it, ko, lt, lv, nb, nl, pl, pt-BR, pt-PT, ro, ru, sh, sk, sl, sq, sr, sv, ta, tg, tr, uk, vi.'''
     },
@@ -12980,9 +12980,11 @@
       'id': 408,
       'caption': '''Enable spellcheck''',
       'tags': [],
-      'desc': '''If this policy is not set or enabled, the user is allowed to use spellcheck.
+      'desc': '''If this policy is not set, the user can enable or disable spellcheck in the language settings.
 
-      If this policy is disabled, the user is not allowed to use spellcheck. The SpellcheckLanguage and SpellcheckLanguageBlacklist policies will also be ignored when this policy is disabled.
+      If this policy is set to true, spellcheck is enabled and the user cannot disable it. On <ph name="MS_WIN_NAME">Microsoft® Windows</ph>, <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> and <ph name="LINUX_OS_NAME">Linux</ph>, spellcheck languages can be individually toggled on or off, so the user can still effectively disable spellcheck by toggling off every spellcheck language. To avoid that, the <ph name="SPELLCHECK_LANGUAGE_POLICY_NAME">SpellcheckLanguage</ph> policy can be used to force specific spellcheck languages to be enabled.
+
+      If this policy is set to false, spellcheck is disabled and the user cannot enable it. The <ph name="SPELLCHECK_LANGUAGE_POLICY_NAME">SpellcheckLanguage</ph> and <ph name="SPELLCHECK_LANGUAGE_BLACKLIST_POLICY_NAME">SpellcheckLanguageBlacklist</ph> policies have no effect when this policy is set to false.
       '''
     },
     {
diff --git a/components/signin/core/browser/account_tracker_service.cc b/components/signin/core/browser/account_tracker_service.cc
index 05be0c6..4821588 100644
--- a/components/signin/core/browser/account_tracker_service.cc
+++ b/components/signin/core/browser/account_tracker_service.cc
@@ -653,15 +653,10 @@
 
 std::string AccountTrackerService::SeedAccountInfo(const std::string& gaia,
                                                    const std::string& email) {
-  const std::string account_id = PickAccountIdForAccount(gaia, email);
-  const bool already_exists = base::ContainsKey(accounts_, account_id);
-  StartTrackingAccount(account_id);
-  AccountInfo& account_info = accounts_[account_id];
-  DCHECK(!already_exists || account_info.gaia.empty() ||
-         account_info.gaia == gaia);
+  AccountInfo account_info;
   account_info.gaia = gaia;
   account_info.email = email;
-  SaveToPrefs(account_info);
+  std::string account_id = SeedAccountInfo(account_info);
 
   DVLOG(1) << "AccountTrackerService::SeedAccountInfo"
            << " account_id=" << account_id << " gaia_id=" << gaia
@@ -673,11 +668,12 @@
 std::string AccountTrackerService::SeedAccountInfo(AccountInfo info) {
   info.account_id = PickAccountIdForAccount(info.gaia, info.email);
 
-  if (!base::ContainsKey(accounts_, info.account_id)) {
-    StartTrackingAccount(info.account_id);
-  }
-
+  const bool already_exists = base::ContainsKey(accounts_, info.account_id);
+  StartTrackingAccount(info.account_id);
   AccountInfo& account_info = accounts_[info.account_id];
+  DCHECK(!already_exists || account_info.gaia.empty() ||
+         account_info.gaia == info.gaia);
+
   // Update the missing fields in |account_info| with |info|.
   if (account_info.UpdateWith(info)) {
     if (!account_info.gaia.empty())
diff --git a/components/signin/core/browser/account_tracker_service_unittest.cc b/components/signin/core/browser/account_tracker_service_unittest.cc
index 7b0aae13..a9367943 100644
--- a/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -120,12 +120,16 @@
  public:
   TrackingEvent(TrackingEventType type,
                 const std::string& account_id,
-                const std::string& gaia_id)
-      : type_(type), account_id_(account_id), gaia_id_(gaia_id) {}
+                const std::string& gaia_id,
+                const std::string& email)
+      : type_(type),
+        account_id_(account_id),
+        gaia_id_(gaia_id),
+        email_(email) {}
 
   bool operator==(const TrackingEvent& event) const {
     return type_ == event.type_ && account_id_ == event.account_id_ &&
-           (gaia_id_.empty() || gaia_id_ == event.gaia_id_);
+           gaia_id_ == event.gaia_id_ && email_ == event.email_;
   }
 
   std::string ToString() const {
@@ -138,8 +142,9 @@
         typestr = "REM";
         break;
     }
-    return base::StringPrintf("{ type: %s, account_id: %s, gaia: %s }", typestr,
-                              account_id_.c_str(), gaia_id_.c_str());
+    return base::StringPrintf(
+        "{ type: %s, account_id: %s, gaia: %s, email: %s }", typestr,
+        account_id_.c_str(), gaia_id_.c_str(), email_.c_str());
   }
 
  private:
@@ -148,6 +153,7 @@
   TrackingEventType type_;
   std::string account_id_;
   std::string gaia_id_;
+  std::string email_;
 };
 
 bool CompareByUser(TrackingEvent a, TrackingEvent b) {
@@ -187,11 +193,13 @@
 };
 
 void AccountTrackerObserver::OnAccountUpdated(const AccountInfo& ids) {
-  events_.push_back(TrackingEvent(UPDATED, ids.account_id, ids.gaia));
+  events_.push_back(
+      TrackingEvent(UPDATED, ids.account_id, ids.gaia, ids.email));
 }
 
 void AccountTrackerObserver::OnAccountRemoved(const AccountInfo& ids) {
-  events_.push_back(TrackingEvent(REMOVED, ids.account_id, ids.gaia));
+  events_.push_back(
+      TrackingEvent(REMOVED, ids.account_id, ids.gaia, ids.email));
 }
 
 void AccountTrackerObserver::Clear() {
@@ -464,7 +472,8 @@
   EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 
   EXPECT_TRUE(account_tracker()
@@ -473,7 +482,8 @@
   ReturnAccountImageFetchSuccess(kAccountKeyAlpha);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
   EXPECT_FALSE(account_tracker()
                    ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha))
@@ -486,7 +496,8 @@
   EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 
   EXPECT_TRUE(account_tracker()
@@ -504,12 +515,14 @@
   EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
   SimulateTokenRevoked(kAccountKeyAlpha);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 }
 
@@ -526,7 +539,8 @@
   EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 
   SimulateTokenAvailable(kAccountKeyAlpha);
@@ -549,9 +563,11 @@
   EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta),
-                    AccountKeyToGaiaId(kAccountKeyBeta)),
+                    AccountKeyToGaiaId(kAccountKeyBeta),
+                    AccountKeyToEmail(kAccountKeyBeta)),
   }));
 }
 
@@ -562,13 +578,15 @@
   EXPECT_FALSE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta),
-                    AccountKeyToGaiaId(kAccountKeyBeta)),
+                    AccountKeyToGaiaId(kAccountKeyBeta),
+                    AccountKeyToEmail(kAccountKeyBeta)),
   }));
   ReturnAccountInfoFetchSuccess(kAccountKeyAlpha);
   EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched());
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 }
 
@@ -689,17 +707,21 @@
 
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta),
-                    AccountKeyToGaiaId(kAccountKeyBeta)),
+                    AccountKeyToGaiaId(kAccountKeyBeta),
+                    AccountKeyToEmail(kAccountKeyBeta)),
   }));
   // Wait until all account images are loaded.
   scoped_task_environment_.RunUntilIdle();
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta),
-                    AccountKeyToGaiaId(kAccountKeyBeta)),
+                    AccountKeyToGaiaId(kAccountKeyBeta),
+                    AccountKeyToEmail(kAccountKeyBeta)),
   }));
 
   std::vector<AccountInfo> infos = account_tracker()->GetAccounts();
@@ -740,20 +762,36 @@
 }
 
 TEST_F(AccountTrackerServiceTest, SeedAccountInfo) {
-  std::vector<AccountInfo> infos = account_tracker()->GetAccounts();
-  EXPECT_EQ(0u, infos.size());
+  EXPECT_TRUE(account_tracker()->GetAccounts().empty());
 
-  const std::string gaia_id = AccountKeyToGaiaId(kAccountKeyAlpha);
-  const std::string email = AccountKeyToEmail(kAccountKeyAlpha);
+  const std::string gaia_id = AccountKeyToGaiaId(kAccountKeyFooBar);
+  const std::string email = AccountKeyToEmail(kAccountKeyFooBar);
+  const std::string email_dotted = AccountKeyToEmail(kAccountKeyFooDotBar);
   const std::string account_id =
       account_tracker()->PickAccountIdForAccount(gaia_id, email);
-  account_tracker()->SeedAccountInfo(gaia_id, email);
 
-  infos = account_tracker()->GetAccounts();
+  account_tracker()->SeedAccountInfo(gaia_id, email);
+  auto infos = account_tracker()->GetAccounts();
   ASSERT_EQ(1u, infos.size());
   EXPECT_EQ(account_id, infos[0].account_id);
   EXPECT_EQ(gaia_id, infos[0].gaia);
   EXPECT_EQ(email, infos[0].email);
+  EXPECT_TRUE(observer()->CheckEvents({
+      TrackingEvent(UPDATED, account_id, gaia_id, email),
+  }));
+
+  account_tracker()->SeedAccountInfo(gaia_id, email_dotted);
+  infos = account_tracker()->GetAccounts();
+  ASSERT_EQ(1u, infos.size()) << "Seeding information to an existing account "
+                                 "should not add a new account";
+  EXPECT_EQ(account_id, infos[0].account_id)
+      << "Account id is either the canonicalized email or gaia, it should "
+         "remain the same";
+  EXPECT_EQ(gaia_id, infos[0].gaia);
+  EXPECT_EQ(email_dotted, infos[0].email) << "Email should be changed";
+  EXPECT_TRUE(observer()->CheckEvents({
+      TrackingEvent(UPDATED, account_id, gaia_id, email_dotted),
+  }));
 }
 
 TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) {
@@ -770,7 +808,7 @@
   EXPECT_EQ(info.email, stored_info.email);
   EXPECT_EQ(info.full_name, stored_info.full_name);
   EXPECT_TRUE(observer()->CheckEvents({
-      TrackingEvent(UPDATED, info.account_id, info.gaia),
+      TrackingEvent(UPDATED, info.account_id, info.gaia, info.email),
   }));
 
   // Validate that seeding new full informations to an existing account works
@@ -785,7 +823,7 @@
   EXPECT_EQ(info.email, stored_info.email);
   EXPECT_EQ(info.given_name, stored_info.given_name);
   EXPECT_TRUE(observer()->CheckEvents({
-      TrackingEvent(UPDATED, info.account_id, info.gaia),
+      TrackingEvent(UPDATED, info.account_id, info.gaia, info.email),
   }));
 
   // Validate that seeding invalid information to an existing account doesn't
@@ -827,7 +865,8 @@
 
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyIncomplete),
-                    AccountKeyToGaiaId(kAccountKeyIncomplete)),
+                    AccountKeyToGaiaId(kAccountKeyIncomplete),
+                    AccountKeyToEmail(kAccountKeyIncomplete)),
   }));
 
   // Enabling network fetches shouldn't cause any actual fetch since the
@@ -1158,7 +1197,8 @@
                      GenerateValidTokenInfoResponse(kAccountKeyChild));
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
   AccountInfo info = account_tracker()->GetAccountInfo(
       AccountKeyToAccountId(kAccountKeyChild));
@@ -1166,7 +1206,8 @@
   SimulateTokenRevoked(kAccountKeyChild);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 }
 
@@ -1184,7 +1225,8 @@
                      GenerateValidTokenInfoResponse(kAccountKeyChild));
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
   AccountInfo info = account_tracker()->GetAccountInfo(
       AccountKeyToAccountId(kAccountKeyChild));
@@ -1194,14 +1236,17 @@
   // On Android, is_child_account is set to false before removing it.
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 #else
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 #endif
 }
@@ -1221,23 +1266,28 @@
 #endif
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
   SimulateTokenRevoked(kAccountKeyChild);
 #if defined(OS_ANDROID)
   // On Android, is_child_account is set to false before removing it.
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 #else
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 #endif
 }
@@ -1261,7 +1311,8 @@
                      GenerateValidTokenInfoResponse(kAccountKeyChild));
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 
   // Now simulate child account graduation.
@@ -1277,13 +1328,15 @@
   EXPECT_FALSE(info.is_child_account);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 
   SimulateTokenRevoked(kAccountKeyChild);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild),
-                    AccountKeyToGaiaId(kAccountKeyChild)),
+                    AccountKeyToGaiaId(kAccountKeyChild),
+                    AccountKeyToEmail(kAccountKeyChild)),
   }));
 }
 
@@ -1293,14 +1346,16 @@
   ReturnAccountInfoFetchSuccess(kAccountKeyAlpha);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 
   SimulateTokenRevoked(kAccountKeyAlpha);
   ReturnAccountImageFetchFailure(kAccountKeyAlpha);
   EXPECT_TRUE(observer()->CheckEvents({
       TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyAlpha),
-                    AccountKeyToGaiaId(kAccountKeyAlpha)),
+                    AccountKeyToGaiaId(kAccountKeyAlpha),
+                    AccountKeyToEmail(kAccountKeyAlpha)),
   }));
 }
 
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 67425c0e..af8ab27 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -714,30 +714,6 @@
 
 if (is_android) {
   import("//build/config/android/rules.gni")
-  import("//components/sync/protocol/protocol_sources.gni")
-
-  _test_support_prepared_protos_dir =
-      "$root_gen_dir/test_support_proto_java_prepare/"
-  action("test_support_proto_java_prepare") {
-    script = "//components/sync/protocol/prepare_protos_for_java_tests.py"
-    inputs = sync_protocol_sources
-    outputs = process_file_template(
-            sync_protocol_sources,
-            [ "$_test_support_prepared_protos_dir/{{source_file_part}}" ])
-    args = [
-      "--output_dir",
-      rebase_path(_test_support_prepared_protos_dir, root_build_dir),
-    ]
-    args += rebase_path(sync_protocol_sources, root_build_dir)
-  }
-
-  proto_java_library("test_support_proto_java") {
-    proto_path = _test_support_prepared_protos_dir
-    sources = get_target_outputs(":test_support_proto_java_prepare")
-    deps = [
-      ":test_support_proto_java_prepare",
-    ]
-  }
 
   android_library("sync_java_test_support") {
     testonly = true
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 64d96cf0..55f3640 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -626,10 +626,8 @@
 
 base::WeakPtr<ModelTypeControllerDelegate>
 SyncEngineBackend::GetNigoriControllerDelegate() {
-  // TODO(crbug.com/922900): return actual ModelTypeControllerDelegate.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
-  return nullptr;
+  return sync_manager_->GetNigoriControllerDelegate();
 }
 
 bool SyncEngineBackend::HasUnsyncedItemsForTest() const {
diff --git a/components/sync/driver/glue/sync_engine_backend.h b/components/sync/driver/glue/sync_engine_backend.h
index 8cc3e530..d406116 100644
--- a/components/sync/driver/glue/sync_engine_backend.h
+++ b/components/sync/driver/glue/sync_engine_backend.h
@@ -172,7 +172,8 @@
   // Notify about change in client id.
   void DoOnInvalidatorClientIdChange(const std::string& client_id);
 
-  // Return ModelTypeControllerDelegate for Nigori.
+  // Returns ModelTypeControllerDelegate for Nigori. USS implementation of
+  // Nigori must be enabled.
   base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate();
 
   bool HasUnsyncedItemsForTest() const;
diff --git a/components/sync/engine/fake_sync_manager.cc b/components/sync/engine/fake_sync_manager.cc
index 66f32bb..c52a7306 100644
--- a/components/sync/engine/fake_sync_manager.cc
+++ b/components/sync/engine/fake_sync_manager.cc
@@ -219,6 +219,11 @@
   return &fake_encryption_handler_;
 }
 
+base::WeakPtr<ModelTypeControllerDelegate>
+FakeSyncManager::GetNigoriControllerDelegate() {
+  return nullptr;
+}
+
 std::vector<std::unique_ptr<ProtocolEvent>>
 FakeSyncManager::GetBufferedProtocolEvents() {
   return std::vector<std::unique_ptr<ProtocolEvent>>();
diff --git a/components/sync/engine/fake_sync_manager.h b/components/sync/engine/fake_sync_manager.h
index c06f795..1c1deab 100644
--- a/components/sync/engine/fake_sync_manager.h
+++ b/components/sync/engine/fake_sync_manager.h
@@ -105,6 +105,8 @@
   const std::string cache_guid() override;
   bool HasUnsyncedItemsForTest() override;
   SyncEncryptionHandler* GetEncryptionHandler() override;
+  base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
+      override;
   std::vector<std::unique_ptr<ProtocolEvent>> GetBufferedProtocolEvents()
       override;
   void RefreshTypes(ModelTypeSet types) override;
diff --git a/components/sync/engine/sync_manager.h b/components/sync/engine/sync_manager.h
index 73fffdc..f3548f4 100644
--- a/components/sync/engine/sync_manager.h
+++ b/components/sync/engine/sync_manager.h
@@ -14,6 +14,7 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/task_runner.h"
 #include "base/threading/thread_checker.h"
 #include "components/sync/base/invalidation_interface.h"
@@ -49,6 +50,7 @@
 class ExtensionsActivity;
 class JsBackend;
 class JsEventHandler;
+class ModelTypeControllerDelegate;
 class ProtocolEvent;
 class SyncCycleSnapshot;
 class SyncEncryptionHandler;
@@ -358,6 +360,11 @@
   // Returns the SyncManager's encryption handler.
   virtual SyncEncryptionHandler* GetEncryptionHandler() = 0;
 
+  // Returns ModelTypeControllerDelegate for Nigori. USS implementation of
+  // Nigori must be enabled.
+  virtual base::WeakPtr<ModelTypeControllerDelegate>
+  GetNigoriControllerDelegate() = 0;
+
   // Ask the SyncManager to fetch updates for the given types.
   virtual void RefreshTypes(ModelTypeSet types) = 0;
 
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index d5e5a82..01be457 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -24,13 +25,17 @@
 #include "components/sync/engine/engine_util.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
 #include "components/sync/engine/polling_constants.h"
+#include "components/sync/engine/sync_engine_switches.h"
 #include "components/sync/engine_impl/cycle/directory_type_debug_info_emitter.h"
 #include "components/sync/engine_impl/loopback_server/loopback_connection_manager.h"
 #include "components/sync/engine_impl/model_type_connector_proxy.h"
 #include "components/sync/engine_impl/net/sync_server_connection_manager.h"
+#include "components/sync/engine_impl/sync_encryption_handler_impl.h"
 #include "components/sync/engine_impl/sync_scheduler.h"
 #include "components/sync/engine_impl/syncer_types.h"
 #include "components/sync/engine_impl/uss_migrator.h"
+#include "components/sync/nigori/nigori_model_type_processor.h"
+#include "components/sync/nigori/nigori_sync_bridge_impl.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "components/sync/syncable/base_node.h"
 #include "components/sync/syncable/directory.h"
@@ -276,10 +281,31 @@
 
   allstatus_.SetHasKeystoreKey(
       !args->restored_keystore_key_for_bootstrapping.empty());
-  sync_encryption_handler_ = std::make_unique<SyncEncryptionHandlerImpl>(
-      &share_, args->encryptor, args->restored_key_for_bootstrapping,
-      args->restored_keystore_key_for_bootstrapping,
-      base::BindRepeating(&Nigori::GenerateScryptSalt));
+
+  Cryptographer* cryptographer_for_directory = nullptr;
+  syncable::NigoriHandler* nigori_handler = nullptr;
+  KeystoreKeysHandler* keystore_keys_handler = nullptr;
+  if (base::FeatureList::IsEnabled(switches::kSyncUSSNigori)) {
+    auto nigori_model_type_processor =
+        std::make_unique<NigoriModelTypeProcessor>();
+    nigori_controller_delegate_ =
+        nigori_model_type_processor->GetControllerDelegate();
+    auto nigori_sync_bridge_impl = std::make_unique<NigoriSyncBridgeImpl>(
+        std::move(nigori_model_type_processor), args->encryptor);
+    keystore_keys_handler = nigori_sync_bridge_impl.get();
+    sync_encryption_handler_ = std::move(nigori_sync_bridge_impl);
+  } else {
+    auto sync_encryption_handler_impl =
+        std::make_unique<SyncEncryptionHandlerImpl>(
+            &share_, args->encryptor, args->restored_key_for_bootstrapping,
+            args->restored_keystore_key_for_bootstrapping,
+            base::BindRepeating(&Nigori::GenerateScryptSalt));
+    cryptographer_for_directory =
+        sync_encryption_handler_impl->GetCryptographerUnsafe();
+    nigori_handler = sync_encryption_handler_impl.get();
+    keystore_keys_handler = sync_encryption_handler_impl.get();
+    sync_encryption_handler_ = std::move(sync_encryption_handler_impl);
+  }
   sync_encryption_handler_->AddObserver(this);
   sync_encryption_handler_->AddObserver(&debug_info_event_listener_);
   sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_);
@@ -293,10 +319,13 @@
           args->authenticated_account_id, args->cache_guid, absolute_db_path);
 
   DCHECK(backing_store);
+
+  // Note: |nigori_handler| and |cryptographer_for_directory| are nullptrs iff
+  // kSyncUSSNigori is enabled.
   share_.directory = std::make_unique<syncable::Directory>(
       std::move(backing_store), args->unrecoverable_error_handler,
-      report_unrecoverable_error_function_, sync_encryption_handler_.get(),
-      sync_encryption_handler_->GetCryptographerUnsafe());
+      report_unrecoverable_error_function_, nigori_handler,
+      cryptographer_for_directory);
 
   DVLOG(1) << "AccountId: " << args->authenticated_account_id;
   if (!OpenDirectory(args)) {
@@ -330,7 +359,7 @@
 
   model_type_registry_ = std::make_unique<ModelTypeRegistry>(
       args->workers, &share_, this, base::Bind(&MigrateDirectoryData),
-      args->cancelation_signal, sync_encryption_handler_.get());
+      args->cancelation_signal, keystore_keys_handler);
   sync_encryption_handler_->AddObserver(model_type_registry_.get());
 
   // Build a SyncCycleContext and store the worker in it.
@@ -942,6 +971,11 @@
   return sync_encryption_handler_.get();
 }
 
+base::WeakPtr<ModelTypeControllerDelegate>
+SyncManagerImpl::GetNigoriControllerDelegate() {
+  return nigori_controller_delegate_;
+}
+
 std::vector<std::unique_ptr<ProtocolEvent>>
 SyncManagerImpl::GetBufferedProtocolEvents() {
   return protocol_event_buffer_.GetBufferedProtocolEvents();
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h
index 10c1a95..1c12aa2d 100644
--- a/components/sync/engine_impl/sync_manager_impl.h
+++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -27,7 +27,6 @@
 #include "components/sync/engine_impl/js_sync_manager_observer.h"
 #include "components/sync/engine_impl/net/server_connection_manager.h"
 #include "components/sync/engine_impl/nudge_handler.h"
-#include "components/sync/engine_impl/sync_encryption_handler_impl.h"
 #include "components/sync/engine_impl/sync_engine_event_listener.h"
 #include "components/sync/js/js_backend.h"
 #include "components/sync/syncable/change_reorder_buffer.h"
@@ -98,6 +97,8 @@
   const std::string cache_guid() override;
   bool HasUnsyncedItemsForTest() override;
   SyncEncryptionHandler* GetEncryptionHandler() override;
+  base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
+      override;
   std::vector<std::unique_ptr<ProtocolEvent>> GetBufferedProtocolEvents()
       override;
   void RegisterDirectoryTypeDebugInfoObserver(
@@ -313,10 +314,12 @@
 
   base::Closure report_unrecoverable_error_function_;
 
-  // Sync's encryption handler. It tracks the set of encrypted types, manages
-  // changing passphrases, and in general handles sync-specific interactions
-  // with the cryptographer.
-  std::unique_ptr<SyncEncryptionHandlerImpl> sync_encryption_handler_;
+  // Points to either SyncEncryptionHandlerImpl or NigoriSyncBridgeImpl
+  // depending on whether USS implementation of Nigori is enabled or not.
+  std::unique_ptr<SyncEncryptionHandler> sync_encryption_handler_;
+
+  // Initialized iff USS implementation of Nigori is enabled.
+  base::WeakPtr<ModelTypeControllerDelegate> nigori_controller_delegate_;
 
   base::WeakPtrFactory<SyncManagerImpl> weak_ptr_factory_;
 
diff --git a/components/sync/engine_impl/sync_manager_impl_unittest.cc b/components/sync/engine_impl/sync_manager_impl_unittest.cc
index b2cebd3..f811528 100644
--- a/components/sync/engine_impl/sync_manager_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_manager_impl_unittest.cc
@@ -50,6 +50,7 @@
 #include "components/sync/syncable/directory.h"
 #include "components/sync/syncable/entry.h"
 #include "components/sync/syncable/mutable_entry.h"
+#include "components/sync/syncable/nigori_handler.h"
 #include "components/sync/syncable/nigori_util.h"
 #include "components/sync/syncable/read_node.h"
 #include "components/sync/syncable/read_transaction.h"
diff --git a/components/sync/nigori/nigori_local_change_processor.h b/components/sync/nigori/nigori_local_change_processor.h
index c26aed10..b5c8507 100644
--- a/components/sync/nigori/nigori_local_change_processor.h
+++ b/components/sync/nigori/nigori_local_change_processor.h
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "components/sync/model/model_error.h"
 #include "components/sync/protocol/entity_metadata.pb.h"
@@ -16,6 +17,7 @@
 
 namespace syncer {
 
+class ModelTypeControllerDelegate;
 class NigoriSyncBridge;
 struct EntityData;
 
@@ -59,6 +61,10 @@
   // disabled (generally until the next restart).
   virtual void ReportError(const ModelError& error) = 0;
 
+  // Returns the delegate for the controller.
+  virtual base::WeakPtr<ModelTypeControllerDelegate>
+  GetControllerDelegate() = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(NigoriLocalChangeProcessor);
 };
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index ea46e06..11fa9c32 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -26,7 +26,9 @@
 }  // namespace
 
 NigoriModelTypeProcessor::NigoriModelTypeProcessor()
-    : bridge_(nullptr), weak_ptr_factory_for_worker_(this) {}
+    : bridge_(nullptr),
+      weak_ptr_factory_for_controller_(this),
+      weak_ptr_factory_for_worker_(this) {}
 
 NigoriModelTypeProcessor::~NigoriModelTypeProcessor() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -373,6 +375,12 @@
   }
 }
 
+base::WeakPtr<ModelTypeControllerDelegate>
+NigoriModelTypeProcessor::GetControllerDelegate() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return weak_ptr_factory_for_controller_.GetWeakPtr();
+}
+
 bool NigoriModelTypeProcessor::IsConnectedForTest() const {
   return IsConnected();
 }
diff --git a/components/sync/nigori/nigori_model_type_processor.h b/components/sync/nigori/nigori_model_type_processor.h
index 846071d..db9f593 100644
--- a/components/sync/nigori/nigori_model_type_processor.h
+++ b/components/sync/nigori/nigori_model_type_processor.h
@@ -52,6 +52,7 @@
   void Put(std::unique_ptr<EntityData> entity_data) override;
   NigoriMetadataBatch GetMetadata() override;
   void ReportError(const ModelError& error) override;
+  base::WeakPtr<ModelTypeControllerDelegate> GetControllerDelegate() override;
 
   bool IsConnectedForTest() const;
 
@@ -98,6 +99,11 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // WeakPtrFactory for this processor for ModelTypeController (only gets
+  // invalidated during destruction).
+  base::WeakPtrFactory<ModelTypeControllerDelegate>
+      weak_ptr_factory_for_controller_;
+
   // WeakPtrFactory for this processor which will be sent to sync thread.
   base::WeakPtrFactory<NigoriModelTypeProcessor> weak_ptr_factory_for_worker_;
 
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index 3cfcd0ea..16f34ff 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -41,6 +41,8 @@
   MOCK_METHOD1(Put, void(std::unique_ptr<EntityData>));
   MOCK_METHOD0(GetMetadata, NigoriMetadataBatch());
   MOCK_METHOD1(ReportError, void(const ModelError&));
+  MOCK_METHOD0(GetControllerDelegate,
+               base::WeakPtr<ModelTypeControllerDelegate>());
 };
 
 class NigoriSyncBridgeImplTest : public testing::Test {
diff --git a/components/sync/protocol/BUILD.gn b/components/sync/protocol/BUILD.gn
index 297ef3c..0de6835 100644
--- a/components/sync/protocol/BUILD.gn
+++ b/components/sync/protocol/BUILD.gn
@@ -31,3 +31,30 @@
     "//components/sync/base",
   ]
 }
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+
+  _test_support_prepared_protos_dir =
+      "$root_gen_dir/test_support_proto_java_prepare/"
+  action("test_support_java_prepare") {
+    script = "prepare_protos_for_java_tests.py"
+    inputs = sync_protocol_sources
+    outputs = process_file_template(
+            sync_protocol_sources,
+            [ "$_test_support_prepared_protos_dir/{{source_file_part}}" ])
+    args = [
+      "--output_dir",
+      rebase_path(_test_support_prepared_protos_dir, root_build_dir),
+    ]
+    args += rebase_path(sync_protocol_sources, root_build_dir)
+  }
+
+  proto_java_library("test_support_java") {
+    proto_path = _test_support_prepared_protos_dir
+    sources = get_target_outputs(":test_support_java_prepare")
+    deps = [
+      ":test_support_java_prepare",
+    ]
+  }
+}
diff --git a/components/sync/syncable/directory.cc b/components/sync/syncable/directory.cc
index 780fea4..fafe289 100644
--- a/components/sync/syncable/directory.cc
+++ b/components/sync/syncable/directory.cc
@@ -1066,6 +1066,7 @@
 }
 
 NigoriHandler* Directory::GetNigoriHandler() {
+  DCHECK(nigori_handler_);
   return nigori_handler_;
 }
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index eed9321..b02337a 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/ui.gni")
 import("//components/viz/viz.gni")
 import("//gpu/vulkan/features.gni")
+import("//media/gpu/args.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 
 config("viz_service_implementation") {
@@ -352,6 +353,8 @@
   if (is_chromeos) {
     deps += [
       "//components/arc/video_accelerator",
+      "//components/chromeos_camera:jpeg_encode_accelerator_service",
+      "//components/chromeos_camera:mjpeg_decode_accelerator_service",
       "//gpu/command_buffer/service:gles2",
       "//media/mojo/services",
     ]
diff --git a/components/viz/service/gl/DEPS b/components/viz/service/gl/DEPS
index 2066c84..3651d67 100644
--- a/components/viz/service/gl/DEPS
+++ b/components/viz/service/gl/DEPS
@@ -2,6 +2,7 @@
 
 include_rules = [
   "+components/arc/video_accelerator",
+  "+components/chromeos_camera",
   "+components/crash/core/common/crash_key.h",
   "+gpu/command_buffer",
   "+gpu/config",
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 6cdd874..4341dff 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -40,15 +40,10 @@
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/ipc_sync_channel.h"
 #include "ipc/ipc_sync_message_filter.h"
-#include "media/gpu/gpu_mjpeg_decode_accelerator_factory.h"
 #include "media/gpu/gpu_video_accelerator_util.h"
 #include "media/gpu/gpu_video_encode_accelerator_factory.h"
 #include "media/gpu/ipc/service/gpu_video_decode_accelerator.h"
 #include "media/gpu/ipc/service/media_gpu_channel_manager.h"
-#if defined(OS_CHROMEOS)
-#include "media/mojo/services/cros_mojo_jpeg_encode_accelerator_service.h"
-#include "media/mojo/services/cros_mojo_mjpeg_decode_accelerator_service.h"
-#endif  // defined(OS_CHROMEOS)
 #include "media/mojo/services/mojo_video_encode_accelerator_provider.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "third_party/skia/include/gpu/GrContext.h"
@@ -73,6 +68,9 @@
 #include "components/arc/video_accelerator/gpu_arc_video_protected_buffer_allocator.h"
 #include "components/arc/video_accelerator/protected_buffer_manager.h"
 #include "components/arc/video_accelerator/protected_buffer_manager_proxy.h"
+#include "components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h"
+#include "components/chromeos_camera/mojo_jpeg_encode_accelerator_service.h"
+#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator_service.h"
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
@@ -91,6 +89,15 @@
     void(int severity, size_t message_start, const std::string& message)>>::
     Leaky g_log_callback = LAZY_INSTANCE_INITIALIZER;
 
+bool IsAcceleratedJpegDecodeSupported() {
+#if defined(OS_CHROMEOS)
+  return chromeos_camera::GpuMjpegDecodeAcceleratorFactory::
+      IsAcceleratedJpegDecodeSupported();
+#else
+  return false;
+#endif  // defined(OS_CHROMEOS)
+}
+
 bool GpuLogMessageHandler(int severity,
                           const char* file,
                           int line,
@@ -221,8 +228,8 @@
       media::GpuVideoAcceleratorUtil::ConvertMediaToGpuEncodeProfiles(
           media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles(
               gpu_preferences_));
-  gpu_info_.jpeg_decode_accelerator_supported = media::
-      GpuMjpegDecodeAcceleratorFactory::IsAcceleratedJpegDecodeSupported();
+  gpu_info_.jpeg_decode_accelerator_supported =
+      IsAcceleratedJpegDecodeSupported();
   // Record initialization only after collecting the GPU info because that can
   // take a significant amount of time.
   gpu_info_.initialization_time = base::Time::Now() - start_time_;
@@ -412,13 +419,15 @@
 void GpuServiceImpl::CreateJpegDecodeAccelerator(
     chromeos_camera::mojom::MjpegDecodeAcceleratorRequest jda_request) {
   DCHECK(io_runner_->BelongsToCurrentThread());
-  media::CrOSMojoMjpegDecodeAcceleratorService::Create(std::move(jda_request));
+  chromeos_camera::MojoMjpegDecodeAcceleratorService::Create(
+      std::move(jda_request));
 }
 
 void GpuServiceImpl::CreateJpegEncodeAccelerator(
     chromeos_camera::mojom::JpegEncodeAcceleratorRequest jea_request) {
   DCHECK(io_runner_->BelongsToCurrentThread());
-  media::CrOSMojoJpegEncodeAcceleratorService::Create(std::move(jea_request));
+  chromeos_camera::MojoJpegEncodeAcceleratorService::Create(
+      std::move(jea_request));
 }
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index d6306fe8..dcb8773 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2006,7 +2006,7 @@
     ]
     deps += [
       "//chromeos/resources",
-      "//media/mojo/clients:cros_mjpeg_decode_accelerator",
+      "//components/chromeos_camera:mojo_mjpeg_decode_accelerator",
     ]
   } else {
     sources += [
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index a0b9023..e7b467ee 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -241,6 +241,7 @@
   INVALID_INITIATOR_ORIGIN = 213,
   RFHI_BEGIN_NAVIGATION_MISSING_INITIATOR_ORIGIN = 214,
   RFHI_BEGIN_NAVIGATION_NON_WEBBY_TRANSITION = 215,
+  RFH_NO_MATCHING_NAVIGATION_REQUEST_ON_COMMIT = 216,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index df8fe7a..ab3bc8b 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -37,6 +37,7 @@
 #include "content/browser/frame_host/origin_policy_throttle.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/loader/navigation_url_loader.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -295,11 +296,9 @@
   }
 
   // Ask whether we should request a policy.
-  std::string origin_policy_request;
-  if (OriginPolicyThrottle::ShouldRequestOriginPolicy(url,
-                                                      &origin_policy_request)) {
+  if (OriginPolicyThrottle::ShouldRequestOriginPolicy(url)) {
     headers->SetHeader(net::HttpRequestHeaders::kSecOriginPolicy,
-                       origin_policy_request);
+                       kDefaultOriginPolicyVersion);
   }
 
   // Next, set the HTTP Origin if needed.
@@ -555,7 +554,9 @@
     bool override_user_agent,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
     mojom::NavigationClientAssociatedPtrInfo navigation_client,
-    blink::mojom::NavigationInitiatorPtr navigation_initiator) {
+    blink::mojom::NavigationInitiatorPtr navigation_initiator,
+    scoped_refptr<PrefetchedSignedExchangeCache>
+        prefetched_signed_exchange_cache) {
   // Only normal navigations to a different document or reloads are expected.
   // - Renderer-initiated fragment-navigations never take place in the browser,
   //   even with PlzNavigate.
@@ -594,6 +595,8 @@
       std::move(navigation_client), std::move(navigation_initiator)));
   navigation_request->blob_url_loader_factory_ =
       std::move(blob_url_loader_factory);
+  navigation_request->prefetched_signed_exchange_cache_ =
+      std::move(prefetched_signed_exchange_cache);
   return navigation_request;
 }
 
@@ -1811,7 +1814,7 @@
           frame_tree_node_->devtools_frame_token()),
       std::move(navigation_ui_data),
       navigation_handle_->service_worker_handle(), appcache_handle_.get(),
-      this);
+      std::move(prefetched_signed_exchange_cache_), this);
   DCHECK(!render_frame_host_);
 }
 
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index c77843a6..09a1053 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -50,6 +50,7 @@
 class NavigationData;
 class NavigationUIData;
 class NavigatorDelegate;
+class PrefetchedSignedExchangeCache;
 class SiteInstanceImpl;
 struct SubresourceLoaderParams;
 
@@ -149,7 +150,9 @@
       bool override_user_agent,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
       mojom::NavigationClientAssociatedPtrInfo navigation_client,
-      blink::mojom::NavigationInitiatorPtr navigation_initiator);
+      blink::mojom::NavigationInitiatorPtr navigation_initiator,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache);
 
   // Creates a request at commit time. This should only be used for
   // renderer-initiated same-document navigations, and navigations whose
@@ -865,6 +868,15 @@
 
   bool was_redirected_ = false;
 
+  // Used when SignedExchangeSubresourcePrefetch is enabled to hold the
+  // prefetched signed exchanges. This is shared with the navigation initiator's
+  // RenderFrameHostImpl. This also means that only the navigations that were
+  // directly initiated by the frame that made the prefetches could use the
+  // prefetched resources, which is a different behavior from regular prefetches
+  // (where all prefetched resources are stored and shared in http cache).
+  scoped_refptr<PrefetchedSignedExchangeCache>
+      prefetched_signed_exchange_cache_;
+
   base::WeakPtrFactory<NavigationRequest> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NavigationRequest);
diff --git a/content/browser/frame_host/navigator.cc b/content/browser/frame_host/navigator.cc
index 7cce6d2..9d05080 100644
--- a/content/browser/frame_host/navigator.cc
+++ b/content/browser/frame_host/navigator.cc
@@ -5,6 +5,7 @@
 #include "content/browser/frame_host/navigator.h"
 
 #include "base/time/time.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/public/browser/stream_handle.h"
 
 namespace content {
@@ -33,6 +34,8 @@
     mojom::BeginNavigationParamsPtr begin_params,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
     mojom::NavigationClientAssociatedPtrInfo navigation_client,
-    blink::mojom::NavigationInitiatorPtr navigation_initiator) {}
+    blink::mojom::NavigationInitiatorPtr navigation_initiator,
+    scoped_refptr<PrefetchedSignedExchangeCache>
+        prefetched_signed_exchange_cache) {}
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index dae4b8d..9ae7978 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -32,6 +32,7 @@
 
 class FrameNavigationEntry;
 class FrameTreeNode;
+class PrefetchedSignedExchangeCache;
 class RenderFrameHostImpl;
 struct CommonNavigationParams;
 
@@ -149,7 +150,9 @@
       mojom::BeginNavigationParamsPtr begin_params,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
       mojom::NavigationClientAssociatedPtrInfo navigation_client,
-      blink::mojom::NavigationInitiatorPtr navigation_initiator);
+      blink::mojom::NavigationInitiatorPtr navigation_initiator,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache);
 
   // Used to restart a navigation that was thought to be same-document in
   // cross-document mode.
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index 0241452..242dacf 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -21,6 +21,7 @@
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/frame_host/navigator_delegate.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
@@ -583,7 +584,9 @@
     mojom::BeginNavigationParamsPtr begin_params,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
     mojom::NavigationClientAssociatedPtrInfo navigation_client,
-    blink::mojom::NavigationInitiatorPtr navigation_initiator) {
+    blink::mojom::NavigationInitiatorPtr navigation_initiator,
+    scoped_refptr<PrefetchedSignedExchangeCache>
+        prefetched_signed_exchange_cache) {
   // TODO(clamy): the url sent by the renderer should be validated with
   // FilterURL.
   // This is a renderer-initiated navigation.
@@ -647,7 +650,8 @@
           std::move(begin_params), controller_->GetLastCommittedEntryIndex(),
           controller_->GetEntryCount(), override_user_agent,
           std::move(blob_url_loader_factory), std::move(navigation_client),
-          std::move(navigation_initiator)));
+          std::move(navigation_initiator),
+          std::move(prefetched_signed_exchange_cache)));
   NavigationRequest* navigation_request = frame_tree_node->navigation_request();
 
   // This frame has already run beforeunload before it sent this IPC.  See if
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
index 6e6daea..9fe28532 100644
--- a/content/browser/frame_host/navigator_impl.h
+++ b/content/browser/frame_host/navigator_impl.h
@@ -95,7 +95,9 @@
       mojom::BeginNavigationParamsPtr begin_params,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
       mojom::NavigationClientAssociatedPtrInfo navigation_client,
-      blink::mojom::NavigationInitiatorPtr navigation_initiator) override;
+      blink::mojom::NavigationInitiatorPtr navigation_initiator,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache) override;
   void RestartNavigationAsCrossDocument(
       std::unique_ptr<NavigationRequest> navigation_request) override;
   void OnAbortNavigation(FrameTreeNode* frame_tree_node) override;
diff --git a/content/browser/frame_host/origin_policy_throttle.cc b/content/browser/frame_host/origin_policy_throttle.cc
index 45e1a682..f1ba220 100644
--- a/content/browser/frame_host/origin_policy_throttle.cc
+++ b/content/browser/frame_host/origin_policy_throttle.cc
@@ -30,17 +30,16 @@
 
 namespace {
 // Constants derived from the spec, https://github.com/WICG/origin-policy
-static const char* kDefaultPolicy = "0";
-static const char* kDeletePolicy = "0";
-static const char* kWellKnown = "/.well-known/origin-policy/";
-static const char* kReportTo = "report-to";
-static const char* kPolicy = "policy";
+static constexpr const char* kDeletePolicy = "0";
+static constexpr const char* kWellKnown = "/.well-known/origin-policy/";
+static constexpr const char* kReportTo = "report-to";
+static constexpr const char* kPolicy = "policy";
 
 // Marker for (temporarily) exempted origins.
 // TODO(vogelheim): Make sure this is outside the value space for policy
 //                  names. A name with a comma in it shouldn't be allowed, but
 //                  I don't think we presently check this anywhere.
-static const char* kExemptedOriginPolicy = "exception,";
+static constexpr const char* kExemptedOriginPolicyVersion = "exception,";
 
 // Maximum policy size (implementation-defined limit in bytes).
 // (Limit copied from network::SimpleURLLoader::kMaxBoundedStringDownloadSize.)
@@ -56,9 +55,7 @@
 }
 
 // static
-bool OriginPolicyThrottle::ShouldRequestOriginPolicy(
-    const GURL& url,
-    std::string* request_version) {
+bool OriginPolicyThrottle::ShouldRequestOriginPolicy(const GURL& url) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   bool origin_policy_enabled =
       base::FeatureList::IsEnabled(features::kOriginPolicy) ||
@@ -70,13 +67,6 @@
   if (!url.SchemeIs(url::kHttpsScheme))
     return false;
 
-  if (request_version) {
-    const KnownVersionMap& versions = GetKnownVersions();
-    const auto iter = versions.find(url::Origin::Create(url));
-    bool has_version = iter != versions.end();
-    bool use_default = !has_version || iter->second == kExemptedOriginPolicy;
-    *request_version = use_default ? std::string(kDefaultPolicy) : iter->second;
-  }
   return true;
 }
 
@@ -95,14 +85,13 @@
   // TODO(vogelheim): Rewrite & hoist up this DCHECK to ensure that ..HasHeader
   //     and ShouldRequestOriginPolicy are always equal on entry to the method.
   //     This depends on https://crbug.com/881234 being fixed.
-  DCHECK(OriginPolicyThrottle::ShouldRequestOriginPolicy(handle->GetURL(),
-                                                         nullptr));
+  DCHECK(OriginPolicyThrottle::ShouldRequestOriginPolicy(handle->GetURL()));
   return base::WrapUnique(new OriginPolicyThrottle(handle));
 }
 
 // static
 void OriginPolicyThrottle::AddExceptionFor(const GURL& url) {
-  GetKnownVersions()[url::Origin::Create(url)] = kExemptedOriginPolicy;
+  GetKnownVersions()[url::Origin::Create(url)] = kExemptedOriginPolicyVersion;
 }
 
 OriginPolicyThrottle::~OriginPolicyThrottle() {}
@@ -157,7 +146,7 @@
   }
 
   // Process policy exceptions.
-  if (iter != versions.end() && iter->second == kExemptedOriginPolicy) {
+  if (iter != versions.end() && iter->second == kExemptedOriginPolicyVersion) {
     return NavigationThrottle::PROCEED;
   }
 
@@ -403,4 +392,13 @@
 void OriginPolicyThrottle::Report(OriginPolicyErrorReason reason) {}
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
+bool OriginPolicyThrottle::IsExemptedForTesting(const url::Origin& origin) {
+  KnownVersionMap& versions = GetKnownVersions();
+  auto iter = versions.find(origin);
+  if (iter != versions.end())
+    return iter->second == kExemptedOriginPolicyVersion;
+
+  return false;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/origin_policy_throttle.h b/content/browser/frame_host/origin_policy_throttle.h
index 82fed28..7b42b5c 100644
--- a/content/browser/frame_host/origin_policy_throttle.h
+++ b/content/browser/frame_host/origin_policy_throttle.h
@@ -32,13 +32,16 @@
 class NavigationHandle;
 enum class OriginPolicyErrorReason;
 
+// Constant derived from the spec, https://github.com/WICG/origin-policy
+static constexpr const char* kDefaultOriginPolicyVersion = "0";
+
 // The OriginPolicyThrottle is responsible for deciding whether an origin
 // policy should be fetched, and doing so when that is positive.
 //
 // The intended use is that the navigation request will
 // - call OriginPolicyThrottle::ShouldRequestOriginPolicy to determine whether
-//   a policy should be requested and which version, and should add the
-//   appropriate SecOriginPolicy: header.
+//   a policy should be requested, and add the appropriate SecOriginPolicy:
+//   header.
 // - call OriginPolicyThrottle::MaybeCreateThrottleFor a given navigation.
 //   This will use presence of the header to decide whether to create a
 //   throttle or not.
@@ -50,10 +53,8 @@
   };
 
   // Determine whether to request a policy (or advertise origin policy
-  // support) and which version.
-  // Returns whether the policy header should be sent. It it returns true,
-  // |version| will contain the policy version to use.
-  static bool ShouldRequestOriginPolicy(const GURL& url, std::string* version);
+  // support). Returns whether the policy header should be sent.
+  static bool ShouldRequestOriginPolicy(const GURL& url);
 
   // Create a throttle (if the request contains the appropriate header.
   // The throttle will handle fetching of the policy and updating the
@@ -86,6 +87,8 @@
   GetRequestedPolicyAndReportGroupFromHeaderStringForTesting(
       const std::string& header);
 
+  static bool IsExemptedForTesting(const url::Origin& origin);
+
  private:
   using FetchCallback = base::OnceCallback<void(std::unique_ptr<std::string>)>;
   using RedirectCallback =
diff --git a/content/browser/frame_host/origin_policy_throttle_unittest.cc b/content/browser/frame_host/origin_policy_throttle_unittest.cc
index 4f90af18..2de6e7a 100644
--- a/content/browser/frame_host/origin_policy_throttle_unittest.cc
+++ b/content/browser/frame_host/origin_policy_throttle_unittest.cc
@@ -43,7 +43,7 @@
 
   void CreateHandleFor(const GURL& url) {
     net::HttpRequestHeaders headers;
-    if (OriginPolicyThrottle::ShouldRequestOriginPolicy(url, nullptr))
+    if (OriginPolicyThrottle::ShouldRequestOriginPolicy(url))
       headers.SetHeader(net::HttpRequestHeaders::kSecOriginPolicy, "0");
 
     nav_handle_ = std::make_unique<MockNavigationHandle>(web_contents());
@@ -72,30 +72,12 @@
 
   for (const auto& test_case : test_cases) {
     SCOPED_TRACE(testing::Message() << "URL: " << test_case.url);
-    EXPECT_EQ(enabled() && test_case.expect,
-              OriginPolicyThrottle::ShouldRequestOriginPolicy(
-                  GURL(test_case.url), nullptr));
+    EXPECT_EQ(
+        enabled() && test_case.expect,
+        OriginPolicyThrottle::ShouldRequestOriginPolicy(GURL(test_case.url)));
   }
 }
 
-TEST_P(OriginPolicyThrottleTest, ShouldRequestLastKnownVersion) {
-  if (!enabled())
-    return;
-
-  GURL url("https://example.org/bla");
-  EXPECT_TRUE(OriginPolicyThrottle::ShouldRequestOriginPolicy(url, nullptr));
-
-  std::string version;
-
-  OriginPolicyThrottle::ShouldRequestOriginPolicy(url, &version);
-  EXPECT_EQ(version, "0");
-
-  OriginPolicyThrottle::GetKnownVersionsForTesting()[url::Origin::Create(url)] =
-      "abcd";
-  OriginPolicyThrottle::ShouldRequestOriginPolicy(url, &version);
-  EXPECT_EQ(version, "abcd");
-}
-
 TEST_P(OriginPolicyThrottleTest, MaybeCreateThrottleFor) {
   CreateHandleFor(GURL("https://example.org/bla"));
   EXPECT_EQ(enabled(),
@@ -158,13 +140,9 @@
   OriginPolicyThrottle::GetKnownVersionsForTesting()[url::Origin::Create(url)] =
       "abcd";
 
-  std::string version;
-  OriginPolicyThrottle::ShouldRequestOriginPolicy(url, &version);
-  EXPECT_EQ(version, "abcd");
-
   OriginPolicyThrottle::AddExceptionFor(url);
-  OriginPolicyThrottle::ShouldRequestOriginPolicy(url, &version);
-  EXPECT_EQ(version, "0");
+  EXPECT_TRUE(
+      OriginPolicyThrottle::IsExemptedForTesting(url::Origin::Create(url)));
 }
 
 TEST_P(OriginPolicyThrottleTest, AddExceptionEndToEnd) {
@@ -196,10 +174,8 @@
   EXPECT_FALSE(navigation->IsDeferred());
 
   // Also check that the header policy did not overwrite the exemption:
-  std::string version;
-  OriginPolicyThrottle::ShouldRequestOriginPolicy(
-      GURL("https://example.org/bla"), &version);
-  EXPECT_EQ(version, "0");
+  EXPECT_TRUE(OriginPolicyThrottle::IsExemptedForTesting(
+      url::Origin::Create(GURL("https://example.org/bla"))));
 }
 
 TEST(OriginPolicyThrottleTest, ParseHeaders) {
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 58568a2..605e95d 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1836,7 +1836,8 @@
         std::move(pending_navigate_->begin_navigation_params),
         std::move(pending_navigate_->blob_url_loader_factory),
         std::move(pending_navigate_->navigation_client),
-        std::move(pending_navigate_->navigation_initiator));
+        std::move(pending_navigate_->navigation_initiator),
+        EnsurePrefetchedSignedExchangeCache());
     pending_navigate_.reset();
   }
 }
@@ -3961,7 +3962,7 @@
   frame_tree_node()->navigator()->OnBeginNavigation(
       frame_tree_node(), validated_params, std::move(begin_params),
       std::move(blob_url_loader_factory), std::move(navigation_client),
-      std::move(navigation_initiator));
+      std::move(navigation_initiator), EnsurePrefetchedSignedExchangeCache());
 }
 
 void RenderFrameHostImpl::SubresourceResponseStarted(
@@ -6382,8 +6383,53 @@
       navigation_request->commit_params().navigation_token !=
           validated_params->navigation_token) {
     navigation_request.reset();
-    // TODO(clamy): We should kill the renderer in all cases where we expect to
-    // have a NavigationRequest matching the commit URL.
+  }
+
+  if (!navigation_request) {
+    // A matching NavigationRequest should have been found, unless in a few very
+    // specific cases. Check if this is one of those cases.
+    bool is_commit_allowed_to_proceed = false;
+
+    //  1) This was a renderer-initiated navigation to a URL that doesn't need
+    //  to be handled by the network stack (eg. about:blank).
+    is_commit_allowed_to_proceed |=
+        !IsURLHandledByNetworkStack(validated_params->url);
+
+    //  2) This was a same-document navigation.
+    //  TODO(clamy): We should enforce having a request on browser-initiated
+    //  same-document navigations.
+    is_commit_allowed_to_proceed |= is_same_document_navigation;
+
+    //  3) This was a navigation to a subframe in an MHTML archive.
+    is_commit_allowed_to_proceed |=
+        GetParent() && frame_tree_node_->frame_tree()
+                           ->root()
+                           ->current_frame_host()
+                           ->is_mhtml_document();
+
+    //  4) Transient interstitial page commits will not have a matching
+    //  NavigationRequest.
+    //  TODO(clamy): Enforce having a NavigationRequest for data URLs when
+    //  committed interstitials have launched or interstitials create
+    //  NavigationRequests.
+    is_commit_allowed_to_proceed |= !!delegate_->GetAsInterstitialPage();
+
+    //  5) Error pages implementations in Chrome can commit twice.
+    //  TODO(clamy): Fix this.
+    is_commit_allowed_to_proceed |= validated_params->url_is_unreachable;
+
+    //  6) Special case for DOMSerializerBrowsertests which are implemented
+    //  entirely renderer-side and unlike normal RenderView based tests load
+    //  file URLs instead of data URLs.
+    //  TODO(clamy): Rework the tests to remove this exception.
+    is_commit_allowed_to_proceed |= validated_params->url.SchemeIsFile();
+
+    if (!is_commit_allowed_to_proceed) {
+      bad_message::ReceivedBadMessage(
+          GetProcess(),
+          bad_message::RFH_NO_MATCHING_NAVIGATION_REQUEST_ON_COMMIT);
+      return false;
+    }
   }
 
   if (!ValidateDidCommitParams(navigation_request.get(), validated_params,
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index e3c8d07..792fb596 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1053,6 +1053,8 @@
                            NavigationCommitInIframePendingDeletionAB);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            NavigationCommitInIframePendingDeletionABC);
+  FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
+                           CommittedOriginIncompatibleWithOriginLock);
 
   class DroppedInterfaceRequestLogger;
 
diff --git a/content/browser/loader/navigation_url_loader.cc b/content/browser/loader/navigation_url_loader.cc
index f6c965e4..04d7ffb 100644
--- a/content/browser/loader/navigation_url_loader.cc
+++ b/content/browser/loader/navigation_url_loader.cc
@@ -11,6 +11,7 @@
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader_factory.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/public/browser/navigation_ui_data.h"
 #include "services/network/public/cpp/features.h"
 
@@ -25,6 +26,8 @@
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     ServiceWorkerNavigationHandle* service_worker_handle,
     AppCacheNavigationHandle* appcache_handle,
+    scoped_refptr<PrefetchedSignedExchangeCache>
+        prefetched_signed_exchange_cache,
     NavigationURLLoaderDelegate* delegate) {
   if (g_loader_factory) {
     return g_loader_factory->CreateLoader(
@@ -34,7 +37,8 @@
   return std::make_unique<NavigationURLLoaderImpl>(
       resource_context, storage_partition, std::move(request_info),
       std::move(navigation_ui_data), service_worker_handle, appcache_handle,
-      delegate, std::vector<std::unique_ptr<NavigationLoaderInterceptor>>());
+      std::move(prefetched_signed_exchange_cache), delegate,
+      std::vector<std::unique_ptr<NavigationLoaderInterceptor>>());
 }
 
 void NavigationURLLoader::SetFactoryForTesting(
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h
index f1a36f6..2cc2e00d 100644
--- a/content/browser/loader/navigation_url_loader.h
+++ b/content/browser/loader/navigation_url_loader.h
@@ -24,6 +24,7 @@
 class NavigationUIData;
 class NavigationURLLoaderDelegate;
 class NavigationURLLoaderFactory;
+class PrefetchedSignedExchangeCache;
 class ResourceContext;
 class ServiceWorkerNavigationHandle;
 class StoragePartition;
@@ -50,6 +51,8 @@
       std::unique_ptr<NavigationUIData> navigation_ui_data,
       ServiceWorkerNavigationHandle* service_worker_handle,
       AppCacheNavigationHandle* appcache_handle,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache,
       NavigationURLLoaderDelegate* delegate);
 
   // For testing purposes; sets the factory for use in testing.
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 81ac6b56..51ee832 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -32,6 +32,7 @@
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/browser/loader/prefetch_url_loader_service.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_request_info_impl.h"
 #include "content/browser/navigation_subresource_loader_params.h"
@@ -468,6 +469,8 @@
       storage::FileSystemContext* upload_file_system_context,
       ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core,
       AppCacheNavigationHandleCore* appcache_handle_core,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache,
       scoped_refptr<SignedExchangePrefetchMetricRecorder>
           signed_exchange_prefetch_metric_recorder,
       std::unique_ptr<NavigationRequestInfo> request_info,
@@ -498,6 +501,7 @@
 
     StartInternal(request_info_.get(), service_worker_navigation_handle_core,
                   nullptr /* appcache_handle_core */,
+                  std::move(prefetched_signed_exchange_cache),
                   std::move(signed_exchange_prefetch_metric_recorder),
                   {} /* factory_for_webui */, url_request_context_getter,
                   std::move(accept_langs));
@@ -508,6 +512,8 @@
           network_loader_factory_info,
       ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core,
       AppCacheNavigationHandleCore* appcache_handle_core,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache,
       scoped_refptr<SignedExchangePrefetchMetricRecorder>
           signed_exchange_prefetch_metric_recorder,
       std::unique_ptr<NavigationRequestInfo> request_info,
@@ -552,12 +558,12 @@
                              resource_context_, &blob_handles_);
     }
 
-    StartInternal(request_info.get(), service_worker_navigation_handle_core,
-                  appcache_handle_core,
-                  std::move(signed_exchange_prefetch_metric_recorder),
-                  std::move(factory_for_webui),
-                  nullptr /* url_request_context_getter */,
-                  std::move(accept_langs));
+    StartInternal(
+        request_info.get(), service_worker_navigation_handle_core,
+        appcache_handle_core, std::move(prefetched_signed_exchange_cache),
+        std::move(signed_exchange_prefetch_metric_recorder),
+        std::move(factory_for_webui), nullptr /* url_request_context_getter */,
+        std::move(accept_langs));
   }
 
   // Common setup routines, called by both StartWithoutNetworkService() and
@@ -572,6 +578,8 @@
       NavigationRequestInfo* request_info,
       ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core,
       AppCacheNavigationHandleCore* appcache_handle_core,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache,
       scoped_refptr<SignedExchangePrefetchMetricRecorder>
           signed_exchange_prefetch_metric_recorder,
       network::mojom::URLLoaderFactoryPtrInfo factory_for_webui,
@@ -613,6 +621,19 @@
       return;
     }
 
+    if (prefetched_signed_exchange_cache) {
+      DCHECK(base::FeatureList::IsEnabled(
+          features::kSignedExchangeSubresourcePrefetch));
+      std::unique_ptr<NavigationLoaderInterceptor>
+          prefetched_signed_exchange_interceptor =
+              prefetched_signed_exchange_cache->MaybeCreateInterceptor(
+                  request_info->common_params.url);
+      if (prefetched_signed_exchange_interceptor) {
+        interceptors_.push_back(
+            std::move(prefetched_signed_exchange_interceptor));
+      }
+    }
+
     // Set-up an interceptor for service workers.
     if (service_worker_navigation_handle_core) {
       std::unique_ptr<NavigationLoaderInterceptor> service_worker_interceptor =
@@ -1460,6 +1481,8 @@
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     ServiceWorkerNavigationHandle* service_worker_navigation_handle,
     AppCacheNavigationHandle* appcache_handle,
+    scoped_refptr<PrefetchedSignedExchangeCache>
+        prefetched_signed_exchange_cache,
     NavigationURLLoaderDelegate* delegate,
     std::vector<std::unique_ptr<NavigationLoaderInterceptor>>
         initial_interceptors)
@@ -1515,6 +1538,7 @@
             base::Unretained(storage_partition->GetFileSystemContext()),
             base::Unretained(service_worker_navigation_handle_core),
             base::Unretained(appcache_handle_core),
+            std::move(prefetched_signed_exchange_cache),
             base::RetainedRef(signed_exchange_prefetch_metric_recorder),
             std::move(request_info), std::move(navigation_ui_data),
             std::move(accept_langs)));
@@ -1640,6 +1664,7 @@
                      std::move(network_factory_info),
                      service_worker_navigation_handle_core,
                      appcache_handle_core,
+                     std::move(prefetched_signed_exchange_cache),
                      std::move(signed_exchange_prefetch_metric_recorder),
                      std::move(request_info), std::move(navigation_ui_data),
                      std::move(factory_for_webui), frame_tree_node_id,
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h
index d9431f0..5bbcb18 100644
--- a/content/browser/loader/navigation_url_loader_impl.h
+++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -25,6 +25,7 @@
 
 class NavigationData;
 class NavigationLoaderInterceptor;
+class PrefetchedSignedExchangeCache;
 class ResourceContext;
 class StoragePartition;
 class StoragePartitionImpl;
@@ -41,6 +42,8 @@
       std::unique_ptr<NavigationUIData> navigation_ui_data,
       ServiceWorkerNavigationHandle* service_worker_handle,
       AppCacheNavigationHandle* appcache_handle,
+      scoped_refptr<PrefetchedSignedExchangeCache>
+          prefetched_signed_exchange_cache,
       NavigationURLLoaderDelegate* delegate,
       std::vector<std::unique_ptr<NavigationLoaderInterceptor>>
           initial_interceptors);
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index edf1552b..fb85a7c9 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/common/navigation_params.h"
 #include "content/common/navigation_params.mojom.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
@@ -202,7 +203,8 @@
         BrowserContext::GetDefaultStoragePartition(browser_context_.get()),
         std::move(request_info), nullptr /* navigation_ui_data */,
         nullptr /* service_worker_handle */, nullptr /* appcache_handle */,
-        delegate, std::move(interceptors));
+        nullptr /* prefetched_signed_exchange_cache */, delegate,
+        std::move(interceptors));
   }
 
   // Requests |redirect_url|, which must return a HTTP 3xx redirect. It's also
diff --git a/content/browser/loader/navigation_url_loader_unittest.cc b/content/browser/loader/navigation_url_loader_unittest.cc
index 69ff3724..fc4e42d0 100644
--- a/content/browser/loader/navigation_url_loader_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/unguessable_token.h"
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/loader/navigation_url_loader.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/test_resource_handler.h"
 #include "content/browser/loader_delegate_impl.h"
@@ -102,7 +103,7 @@
     return NavigationURLLoader::Create(
         browser_context_->GetResourceContext(),
         BrowserContext::GetDefaultStoragePartition(browser_context_.get()),
-        std::move(request_info), nullptr, nullptr, nullptr, delegate);
+        std::move(request_info), nullptr, nullptr, nullptr, nullptr, delegate);
   }
 
   // Helper function for fetching the body of a URL to a string.
diff --git a/content/browser/loader/prefetched_signed_exchange_cache.cc b/content/browser/loader/prefetched_signed_exchange_cache.cc
index 300537d..51e630e5 100644
--- a/content/browser/loader/prefetched_signed_exchange_cache.cc
+++ b/content/browser/loader/prefetched_signed_exchange_cache.cc
@@ -5,8 +5,27 @@
 #include "content/browser/loader/prefetched_signed_exchange_cache.h"
 
 #include "base/feature_list.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/browser/navigation_subresource_loader_params.h"
+#include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_features.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/http/http_util.h"
+#include "net/url_request/redirect_util.h"
+#include "services/network/public/cpp/constants.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
+#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_loader.mojom.h"
+#include "storage/browser/blob/blob_data_handle.h"
+#include "storage/browser/blob/blob_impl.h"
+#include "storage/browser/blob/mojo_blob_reader.h"
 
 namespace content {
 
@@ -16,6 +35,255 @@
 // prevent OOM crash of the browser process.
 constexpr size_t kMaxEntrySize = 100u;
 
+void UpdateRequestResponseStartTime(
+    network::ResourceResponseHead* response_head) {
+  const base::TimeTicks now_ticks = base::TimeTicks::Now();
+  const base::Time now = base::Time::Now();
+  response_head->request_start = now_ticks;
+  response_head->response_start = now_ticks;
+  response_head->load_timing.request_start_time = now;
+  response_head->load_timing.request_start = now_ticks;
+}
+
+// A utility subclass of MojoBlobReader::Delegate that calls the passed callback
+// in OnComplete().
+class MojoBlobReaderDelegate : public storage::MojoBlobReader::Delegate {
+ public:
+  explicit MojoBlobReaderDelegate(base::OnceCallback<void(net::Error)> callback)
+      : callback_(std::move(callback)) {}
+
+ private:
+  // storage::MojoBlobReader::Delegate
+  RequestSideData DidCalculateSize(uint64_t total_size,
+                                   uint64_t content_size) override {
+    return DONT_REQUEST_SIDE_DATA;
+  }
+
+  void OnComplete(net::Error result, uint64_t total_written_bytes) override {
+    std::move(callback_).Run(result);
+  }
+
+  base::OnceCallback<void(net::Error)> callback_;
+};
+
+// A URLLoader which returns a synthesized redirect response for signed
+// exchange's outer URL request.
+class RedirectResponseURLLoader : public network::mojom::URLLoader {
+ public:
+  RedirectResponseURLLoader(const network::ResourceRequest& url_request,
+                            const GURL& inner_url,
+                            const network::ResourceResponseHead& outer_response,
+                            network::mojom::URLLoaderClientPtr client)
+      : client_(std::move(client)) {
+    network::ResourceResponseHead response_head =
+        signed_exchange_utils::CreateRedirectResponseHead(
+            outer_response, false /* is_fallback_redirect */);
+    UpdateRequestResponseStartTime(&response_head);
+    client_->OnReceiveRedirect(signed_exchange_utils::CreateRedirectInfo(
+                                   inner_url, url_request, outer_response,
+                                   false /* is_fallback_redirect */),
+                               response_head);
+  }
+  ~RedirectResponseURLLoader() override {}
+
+ private:
+  // network::mojom::URLLoader overrides:
+  void FollowRedirect(const std::vector<std::string>& removed_headers,
+                      const net::HttpRequestHeaders& modified_headers,
+                      const base::Optional<GURL>& new_url) override {
+    NOTREACHED();
+  }
+  void ProceedWithResponse() override { NOTREACHED(); }
+  void SetPriority(net::RequestPriority priority,
+                   int intra_priority_value) override {
+    // There is nothing to do, because this class just calls OnReceiveRedirect.
+  }
+  void PauseReadingBodyFromNet() override {
+    // There is nothing to do, because we don't fetch the resource from the
+    // network.
+  }
+  void ResumeReadingBodyFromNet() override {
+    // There is nothing to do, because we don't fetch the resource from the
+    // network.
+  }
+
+  network::mojom::URLLoaderClientPtr client_;
+
+  DISALLOW_COPY_AND_ASSIGN(RedirectResponseURLLoader);
+};
+
+// A URLLoader which returns the inner response of signed exchange.
+class InnerResponseURLLoader : public network::mojom::URLLoader {
+ public:
+  InnerResponseURLLoader(
+      const network::ResourceResponseHead& inner_response,
+      std::unique_ptr<const storage::BlobDataHandle> blob_data_handle,
+      const network::URLLoaderCompletionStatus& completion_status,
+      network::mojom::URLLoaderClientPtr client)
+      : blob_data_handle_(std::move(blob_data_handle)),
+        completion_status_(completion_status),
+        client_(std::move(client)),
+        weak_factory_(this) {
+    network::ResourceResponseHead response = inner_response;
+    UpdateRequestResponseStartTime(&response);
+    response.encoded_data_length = 0;
+    client_->OnReceiveResponse(response);
+    // When Network Service is not enabled, we need to wait ProceedWithResponse.
+    // See https://crbug.com/791049.
+    if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+      SendResponseBody();
+  }
+  ~InnerResponseURLLoader() override {}
+
+ private:
+  // network::mojom::URLLoader overrides:
+  void FollowRedirect(const std::vector<std::string>& removed_headers,
+                      const net::HttpRequestHeaders& modified_headers,
+                      const base::Optional<GURL>& new_url) override {
+    NOTREACHED();
+  }
+  void ProceedWithResponse() override {
+    DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
+    SendResponseBody();
+  }
+  void SetPriority(net::RequestPriority priority,
+                   int intra_priority_value) override {
+    // There is nothing to do, because there is no prioritization mechanism for
+    // reading a blob.
+  }
+  void PauseReadingBodyFromNet() override {
+    // There is nothing to do, because we don't fetch the resource from the
+    // network.
+  }
+  void ResumeReadingBodyFromNet() override {
+    // There is nothing to do, because we don't fetch the resource from the
+    // network.
+  }
+
+  void SendResponseBody() {
+    mojo::ScopedDataPipeProducerHandle pipe_producer_handle;
+    mojo::ScopedDataPipeConsumerHandle pipe_consumer_handle;
+    MojoCreateDataPipeOptions options;
+    options.struct_size = sizeof(MojoCreateDataPipeOptions);
+    options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
+    options.element_num_bytes = 1;
+    options.capacity_num_bytes = network::kDataPipeDefaultAllocationSize;
+    MojoResult rv = mojo::CreateDataPipe(&options, &pipe_producer_handle,
+                                         &pipe_consumer_handle);
+    if (rv != MOJO_RESULT_OK) {
+      client_->OnComplete(
+          network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
+      return;
+    }
+
+    storage::MojoBlobReader::Create(
+        blob_data_handle_.get(), net::HttpByteRange(),
+        std::make_unique<MojoBlobReaderDelegate>(
+            base::BindOnce(&InnerResponseURLLoader::BlobReaderComplete,
+                           weak_factory_.GetWeakPtr())),
+        std::move(pipe_producer_handle));
+
+    client_->OnStartLoadingResponseBody(std::move(pipe_consumer_handle));
+  }
+
+  void BlobReaderComplete(net::Error result) {
+    network::URLLoaderCompletionStatus status;
+    if (result == net::OK) {
+      status = completion_status_;
+      status.exists_in_cache = true;
+      status.completion_time = base::TimeTicks::Now();
+      status.encoded_data_length = 0;
+    } else {
+      status = network::URLLoaderCompletionStatus(status);
+    }
+    client_->OnComplete(status);
+  }
+
+  std::unique_ptr<const storage::BlobDataHandle> blob_data_handle_;
+  const network::URLLoaderCompletionStatus completion_status_;
+  network::mojom::URLLoaderClientPtr client_;
+
+  base::WeakPtrFactory<InnerResponseURLLoader> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(InnerResponseURLLoader);
+};
+
+// A NavigationLoaderInterceptor which handles a request which matches the
+// prefetched signed exchange that has been stored to a
+// PrefetchedSignedExchangeCache.
+class PrefetchedNavigationLoaderInterceptor
+    : public NavigationLoaderInterceptor {
+ public:
+  explicit PrefetchedNavigationLoaderInterceptor(
+      std::unique_ptr<const PrefetchedSignedExchangeCache::Entry> exchange)
+      : exchange_(std::move(exchange)), weak_factory_(this) {}
+
+  ~PrefetchedNavigationLoaderInterceptor() override {}
+
+  void MaybeCreateLoader(
+      const network::ResourceRequest& tentative_resource_request,
+      ResourceContext* resource_context,
+      LoaderCallback callback,
+      FallbackCallback fallback_callback) override {
+    // Currently we just check the URL matching. But we should check the Vary
+    // header (eg: HttpVaryData::MatchesRequest()) and Cache-Control header.
+    // And also we shuold check the expires parameter of the signed exchange's
+    // signature. TODO(crbug.com/935267): Implement these checking logic.
+    if (!outer_request_handled_ &&
+        tentative_resource_request.url == exchange_->outer_url()) {
+      outer_request_handled_ = true;
+      std::move(callback).Run(base::BindOnce(
+          &PrefetchedNavigationLoaderInterceptor::StartRedirectResponse,
+          weak_factory_.GetWeakPtr()));
+      return;
+    }
+    if (tentative_resource_request.url == exchange_->inner_url()) {
+      std::move(callback).Run(base::BindOnce(
+          &PrefetchedNavigationLoaderInterceptor::StartInnerResponse,
+          weak_factory_.GetWeakPtr()));
+      return;
+    }
+    NOTREACHED();
+  }
+
+  base::Optional<SubresourceLoaderParams> MaybeCreateSubresourceLoaderParams()
+      override {
+    // TODO(crbug.com/935267): Implement this to pass the prefetched signed
+    // exchanges of subresources to the renderer process.
+    return base::nullopt;
+  }
+
+ private:
+  void StartRedirectResponse(const network::ResourceRequest& resource_request,
+                             network::mojom::URLLoaderRequest request,
+                             network::mojom::URLLoaderClientPtr client) {
+    mojo::MakeStrongBinding(
+        std::make_unique<RedirectResponseURLLoader>(
+            resource_request, exchange_->inner_url(),
+            *exchange_->outer_response(), std::move(client)),
+        std::move(request));
+  }
+  void StartInnerResponse(const network::ResourceRequest& resource_request,
+                          network::mojom::URLLoaderRequest request,
+                          network::mojom::URLLoaderClientPtr client) {
+    mojo::MakeStrongBinding(
+        std::make_unique<InnerResponseURLLoader>(
+            *exchange_->inner_response(),
+            std::make_unique<const storage::BlobDataHandle>(
+                *exchange_->blob_data_handle()),
+            *exchange_->completion_status(), std::move(client)),
+        std::move(request));
+  }
+
+  std::unique_ptr<const PrefetchedSignedExchangeCache::Entry> exchange_;
+
+  bool outer_request_handled_ = false;
+
+  base::WeakPtrFactory<PrefetchedNavigationLoaderInterceptor> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrefetchedNavigationLoaderInterceptor);
+};
+
 }  // namespace
 
 PrefetchedSignedExchangeCache::Entry::Entry() = default;
@@ -49,6 +317,33 @@
   blob_data_handle_ = std::move(blob_data_handle);
 }
 
+std::unique_ptr<const PrefetchedSignedExchangeCache::Entry>
+PrefetchedSignedExchangeCache::Entry::Clone() const {
+  DCHECK(outer_url().is_valid());
+  DCHECK(outer_response());
+  DCHECK(header_integrity());
+  DCHECK(inner_url().is_valid());
+  DCHECK(inner_response());
+  DCHECK(completion_status());
+  DCHECK(blob_data_handle());
+
+  std::unique_ptr<Entry> clone = std::make_unique<Entry>();
+  clone->SetOuterUrl(outer_url_);
+  clone->SetOuterResponse(
+      std::make_unique<const network::ResourceResponseHead>(*outer_response_));
+  clone->SetHeaderIntegrity(
+      std::make_unique<const net::SHA256HashValue>(*header_integrity_));
+  clone->SetInnerUrl(inner_url_);
+  clone->SetInnerResponse(
+      std::make_unique<const network::ResourceResponseHead>(*inner_response_));
+  clone->SetCompletionStatus(
+      std::make_unique<const network::URLLoaderCompletionStatus>(
+          *completion_status_));
+  clone->SetBlobDataHandle(
+      std::make_unique<const storage::BlobDataHandle>(*blob_data_handle_));
+  return clone;
+}
+
 PrefetchedSignedExchangeCache::PrefetchedSignedExchangeCache() {
   DCHECK(base::FeatureList::IsEnabled(
       features::kSignedExchangeSubresourcePrefetch));
@@ -72,4 +367,14 @@
   exchanges_[outer_url] = std::move(cached_exchange);
 }
 
+std::unique_ptr<NavigationLoaderInterceptor>
+PrefetchedSignedExchangeCache::MaybeCreateInterceptor(const GURL& outer_url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  const auto it = exchanges_.find(outer_url);
+  if (it == exchanges_.end())
+    return nullptr;
+  return std::make_unique<PrefetchedNavigationLoaderInterceptor>(
+      it->second->Clone());
+}
+
 }  // namespace content
diff --git a/content/browser/loader/prefetched_signed_exchange_cache.h b/content/browser/loader/prefetched_signed_exchange_cache.h
index f0feafb..7f9e1ec 100644
--- a/content/browser/loader/prefetched_signed_exchange_cache.h
+++ b/content/browser/loader/prefetched_signed_exchange_cache.h
@@ -30,6 +30,8 @@
 
 namespace content {
 
+class NavigationLoaderInterceptor;
+
 // PrefetchedSignedExchangeCache keeps prefetched and verified signed
 // exchanges.
 class CONTENT_EXPORT PrefetchedSignedExchangeCache
@@ -77,6 +79,8 @@
     void SetBlobDataHandle(
         std::unique_ptr<const storage::BlobDataHandle> blob_data_handle);
 
+    std::unique_ptr<const Entry> Clone() const;
+
    private:
     GURL outer_url_;
     std::unique_ptr<const network::ResourceResponseHead> outer_response_;
@@ -94,6 +98,9 @@
 
   void Store(std::unique_ptr<const Entry> cached_exchange);
 
+  std::unique_ptr<NavigationLoaderInterceptor> MaybeCreateInterceptor(
+      const GURL& outer_url);
+
  private:
   friend class base::RefCountedThreadSafe<PrefetchedSignedExchangeCache>;
   friend class SignedExchangeSubresourcePrefetchBrowserTest;
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc
index d30f6df9..d8c1dcc 100644
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -30,6 +30,7 @@
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/loader/detachable_resource_handler.h"
 #include "content/browser/loader/navigation_url_loader.h"
+#include "content/browser/loader/prefetched_signed_exchange_cache.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/loader/resource_loader.h"
 #include "content/browser/loader/resource_message_filter.h"
@@ -845,7 +846,8 @@
         NavigationURLLoader::Create(
             browser_context_->GetResourceContext(),
             BrowserContext::GetDefaultStoragePartition(browser_context_.get()),
-            std::move(request_info), nullptr, nullptr, nullptr, &delegate);
+            std::move(request_info), nullptr, nullptr, nullptr, nullptr,
+            &delegate);
 
     // The navigation should fail with the expected error code.
     delegate.WaitForRequestFailed();
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index 477d3fd..ea0d8eb7 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -29,7 +29,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_switches.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom.h"
@@ -104,25 +104,27 @@
   return "";
 }
 
-// Returns whether |sender_info| contains a valid application server key, that
-// is, a NIST P-256 public key in uncompressed format.
-bool IsApplicationServerKey(const std::string& sender_info) {
-  return sender_info.size() == 65 && sender_info[0] == 0x04;
+// Returns whether |application_server_key| contains a valid application server
+// key, that is, a NIST P-256 public key in uncompressed format.
+bool IsApplicationServerKey(const std::string& application_server_key) {
+  return application_server_key.size() == 65 &&
+         application_server_key[0] == 0x04;
 }
 
-// Returns sender_info if non-empty, otherwise checks if stored_sender_id
-// may be used as a fallback and if so, returns stored_sender_id instead.
+// Returns application_server_key if non-empty, otherwise checks if
+// stored_sender_id may be used as a fallback and if so, returns
+// stored_sender_id instead.
 //
 // This is in order to support the legacy way of subscribing from a service
 // worker (first subscribe from the document using a gcm_sender_id set in the
 // manifest, and then subscribe from the service worker with no key).
 //
-// An empty string will be returned if sender_info is empty and the fallback
-// is not a numeric gcm sender id.
-std::string FixSenderInfo(const std::string& sender_info,
+// An empty string will be returned if application_server_key is empty and the
+// fallback is not a numeric gcm sender id.
+std::string FixSenderInfo(const std::string& application_server_key,
                           const std::string& stored_sender_id) {
-  if (!sender_info.empty())
-    return sender_info;
+  if (!application_server_key.empty())
+    return application_server_key;
   if (base::ContainsOnlyChars(stored_sender_id, "0123456789"))
     return stored_sender_id;
   return std::string();
@@ -139,7 +141,7 @@
   GURL requesting_origin;
   int64_t service_worker_registration_id;
   base::Optional<std::string> existing_subscription_id;
-  blink::PushSubscriptionOptionsParams options;
+  blink::WebPushSubscriptionOptions options;
   SubscribeCallback callback;
 
   // The following member should only be read if FromDocument() is true.
@@ -175,7 +177,7 @@
                                      const GURL& origin,
                                      int64_t service_worker_registration_id,
                                      const GURL& endpoint,
-                                     const std::string& sender_info,
+                                     const std::string& application_server_key,
                                      bool is_valid,
                                      const std::vector<uint8_t>& p256dh,
                                      const std::vector<uint8_t>& auth);
@@ -302,7 +304,7 @@
 void PushMessagingManager::Subscribe(
     int32_t render_frame_id,
     int64_t service_worker_registration_id,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     SubscribeCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -329,7 +331,7 @@
   }
   data.requesting_origin = service_worker_registration->scope().GetOrigin();
 
-  DCHECK(!(data.options.sender_info.empty() && data.FromDocument()));
+  DCHECK(!(data.options.application_server_key.empty() && data.FromDocument()));
 
   int64_t registration_id = data.service_worker_registration_id;
   service_worker_context_->GetRegistrationUserData(
@@ -354,7 +356,7 @@
     const std::string& stored_sender_id = subscription_id_and_sender_id[1];
 
     std::string fixed_sender_id =
-        FixSenderInfo(data.options.sender_info, stored_sender_id);
+        FixSenderInfo(data.options.application_server_key, stored_sender_id);
     if (fixed_sender_id.empty()) {
       SendSubscriptionError(std::move(data),
                             blink::mojom::PushRegistrationStatus::NO_SENDER_ID);
@@ -375,14 +377,14 @@
   // blink::ServiceWorkerStatusCode::kErrorNotFound by rejecting
   // the subscription algorithm instead of trying to subscribe.
 
-  if (!data.options.sender_info.empty()) {
+  if (!data.options.application_server_key.empty()) {
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&Core::RegisterOnUI, base::Unretained(ui_core_.get()),
                        std::move(data)));
   } else {
-    // No |sender_info| was provided by the developer. Fall back to checking
-    // whether a previous subscription did identify a sender.
+    // No |application_server_key| was provided by the developer. Fall back to
+    // checking whether a previous subscription did identify a sender.
     int64_t registration_id = data.service_worker_registration_id;
     service_worker_context_->GetRegistrationUserData(
         registration_id, {kPushSenderIdServiceWorkerKey},
@@ -403,15 +405,15 @@
   }
   DCHECK_EQ(1u, stored_sender_id.size());
   // We should only be here because no sender info was supplied to subscribe().
-  DCHECK(data.options.sender_info.empty());
+  DCHECK(data.options.application_server_key.empty());
   std::string fixed_sender_id =
-      FixSenderInfo(data.options.sender_info, stored_sender_id[0]);
+      FixSenderInfo(data.options.application_server_key, stored_sender_id[0]);
   if (fixed_sender_id.empty()) {
     SendSubscriptionError(std::move(data),
                           blink::mojom::PushRegistrationStatus::NO_SENDER_ID);
     return;
   }
-  data.options.sender_info = fixed_sender_id;
+  data.options.application_server_key = fixed_sender_id;
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&Core::RegisterOnUI, base::Unretained(ui_core_.get()),
@@ -474,7 +476,7 @@
 
   int64_t registration_id = data.service_worker_registration_id;
   GURL requesting_origin = data.requesting_origin;
-  blink::PushSubscriptionOptionsParams options = data.options;
+  blink::WebPushSubscriptionOptions options = data.options;
   int render_frame_id = data.render_frame_id;
   if (data.FromDocument()) {
     push_service->SubscribeFromDocument(
@@ -548,12 +550,12 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   GURL requesting_origin = data.requesting_origin;
   int64_t registration_id = data.service_worker_registration_id;
-  std::string sender_info = data.options.sender_info;
+  std::string application_server_key = data.options.application_server_key;
 
   service_worker_context_->StoreRegistrationUserData(
       registration_id, requesting_origin,
       {{kPushRegistrationIdServiceWorkerKey, push_subscription_id},
-       {kPushSenderIdServiceWorkerKey, sender_info}},
+       {kPushSenderIdServiceWorkerKey, application_server_key}},
       base::BindOnce(&PushMessagingManager::DidPersistRegistrationOnIO,
                      weak_factory_io_to_io_.GetWeakPtr(), std::move(data),
                      push_subscription_id, p256dh, auth, status));
@@ -605,7 +607,8 @@
   }
 
   const GURL endpoint = CreateEndpoint(
-      IsApplicationServerKey(data.options.sender_info), push_subscription_id);
+      IsApplicationServerKey(data.options.application_server_key),
+      push_subscription_id);
 
   std::move(data.callback).Run(status, endpoint, data.options, p256dh, auth);
 
@@ -749,17 +752,19 @@
 void PushMessagingManager::DidGetSubscription(
     GetSubscriptionCallback callback,
     int64_t service_worker_registration_id,
-    const std::vector<std::string>& push_subscription_id_and_sender_info,
+    const std::vector<std::string>&
+        push_subscription_id_and_application_server_key,
     blink::ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   blink::mojom::PushGetRegistrationStatus get_status =
       blink::mojom::PushGetRegistrationStatus::STORAGE_ERROR;
   switch (service_worker_status) {
     case blink::ServiceWorkerStatusCode::kOk: {
-      DCHECK_EQ(2u, push_subscription_id_and_sender_info.size());
+      DCHECK_EQ(2u, push_subscription_id_and_application_server_key.size());
       const std::string& push_subscription_id =
-          push_subscription_id_and_sender_info[0];
-      const std::string& sender_info = push_subscription_id_and_sender_info[1];
+          push_subscription_id_and_application_server_key[0];
+      const std::string& application_server_key =
+          push_subscription_id_and_application_server_key[1];
 
       if (!service_available_) {
         // Return not found in incognito mode, so websites can't detect it.
@@ -782,7 +787,8 @@
 
       const GURL origin = registration->scope().GetOrigin();
 
-      const bool uses_standard_protocol = IsApplicationServerKey(sender_info);
+      const bool uses_standard_protocol =
+          IsApplicationServerKey(application_server_key);
       const GURL endpoint =
           CreateEndpoint(uses_standard_protocol, push_subscription_id);
 
@@ -790,12 +796,12 @@
           FROM_HERE, {BrowserThread::UI},
           base::BindOnce(&Core::GetSubscriptionInfoOnUI,
                          base::Unretained(ui_core_.get()), origin,
-                         service_worker_registration_id, sender_info,
+                         service_worker_registration_id, application_server_key,
                          push_subscription_id,
                          base::Bind(&Core::GetSubscriptionDidGetInfoOnUI,
                                     ui_core_weak_ptr_, base::Passed(&callback),
                                     origin, service_worker_registration_id,
-                                    endpoint, sender_info)));
+                                    endpoint, application_server_key)));
 
       return;
     }
@@ -843,19 +849,19 @@
     const GURL& origin,
     int64_t service_worker_registration_id,
     const GURL& endpoint,
-    const std::string& sender_info,
+    const std::string& application_server_key,
     bool is_valid,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (is_valid) {
-    blink::PushSubscriptionOptionsParams options;
+    blink::WebPushSubscriptionOptions options;
     // Chrome rejects subscription requests with userVisibleOnly false, so it
     // must have been true. TODO(harkness): If Chrome starts accepting silent
     // push subscriptions with userVisibleOnly false, the bool will need to be
     // stored.
     options.user_visible_only = true;
-    options.sender_info = sender_info;
+    options.application_server_key = application_server_key;
 
     blink::mojom::PushGetRegistrationStatus status =
         blink::mojom::PushGetRegistrationStatus::SUCCESS;
@@ -891,7 +897,7 @@
     push_service->Unsubscribe(blink::mojom::PushUnregistrationReason::
                                   GET_SUBSCRIPTION_STORAGE_CORRUPT,
                               origin, service_worker_registration_id,
-                              sender_info,
+                              application_server_key,
                               base::Bind(&Core::GetSubscriptionDidUnsubscribe,
                                          weak_factory_ui_to_ui_.GetWeakPtr(),
                                          base::Passed(&callback), status));
diff --git a/content/browser/push_messaging/push_messaging_manager.h b/content/browser/push_messaging/push_messaging_manager.h
index 3491f100..ec6c0a55 100644
--- a/content/browser/push_messaging/push_messaging_manager.h
+++ b/content/browser/push_messaging/push_messaging_manager.h
@@ -26,7 +26,7 @@
 enum class PushUnregistrationStatus;
 }  // namespace mojom
 
-struct PushSubscriptionOptionsParams;
+struct WebPushSubscriptionOptions;
 }  // namespace blink
 
 namespace content {
@@ -47,7 +47,7 @@
   // blink::mojom::PushMessaging impl, run on IO thread.
   void Subscribe(int32_t render_frame_id,
                  int64_t service_worker_registration_id,
-                 const blink::PushSubscriptionOptionsParams& options,
+                 const blink::WebPushSubscriptionOptions& options,
                  bool user_gesture,
                  SubscribeCallback callback) override;
   void Unsubscribe(int64_t service_worker_registration_id,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 9b35d7f..cdda5ba0 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -14005,6 +14005,8 @@
                      std::string());
   EXPECT_TRUE(controller.GetPendingEntry());
   EXPECT_EQ(another_url, controller.GetPendingEntry()->GetURL());
+  NavigationRequest* navigation_request = root->navigation_request();
+  ASSERT_TRUE(navigation_request);
 
   RenderProcessHostKillWaiter kill_waiter(
       root->current_frame_host()->GetProcess());
@@ -14021,6 +14023,8 @@
   params->gesture = NavigationGestureAuto;
   params->method = "GET";
   params->page_state = PageState::CreateFromURL(start_url);
+  params->navigation_token =
+      root->navigation_request()->commit_params().navigation_token;
 
   // Use an origin mismatched with the origin lock.
   params->origin = url::Origin::Create(another_url);
@@ -14030,6 +14034,13 @@
   EXPECT_EQ(start_url.host(), policy->GetOriginLock(process_id).host());
   EXPECT_NE(another_url.host(), policy->GetOriginLock(process_id).host());
 
+  // Transfer the NavigationRequest ownership to the RenderFrameHost. The test
+  // for NavigationRequest match happens before the check of origin lock and
+  // needs to be successful.
+  root->TransferNavigationRequestOwnership(root->current_frame_host());
+  root->current_frame_host()->OnCrossDocumentCommitProcessed(
+      navigation_request, blink::mojom::CommitResult::Ok);
+
   // Simulate a commit IPC.
   service_manager::mojom::InterfaceProviderPtr interface_provider;
   blink::mojom::DocumentInterfaceBrokerPtrInfo
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index ba6fd90..3e112d9 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -56,6 +56,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "net/base/features.h"
 #include "net/base/net_errors.h"
 #include "net/cookies/canonical_cookie.h"
@@ -1256,10 +1257,14 @@
     // TODO(lazyboy): Fix.
     if (storage_origin.is_empty()) {
       IncrementTaskCountOnUI();
+      // TODO(crbug.com/960325): Sometimes SessionStorage fails to call its
+      // callback. Figure out why.
       ClearSessionStorageOnUIThread(
           base::WrapRefCounted(dom_storage_context),
           base::WrapRefCounted(special_storage_policy), origin_matcher,
-          perform_storage_cleanup, decrement_callback);
+          perform_storage_cleanup,
+          mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+              static_cast<base::OnceClosure>(decrement_callback)));
     }
   }
 
diff --git a/content/browser/web_package/signed_exchange_certificate_chain.cc b/content/browser/web_package/signed_exchange_certificate_chain.cc
index f869df9e..7237f87 100644
--- a/content/browser/web_package/signed_exchange_certificate_chain.cc
+++ b/content/browser/web_package/signed_exchange_certificate_chain.cc
@@ -179,10 +179,22 @@
 SignedExchangeCertificateChain::~SignedExchangeCertificateChain() = default;
 
 bool SignedExchangeCertificateChain::ShouldIgnoreErrors() const {
-  static base::NoDestructor<
-      SignedExchangeCertificateChain::IgnoreErrorsSPKIList>
-      instance(*base::CommandLine::ForCurrentProcess());
-  return instance->ShouldIgnoreErrors(cert_);
+  return IgnoreErrorsSPKIList::ShouldIgnoreErrors(cert_);
+}
+
+std::unique_ptr<SignedExchangeCertificateChain::IgnoreErrorsSPKIList>&
+SignedExchangeCertificateChain::IgnoreErrorsSPKIList::GetInstance() {
+  static base::NoDestructor<std::unique_ptr<IgnoreErrorsSPKIList>> instance(
+      std::make_unique<IgnoreErrorsSPKIList>(
+          *base::CommandLine::ForCurrentProcess()));
+  return *instance;
+}
+
+std::unique_ptr<SignedExchangeCertificateChain::IgnoreErrorsSPKIList>
+SignedExchangeCertificateChain::IgnoreErrorsSPKIList::SetInstanceForTesting(
+    std::unique_ptr<IgnoreErrorsSPKIList> p) {
+  GetInstance().swap(p);
+  return p;
 }
 
 SignedExchangeCertificateChain::IgnoreErrorsSPKIList::IgnoreErrorsSPKIList(
@@ -208,8 +220,15 @@
 SignedExchangeCertificateChain::IgnoreErrorsSPKIList::~IgnoreErrorsSPKIList() =
     default;
 
+// static
 bool SignedExchangeCertificateChain::IgnoreErrorsSPKIList::ShouldIgnoreErrors(
     scoped_refptr<net::X509Certificate> certificate) {
+  return GetInstance()->ShouldIgnoreErrorsInternal(certificate);
+}
+
+bool SignedExchangeCertificateChain::IgnoreErrorsSPKIList::
+    ShouldIgnoreErrorsInternal(
+        scoped_refptr<net::X509Certificate> certificate) {
   if (hash_set_.empty())
     return false;
 
diff --git a/content/browser/web_package/signed_exchange_certificate_chain.h b/content/browser/web_package/signed_exchange_certificate_chain.h
index 58f3acb..520f614 100644
--- a/content/browser/web_package/signed_exchange_certificate_chain.h
+++ b/content/browser/web_package/signed_exchange_certificate_chain.h
@@ -38,16 +38,27 @@
   // CONTENT_EXPORT since it is used from the unit test.
   class CONTENT_EXPORT IgnoreErrorsSPKIList {
    public:
+    static bool ShouldIgnoreErrors(
+        scoped_refptr<net::X509Certificate> certificate);
+
     explicit IgnoreErrorsSPKIList(const base::CommandLine& command_line);
     ~IgnoreErrorsSPKIList();
-    bool ShouldIgnoreErrors(scoped_refptr<net::X509Certificate> certificate);
+
+    // Used for tests to override the instance. Returns the old instance, which
+    // should be restored when the test's done.
+    static std::unique_ptr<IgnoreErrorsSPKIList> SetInstanceForTesting(
+        std::unique_ptr<IgnoreErrorsSPKIList> p);
 
    private:
     FRIEND_TEST_ALL_PREFIXES(SignedExchangeCertificateChainTest,
                              IgnoreErrorsSPKIList);
 
+    static std::unique_ptr<IgnoreErrorsSPKIList>& GetInstance();
+
     explicit IgnoreErrorsSPKIList(const std::string& spki_list);
     void Parse(const std::string& spki_list);
+    bool ShouldIgnoreErrorsInternal(
+        scoped_refptr<net::X509Certificate> certificate);
 
     network::IgnoreErrorsCertVerifier::SPKIHashSet hash_set_;
     DISALLOW_COPY_AND_ASSIGN(IgnoreErrorsSPKIList);
diff --git a/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc b/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc
index 2e7ac3d5..0d93a8e 100644
--- a/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc
+++ b/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_piece.h"
 #include "components/cbor/values.h"
 #include "components/cbor/writer.h"
+#include "content/browser/web_package/signed_exchange_test_utils.h"
 #include "content/public/common/content_paths.h"
 #include "net/cert/x509_util.h"
 #include "net/test/cert_test_util.h"
@@ -21,11 +22,6 @@
 
 namespace {
 
-constexpr char kPEMECDSAP256SPKIHash[] =
-    "/e65FnHy30VS3LY9z8f0bNU1v6IQbg7g/mhGEQ11Im0=";
-constexpr char kPEMECDSAP384SPKIHash[] =
-    "V8vLfOEWuDlMqzl7Ki6PmnvSepqzjY1qU0gl+DrEh9A=";
-
 cbor::Value CBORByteString(base::StringPiece str) {
   return cbor::Value(str, cbor::Value::Type::BYTE_STRING);
 }
@@ -228,14 +224,14 @@
   scoped_refptr<net::X509Certificate> cert_ecdsap384 =
       LoadCertificate("secp384r1-sha256.public.pem");
 
-  EXPECT_FALSE(ignore_nothing.ShouldIgnoreErrors(cert_ecdsap256));
-  EXPECT_FALSE(ignore_nothing.ShouldIgnoreErrors(cert_ecdsap384));
-  EXPECT_TRUE(ignore_ecdsap256.ShouldIgnoreErrors(cert_ecdsap256));
-  EXPECT_FALSE(ignore_ecdsap256.ShouldIgnoreErrors(cert_ecdsap384));
-  EXPECT_FALSE(ignore_ecdsap384.ShouldIgnoreErrors(cert_ecdsap256));
-  EXPECT_TRUE(ignore_ecdsap384.ShouldIgnoreErrors(cert_ecdsap384));
-  EXPECT_TRUE(ignore_both.ShouldIgnoreErrors(cert_ecdsap256));
-  EXPECT_TRUE(ignore_both.ShouldIgnoreErrors(cert_ecdsap384));
+  EXPECT_FALSE(ignore_nothing.ShouldIgnoreErrorsInternal(cert_ecdsap256));
+  EXPECT_FALSE(ignore_nothing.ShouldIgnoreErrorsInternal(cert_ecdsap384));
+  EXPECT_TRUE(ignore_ecdsap256.ShouldIgnoreErrorsInternal(cert_ecdsap256));
+  EXPECT_FALSE(ignore_ecdsap256.ShouldIgnoreErrorsInternal(cert_ecdsap384));
+  EXPECT_FALSE(ignore_ecdsap384.ShouldIgnoreErrorsInternal(cert_ecdsap256));
+  EXPECT_TRUE(ignore_ecdsap384.ShouldIgnoreErrorsInternal(cert_ecdsap384));
+  EXPECT_TRUE(ignore_both.ShouldIgnoreErrorsInternal(cert_ecdsap256));
+  EXPECT_TRUE(ignore_both.ShouldIgnoreErrorsInternal(cert_ecdsap384));
 }
 
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc
index da337c9..22fad8e 100644
--- a/content/browser/web_package/signed_exchange_handler.cc
+++ b/content/browser/web_package/signed_exchange_handler.cc
@@ -553,7 +553,8 @@
           net::x509_util::CryptoBufferAsStringPiece(
               verified_cert->cert_buffer())) &&
       !base::FeatureList::IsEnabled(
-          features::kAllowSignedHTTPExchangeCertsWithoutExtension)) {
+          features::kAllowSignedHTTPExchangeCertsWithoutExtension) &&
+      !unverified_cert_chain_->ShouldIgnoreErrors()) {
     signed_exchange_utils::ReportErrorAndTraceEvent(
         devtools_proxy_.get(),
         "Certificate must have CanSignHttpExchangesDraft extension. To ignore "
@@ -576,7 +577,8 @@
     // 2019-05-01 00:00:00 UTC.
     const base::Time kRequirementStartDateForIssuance =
         base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1556668800);
-    if (verified_cert->valid_start() >= kRequirementStartDateForIssuance) {
+    if (verified_cert->valid_start() >= kRequirementStartDateForIssuance &&
+        !unverified_cert_chain_->ShouldIgnoreErrors()) {
       signed_exchange_utils::ReportErrorAndTraceEvent(
           devtools_proxy_.get(),
           "Signed Exchange's certificate issued after 2019-05-01 must not have "
@@ -588,7 +590,8 @@
     // 2019-08-01 00:00:00 UTC.
     const base::Time kRequirementStartDateForVerification =
         base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(1564617600);
-    if (GetVerificationTime() >= kRequirementStartDateForVerification) {
+    if (GetVerificationTime() >= kRequirementStartDateForVerification &&
+        !unverified_cert_chain_->ShouldIgnoreErrors()) {
       signed_exchange_utils::ReportErrorAndTraceEvent(
           devtools_proxy_.get(),
           "After 2019-08-01, Signed Exchange's certificate must not have a "
diff --git a/content/browser/web_package/signed_exchange_handler_unittest.cc b/content/browser/web_package/signed_exchange_handler_unittest.cc
index 037cdfbc..e2095ed 100644
--- a/content/browser/web_package/signed_exchange_handler_unittest.cc
+++ b/content/browser/web_package/signed_exchange_handler_unittest.cc
@@ -17,6 +17,8 @@
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
 #include "content/browser/web_package/signed_exchange_request_matcher.h"
 #include "content/browser/web_package/signed_exchange_signature_verifier.h"
+#include "content/browser/web_package/signed_exchange_test_utils.h"
+#include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -32,6 +34,7 @@
 #include "net/test/test_data_directory.h"
 #include "net/url_request/url_request_test_util.h"
 #include "services/network/network_context.h"
+#include "services/network/public/cpp/network_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -63,6 +66,10 @@
 constexpr base::StringPiece kDummySCTList(kDummySCTBytes,
                                           sizeof(kDummySCTBytes));
 
+class TestBrowserClient : public ContentBrowserClient {
+  bool CanIgnoreCertificateErrorIfNeeded() override { return true; }
+};
+
 std::string GetTestFileContents(base::StringPiece name) {
   base::FilePath path;
   base::PathService::Get(content::DIR_TEST_DATA, &path);
@@ -176,6 +183,7 @@
   }
 
   void SetUp() override {
+    original_client_ = SetBrowserClientForTesting(&browser_client_);
     SignedExchangeHandler::SetVerificationTimeForTesting(
         base::Time::UnixEpoch() +
         base::TimeDelta::FromSeconds(kSignatureHeaderDate));
@@ -197,10 +205,15 @@
   }
 
   void TearDown() override {
+    if (original_ignore_errors_spki_list_) {
+      SignedExchangeCertificateChain::IgnoreErrorsSPKIList::
+          SetInstanceForTesting(std::move(original_ignore_errors_spki_list_));
+    }
     SignedExchangeHandler::SetNetworkContextForTesting(nullptr);
     network::NetworkContext::SetCertVerifierForTesting(nullptr);
     SignedExchangeHandler::SetVerificationTimeForTesting(
         base::Optional<base::Time>());
+    SetBrowserClientForTesting(original_client_);
   }
 
   void SetCertVerifier(std::unique_ptr<net::CertVerifier> cert_verifier) {
@@ -208,6 +221,18 @@
     network::NetworkContext::SetCertVerifierForTesting(cert_verifier_.get());
   }
 
+  void SetIgnoreCertificateErrorsSPKIList(const std::string value) {
+    DCHECK(!original_ignore_errors_spki_list_);
+    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
+    command_line.AppendSwitchASCII(
+        network::switches::kIgnoreCertificateErrorsSPKIList, value);
+    original_ignore_errors_spki_list_ = SignedExchangeCertificateChain::
+        IgnoreErrorsSPKIList::SetInstanceForTesting(
+            std::make_unique<
+                SignedExchangeCertificateChain::IgnoreErrorsSPKIList>(
+                command_line));
+  }
+
   // Creates a net::CertVerifyResult with some useful default values.
   net::CertVerifyResult CreateCertVerifyResult() {
     net::CertVerifyResult result;
@@ -377,10 +402,14 @@
 
   base::test::ScopedFeatureList feature_list_;
   content::TestBrowserThreadBundle browser_thread_bundle_;
+  TestBrowserClient browser_client_;
+  ContentBrowserClient* original_client_;
   std::unique_ptr<net::TestURLRequestContext> url_request_context_;
   std::unique_ptr<network::NetworkContext> network_context_;
   network::mojom::NetworkContextPtr network_context_ptr_;
   const url::Origin request_initiator_;
+  std::unique_ptr<SignedExchangeCertificateChain::IgnoreErrorsSPKIList>
+      original_ignore_errors_spki_list_;
   std::unique_ptr<net::MockSourceStream> source_stream_;
   std::unique_ptr<MockSignedExchangeCertFetcherFactory> cert_fetcher_factory_;
 
@@ -607,6 +636,34 @@
   ReadStream(source_, nullptr);
 }
 
+TEST_P(SignedExchangeHandlerTest,
+       CertValidMoreThan90DaysShouldBeAllowedByIgnoreErrorsSPKIListFlag) {
+  SetIgnoreCertificateErrorsSPKIList(kPEMECDSAP256SPKIHash);
+
+  SignedExchangeHandler::SetVerificationTimeForTesting(
+      base::Time::UnixEpoch() +
+      base::TimeDelta::FromSeconds(kCertValidityPeriodEnforcementDate));
+  mock_cert_fetcher_factory_->ExpectFetch(
+      GURL("https://cert.example.org/cert.msg"),
+      GetTestFileContents("test.example.org.public.pem.cbor"));
+  SetupMockCertVerifier("prime256v1-sha256.public.pem",
+                        CreateCertVerifyResult());
+  SetSourceStreamContents("test.example.org_test.sxg");
+
+  CreateSignedExchangeHandler(CreateTestURLRequestContext());
+  WaitForHeader();
+
+  ASSERT_TRUE(read_header());
+  EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result());
+  EXPECT_EQ(net::OK, error());
+  std::string payload;
+  int rv = ReadPayloadStream(&payload);
+  std::string expected_payload = GetTestFileContents("test.html");
+
+  EXPECT_EQ(expected_payload, payload);
+  EXPECT_EQ(static_cast<int>(expected_payload.size()), rv);
+}
+
 TEST_P(SignedExchangeHandlerTest, CertWithoutExtensionAllowedByFeatureFlag) {
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_feature_list_.InitAndEnableFeature(
@@ -633,6 +690,31 @@
   EXPECT_EQ(static_cast<int>(expected_payload.size()), rv);
 }
 
+TEST_P(SignedExchangeHandlerTest,
+       CertWithoutExtensionAllowedByIgnoreErrorsSPKIListFlag) {
+  SetIgnoreCertificateErrorsSPKIList(kPEMECDSAP256SPKIHash);
+
+  mock_cert_fetcher_factory_->ExpectFetch(
+      GURL("https://cert.example.org/cert.msg"),
+      GetTestFileContents("test.example.org-noext.public.pem.cbor"));
+  SetupMockCertVerifier("prime256v1-sha256-noext.public.pem",
+                        CreateCertVerifyResult());
+  SetSourceStreamContents("test.example.org_noext_test.sxg");
+
+  CreateSignedExchangeHandler(CreateTestURLRequestContext());
+  WaitForHeader();
+
+  ASSERT_TRUE(read_header());
+  EXPECT_EQ(SignedExchangeLoadResult::kSuccess, result());
+  EXPECT_EQ(net::OK, error());
+  std::string payload;
+  int rv = ReadPayloadStream(&payload);
+  std::string expected_payload = GetTestFileContents("test.html");
+
+  EXPECT_EQ(expected_payload, payload);
+  EXPECT_EQ(static_cast<int>(expected_payload.size()), rv);
+}
+
 TEST_P(SignedExchangeHandlerTest, CertSha256Mismatch) {
   // The certificate is for "127.0.0.1". And the SHA 256 hash of the certificate
   // is different from the cert-sha256 of the signature in the sxg file. So the
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index abdc1d7..dd58fdb 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -45,27 +45,6 @@
 constexpr char kContentTypeOptionsHeaderName[] = "x-content-type-options";
 constexpr char kNoSniffHeaderValue[] = "nosniff";
 
-net::RedirectInfo CreateRedirectInfo(
-    const GURL& new_url,
-    const network::ResourceRequest& outer_request,
-    const network::ResourceResponseHead& outer_response,
-    bool is_fallback_redirect) {
-  // https://wicg.github.io/webpackage/loading.html#mp-http-fetch
-  // Step 3. Set actualResponse's status to 303. [spec text]
-  return net::RedirectInfo::ComputeRedirectInfo(
-      "GET", outer_request.url, outer_request.site_for_cookies,
-      outer_request.top_frame_origin,
-      outer_request.update_first_party_url_on_redirect
-          ? net::URLRequest::FirstPartyURLPolicy::
-                UPDATE_FIRST_PARTY_URL_ON_REDIRECT
-          : net::URLRequest::FirstPartyURLPolicy::NEVER_CHANGE_FIRST_PARTY_URL,
-      outer_request.referrer_policy, outer_request.referrer.spec(), 303,
-      new_url,
-      net::RedirectUtil::GetReferrerPolicyHeader(outer_response.headers.get()),
-      false /* insecure_scheme_was_upgraded */, true /* copy_fragment */,
-      is_fallback_redirect);
-}
-
 bool HasNoSniffHeader(const network::ResourceResponseHead& response) {
   std::string content_type_options;
   response.headers->EnumerateHeader(nullptr, kContentTypeOptionsHeaderName,
@@ -77,57 +56,6 @@
 
 }  // namespace
 
-class SignedExchangeLoader::OuterResponseInfo {
- public:
-  explicit OuterResponseInfo(const network::ResourceResponseHead& response)
-      : request_start_(response.request_start),
-        response_start_(response.response_start),
-        request_time_(response.request_time),
-        response_time_(response.response_time),
-        load_timing_(response.load_timing) {
-    if (base::FeatureList::IsEnabled(
-            features::kSignedExchangeSubresourcePrefetch) &&
-        response.headers) {
-      response.headers->GetNormalizedHeader("link", &link_header_);
-    }
-  }
-
-  network::ResourceResponseHead CreateRedirectResponseHead() const {
-    network::ResourceResponseHead response_head;
-    response_head.encoded_data_length = 0;
-    std::string buf;
-    if (link_header_.empty()) {
-      buf = base::StringPrintf("HTTP/1.1 %d %s\r\n", 303, "See Other");
-    } else {
-      DCHECK(base::FeatureList::IsEnabled(
-          features::kSignedExchangeSubresourcePrefetch));
-      buf = base::StringPrintf(
-          "HTTP/1.1 %d %s\r\n"
-          "link: %s\r\n",
-          303, "See Other", link_header_.c_str());
-    }
-    response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
-        net::HttpUtil::AssembleRawHeaders(buf));
-    response_head.encoded_data_length = 0;
-    response_head.request_start = request_start_;
-    response_head.response_start = response_start_;
-    response_head.request_time = request_time_;
-    response_head.response_time = response_time_;
-    response_head.load_timing = load_timing_;
-    return response_head;
-  }
-
- private:
-  const base::TimeTicks request_start_;
-  const base::TimeTicks response_start_;
-  const base::Time request_time_;
-  const base::Time response_time_;
-  const net::LoadTimingInfo load_timing_;
-  std::string link_header_;
-
-  DISALLOW_COPY_AND_ASSIGN(OuterResponseInfo);
-};
-
 SignedExchangeLoader::SignedExchangeLoader(
     const network::ResourceRequest& outer_request,
     const network::ResourceResponseHead& outer_response,
@@ -143,7 +71,6 @@
     scoped_refptr<SignedExchangePrefetchMetricRecorder> metric_recorder,
     const std::string& accept_langs)
     : outer_request_(outer_request),
-      outer_response_info_(std::make_unique<OuterResponseInfo>(outer_response)),
       outer_response_(outer_response),
       forwarding_client_(std::move(forwarding_client)),
       url_loader_client_binding_(this),
@@ -319,22 +246,24 @@
     // Make a fallback redirect to |request_url|.
     DCHECK(!fallback_url_);
     fallback_url_ = request_url;
-    DCHECK(outer_response_info_);
     forwarding_client_->OnReceiveRedirect(
-        CreateRedirectInfo(request_url, outer_request_, outer_response_,
-                           true /* is_fallback_redirect */),
-        std::move(outer_response_info_)->CreateRedirectResponseHead());
+        signed_exchange_utils::CreateRedirectInfo(
+            request_url, outer_request_, outer_response_,
+            true /* is_fallback_redirect */),
+        signed_exchange_utils::CreateRedirectResponseHead(
+            outer_response_, true /* is_fallback_redirect */));
     forwarding_client_.reset();
     return;
   }
   DCHECK_EQ(result, SignedExchangeLoadResult::kSuccess);
   inner_request_url_ = request_url;
 
-  DCHECK(outer_response_info_);
   forwarding_client_->OnReceiveRedirect(
-      CreateRedirectInfo(request_url, outer_request_, outer_response_,
-                         false /* is_fallback_redirect */),
-      std::move(outer_response_info_)->CreateRedirectResponseHead());
+      signed_exchange_utils::CreateRedirectInfo(
+          request_url, outer_request_, outer_response_,
+          false /* is_fallback_redirect */),
+      signed_exchange_utils::CreateRedirectResponseHead(
+          outer_response_, false /* is_fallback_redirect */));
   forwarding_client_.reset();
 
   const base::Optional<net::SSLInfo>& ssl_info = resource_response.ssl_info;
diff --git a/content/browser/web_package/signed_exchange_loader.h b/content/browser/web_package/signed_exchange_loader.h
index f6949800..c14193b 100644
--- a/content/browser/web_package/signed_exchange_loader.h
+++ b/content/browser/web_package/signed_exchange_loader.h
@@ -116,8 +116,6 @@
       SignedExchangeHandlerFactory* factory);
 
  private:
-  class OuterResponseInfo;
-
   // Called from |signed_exchange_handler_| when it finds an origin-signed HTTP
   // exchange.
   void OnHTTPExchangeFound(
@@ -134,9 +132,6 @@
 
   const network::ResourceRequest outer_request_;
 
-  // This info is used to create a dummy redirect response.
-  std::unique_ptr<const OuterResponseInfo> outer_response_info_;
-
   // The outer response of signed HTTP exchange which was received from network.
   const network::ResourceResponseHead outer_response_;
 
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index 45348f58..d47ab9f 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <tuple>
+
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
@@ -199,25 +201,44 @@
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeRequestHandlerBrowserTestBase);
 };
 
-enum class SignedExchangeRequestHandlerBrowserTestPrefetchParam {
-  kPrefetchDisabled,
-  kPrefetchEnabled
-};
-
 class SignedExchangeRequestHandlerBrowserTest
-    : public SignedExchangeRequestHandlerBrowserTestBase,
-      public testing::WithParamInterface<
-          SignedExchangeRequestHandlerBrowserTestPrefetchParam> {
+    : public testing::WithParamInterface<
+          std::tuple<bool /* prefetch_enabled */,
+                     bool /* network_service_enabled */,
+                     bool /* sxg_subresource_prefetch_enabled */>>,
+      public SignedExchangeRequestHandlerBrowserTestBase {
  public:
   SignedExchangeRequestHandlerBrowserTest() = default;
+  ~SignedExchangeRequestHandlerBrowserTest() = default;
 
- protected:
-  bool PrefetchIsEnabled() {
-    return GetParam() == SignedExchangeRequestHandlerBrowserTestPrefetchParam::
-                             kPrefetchEnabled;
+  void SetUp() override {
+    bool network_service_enabled;
+    bool sxg_subresource_prefetch_enabled;
+    std::tie(prefetch_enabled_, network_service_enabled,
+             sxg_subresource_prefetch_enabled) = GetParam();
+    std::vector<base::Feature> enable_features;
+    std::vector<base::Feature> disabled_features;
+    if (network_service_enabled) {
+      enable_features.push_back(network::features::kNetworkService);
+    } else {
+      disabled_features.push_back(network::features::kNetworkService);
+    }
+    if (sxg_subresource_prefetch_enabled) {
+      enable_features.push_back(features::kSignedExchangeSubresourcePrefetch);
+    } else {
+      disabled_features.push_back(features::kSignedExchangeSubresourcePrefetch);
+    }
+    feature_list_.InitWithFeatures(enable_features, disabled_features);
+    SignedExchangeRequestHandlerBrowserTestBase::SetUp();
   }
 
+ protected:
+  bool PrefetchIsEnabled() const { return prefetch_enabled_; }
+
  private:
+  bool prefetch_enabled_;
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeRequestHandlerBrowserTest);
 };
 
@@ -529,13 +550,11 @@
       PrefetchIsEnabled() ? 2 : 1);
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    SignedExchangeRequestHandlerBrowserTest,
-    SignedExchangeRequestHandlerBrowserTest,
-    testing::Values(
-        SignedExchangeRequestHandlerBrowserTestPrefetchParam::kPrefetchDisabled,
-        SignedExchangeRequestHandlerBrowserTestPrefetchParam::
-            kPrefetchEnabled));
+INSTANTIATE_TEST_SUITE_P(,
+                         SignedExchangeRequestHandlerBrowserTest,
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool(),
+                                            ::testing::Bool()));
 
 class SignedExchangeRequestHandlerDownloadBrowserTest
     : public SignedExchangeRequestHandlerBrowserTestBase {
diff --git a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
index 7d25cd4..79e284b 100644
--- a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
@@ -152,6 +152,74 @@
   EXPECT_EQ(target_sxg_url, it->second->outer_url());
   EXPECT_EQ(target_url, it->second->inner_url());
   EXPECT_EQ(target_header_integrity, *it->second->header_integrity());
+
+  // Shutdown the server.
+  EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+
+  // Subsequent navigation to the target URL wouldn't hit the network for
+  // the target URL. The target content should still be read correctly.
+  // The content is loaded from PrefetchedSignedExchangeCache.
+  NavigateToURLAndWaitTitle(target_sxg_url, "Prefetch Target (SXG)");
+}
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
+                       PrefetchedSignedExchangeCache_SameUrl) {
+  int target_sxg_fetch_count = 0;
+  const char* prefetch_path = "/prefetch.html";
+  const char* target_sxg_path = "/target.html";
+  const char* target_path = "/target.html";
+
+  base::RunLoop target_sxg_prefetch_waiter;
+  RegisterRequestMonitor(embedded_test_server(), target_sxg_path,
+                         &target_sxg_fetch_count, &target_sxg_prefetch_waiter);
+  RegisterRequestHandler(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_EQ(0, prefetch_url_loader_called_);
+
+  const GURL target_sxg_url = embedded_test_server()->GetURL(target_sxg_path);
+  const GURL target_url = embedded_test_server()->GetURL(target_path);
+
+  RegisterResponse(
+      prefetch_path,
+      ResponseEntry(base::StringPrintf(
+          "<body><link rel='prefetch' href='%s'></body>", target_sxg_path)));
+  RegisterResponse(
+      target_sxg_path,
+      // We mock the SignedExchangeHandler, so just return a HTML content
+      // as "application/signed-exchange;v=b3".
+      ResponseEntry("<head><title>Prefetch Target (SXG)</title></head>",
+                    "application/signed-exchange;v=b3",
+                    {{"x-content-type-options", "nosniff"}}));
+
+  const net::SHA256HashValue target_header_integrity = {{0x01}};
+  MockSignedExchangeHandlerFactory factory({MockSignedExchangeHandlerParams(
+      target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK, target_url,
+      "text/html", {}, target_header_integrity)});
+  ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
+
+  NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
+  target_sxg_prefetch_waiter.Run();
+  EXPECT_EQ(1, target_sxg_fetch_count);
+
+  WaitUntilLoaded(target_sxg_url);
+
+  EXPECT_EQ(1, prefetch_url_loader_called_);
+
+  const auto& cached_exchanges = GetCachedExchanges();
+  EXPECT_EQ(1u, cached_exchanges.size());
+  const auto it = cached_exchanges.find(target_sxg_url);
+  ASSERT_TRUE(it != cached_exchanges.end());
+  EXPECT_EQ(target_sxg_url, it->second->outer_url());
+  EXPECT_EQ(target_url, it->second->inner_url());
+  EXPECT_EQ(target_header_integrity, *it->second->header_integrity());
+
+  // Shutdown the server.
+  EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+
+  // Subsequent navigation to the target URL wouldn't hit the network for
+  // the target URL. The target content should still be read correctly.
+  // The content is loaded from PrefetchedSignedExchangeCache.
+  NavigateToURLAndWaitTitle(target_sxg_url, "Prefetch Target (SXG)");
 }
 
 IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
diff --git a/content/browser/web_package/signed_exchange_test_utils.h b/content/browser/web_package/signed_exchange_test_utils.h
new file mode 100644
index 0000000..76d44f0
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_test_utils.h
@@ -0,0 +1,17 @@
+// 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 CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_TEST_UTILS_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_TEST_UTILS_H_
+
+namespace content {
+
+constexpr char kPEMECDSAP256SPKIHash[] =
+    "/e65FnHy30VS3LY9z8f0bNU1v6IQbg7g/mhGEQ11Im0=";
+constexpr char kPEMECDSAP384SPKIHash[] =
+    "V8vLfOEWuDlMqzl7Ki6PmnvSepqzjY1qU0gl+DrEh9A=";
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_TEST_UTILS_H_
diff --git a/content/browser/web_package/signed_exchange_utils.cc b/content/browser/web_package/signed_exchange_utils.cc
index 2c886413..a7248ef 100644
--- a/content/browser/web_package/signed_exchange_utils.cc
+++ b/content/browser/web_package/signed_exchange_utils.cc
@@ -194,5 +194,60 @@
   return SignedExchangeLoadResult::kSignatureVerificationError;
 }
 
+net::RedirectInfo CreateRedirectInfo(
+    const GURL& new_url,
+    const network::ResourceRequest& outer_request,
+    const network::ResourceResponseHead& outer_response,
+    bool is_fallback_redirect) {
+  // https://wicg.github.io/webpackage/loading.html#mp-http-fetch
+  // Step 3. Set actualResponse's status to 303. [spec text]
+  return net::RedirectInfo::ComputeRedirectInfo(
+      "GET", outer_request.url, outer_request.site_for_cookies,
+      outer_request.top_frame_origin,
+      outer_request.update_first_party_url_on_redirect
+          ? net::URLRequest::FirstPartyURLPolicy::
+                UPDATE_FIRST_PARTY_URL_ON_REDIRECT
+          : net::URLRequest::FirstPartyURLPolicy::NEVER_CHANGE_FIRST_PARTY_URL,
+      outer_request.referrer_policy, outer_request.referrer.spec(), 303,
+      new_url,
+      net::RedirectUtil::GetReferrerPolicyHeader(outer_response.headers.get()),
+      false /* insecure_scheme_was_upgraded */, true /* copy_fragment */,
+      is_fallback_redirect);
+}
+
+network::ResourceResponseHead CreateRedirectResponseHead(
+    const network::ResourceResponseHead& outer_response,
+    bool is_fallback_redirect) {
+  network::ResourceResponseHead response_head;
+  response_head.encoded_data_length = 0;
+  std::string buf;
+  std::string link_header;
+  if (!is_fallback_redirect &&
+      base::FeatureList::IsEnabled(
+          features::kSignedExchangeSubresourcePrefetch) &&
+      outer_response.headers) {
+    outer_response.headers->GetNormalizedHeader("link", &link_header);
+  }
+  if (link_header.empty()) {
+    buf = base::StringPrintf("HTTP/1.1 %d %s\r\n", 303, "See Other");
+  } else {
+    DCHECK(base::FeatureList::IsEnabled(
+        features::kSignedExchangeSubresourcePrefetch));
+    buf = base::StringPrintf(
+        "HTTP/1.1 %d %s\r\n"
+        "link: %s\r\n",
+        303, "See Other", link_header.c_str());
+  }
+  response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+      net::HttpUtil::AssembleRawHeaders(buf));
+  response_head.encoded_data_length = 0;
+  response_head.request_start = outer_response.request_start;
+  response_head.response_start = outer_response.response_start;
+  response_head.request_time = outer_response.request_time;
+  response_head.response_time = outer_response.response_time;
+  response_head.load_timing = outer_response.load_timing;
+  return response_head;
+}
+
 }  // namespace signed_exchange_utils
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_utils.h b/content/browser/web_package/signed_exchange_utils.h
index 44ec88f59..1500b0f 100644
--- a/content/browser/web_package/signed_exchange_utils.h
+++ b/content/browser/web_package/signed_exchange_utils.h
@@ -12,9 +12,12 @@
 #include "content/browser/web_package/signed_exchange_error.h"
 #include "content/browser/web_package/signed_exchange_signature_verifier.h"
 #include "content/common/content_export.h"
+#include "net/url_request/redirect_util.h"
+#include "services/network/public/cpp/resource_response.h"
 #include "url/gurl.h"
 
 namespace network {
+struct ResourceRequest;
 struct ResourceResponseHead;
 }  // namespace network
 
@@ -73,6 +76,19 @@
 SignedExchangeLoadResult GetLoadResultFromSignatureVerifierResult(
     SignedExchangeSignatureVerifier::Result verify_result);
 
+// Creates a RedirectInfo of synthesized redirect for signed exchange loading.
+net::RedirectInfo CreateRedirectInfo(
+    const GURL& new_url,
+    const network::ResourceRequest& outer_request,
+    const network::ResourceResponseHead& outer_response,
+    bool is_fallback_redirect);
+
+// Creates a ResourceResponseHead of synthesized redirect for signed exchange
+// loading.
+network::ResourceResponseHead CreateRedirectResponseHead(
+    const network::ResourceResponseHead& outer_response,
+    bool is_fallback_redirect);
+
 }  // namespace signed_exchange_utils
 }  // namespace content
 
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h
index de5b36a..7b2a07a 100644
--- a/content/public/browser/push_messaging_service.h
+++ b/content/public/browser/push_messaging_service.h
@@ -20,7 +20,7 @@
 enum class PushUnregistrationReason;
 enum class PushUnregistrationStatus;
 }  // namespace mojom
-struct PushSubscriptionOptionsParams;
+struct WebPushSubscriptionOptions;
 }  // namespace blink
 
 namespace content {
@@ -63,7 +63,7 @@
       int64_t service_worker_registration_id,
       int renderer_id,
       int render_frame_id,
-      const blink::PushSubscriptionOptionsParams& options,
+      const blink::WebPushSubscriptionOptions& options,
       bool user_gesture,
       RegisterCallback callback) = 0;
 
@@ -75,7 +75,7 @@
   virtual void SubscribeFromWorker(
       const GURL& requesting_origin,
       int64_t service_worker_registration_id,
-      const blink::PushSubscriptionOptionsParams& options,
+      const blink::WebPushSubscriptionOptions& options,
       RegisterCallback callback) = 0;
 
   // Retrieves the subscription associated with |origin| and
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index dc3feb9..29dff3b 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -135,7 +135,7 @@
 // When a screen reader is detected, allow users the option of letting
 // Google provide descriptions for unlabeled images.
 const base::Feature kExperimentalAccessibilityLabels{
-    "ExperimentalAccessibilityLabels", base::FEATURE_ENABLED_BY_DEFAULT};
+    "ExperimentalAccessibilityLabels", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Throttle tasks in Blink background timer queues based on CPU budgets
 // for the background tab. Bug: https://crbug.com/639852.
diff --git a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
index 53ff571..7b384bfd 100644
--- a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
@@ -331,11 +331,11 @@
     dropped_frame_count_ += rendering_frame_buffer_->frames_queued() - 1;
     rendering_frame_buffer_->Reset();
     timestamps_to_clock_times_.clear();
-    RenderWithoutAlgorithm(std::move(frame));
+    RenderWithoutAlgorithm(frame);
   }
 
   timestamps_to_clock_times_[frame->timestamp()] = render_time;
-  rendering_frame_buffer_->EnqueueFrame(frame);
+  rendering_frame_buffer_->EnqueueFrame(std::move(frame));
 }
 
 bool WebMediaPlayerMSCompositor::UpdateCurrentFrame(
diff --git a/content/renderer/push_messaging/push_messaging_client.cc b/content/renderer/push_messaging/push_messaging_client.cc
index b96b56d..6961f408 100644
--- a/content/renderer/push_messaging/push_messaging_client.cc
+++ b/content/renderer/push_messaging/push_messaging_client.cc
@@ -15,11 +15,10 @@
 #include "content/renderer/push_messaging/push_messaging_utils.h"
 #include "content/renderer/render_frame_impl.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_error.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h"
-#include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_console_message.h"
 #include "third_party/blink/public/web/web_local_frame.h"
@@ -55,7 +54,7 @@
 
   // If a developer provided an application server key in |options|, skip
   // fetching the manifest.
-  if (options.application_server_key.IsEmpty()) {
+  if (options.application_server_key.empty()) {
     blink::WebManifestManager* manifest_manager =
         blink::WebManifestManager::FromFrame(
             RenderFrameImpl::FromRoutingID(routing_id())->GetWebFrame());
@@ -64,11 +63,11 @@
                        base::Unretained(this), service_worker_registration_id,
                        options, user_gesture, std::move(callbacks)));
   } else {
-    blink::PushSubscriptionOptionsParams content_options;
+    blink::WebPushSubscriptionOptions content_options;
     content_options.user_visible_only = options.user_visible_only;
     // Just treat the server key as a string of bytes and pass it to the push
     // service.
-    content_options.sender_info = options.application_server_key.Latin1();
+    content_options.application_server_key = options.application_server_key;
     DoSubscribe(service_worker_registration_id, content_options, user_gesture,
                 std::move(callbacks));
   }
@@ -81,8 +80,8 @@
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
     const blink::WebURL& manifest_url,
     const blink::Manifest& manifest) {
-  // Get the sender_info from the manifest since it wasn't provided by
-  // the caller.
+  // Get the application_server_key from the manifest since it wasn't provided
+  // by the caller.
   if (manifest.IsEmpty()) {
     DidSubscribe(
         std::move(callbacks),
@@ -91,10 +90,10 @@
     return;
   }
 
-  blink::PushSubscriptionOptionsParams content_options;
+  blink::WebPushSubscriptionOptions content_options;
   content_options.user_visible_only = options.user_visible_only;
   if (!manifest.gcm_sender_id.is_null()) {
-    content_options.sender_info =
+    content_options.application_server_key =
         base::UTF16ToUTF8(manifest.gcm_sender_id.string());
   }
 
@@ -104,10 +103,10 @@
 
 void PushMessagingClient::DoSubscribe(
     int64_t service_worker_registration_id,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
-  if (options.sender_info.empty()) {
+  if (options.application_server_key.empty()) {
     DidSubscribe(std::move(callbacks),
                  blink::mojom::PushRegistrationStatus::NO_SENDER_ID,
                  base::nullopt, base::nullopt, base::nullopt, base::nullopt);
@@ -127,7 +126,7 @@
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
     blink::mojom::PushRegistrationStatus status,
     const base::Optional<GURL>& endpoint,
-    const base::Optional<blink::PushSubscriptionOptionsParams>& options,
+    const base::Optional<blink::WebPushSubscriptionOptions>& options,
     const base::Optional<std::vector<uint8_t>>& p256dh,
     const base::Optional<std::vector<uint8_t>>& auth) {
   DCHECK(callbacks);
@@ -144,7 +143,7 @@
 
     callbacks->OnSuccess(std::make_unique<blink::WebPushSubscription>(
         endpoint.value(), options.value().user_visible_only,
-        blink::WebString::FromLatin1(options.value().sender_info),
+        blink::WebString::FromLatin1(options.value().application_server_key),
         p256dh.value(), auth.value()));
   } else {
     callbacks->OnError(PushRegistrationStatusToWebPushError(status));
diff --git a/content/renderer/push_messaging/push_messaging_client.h b/content/renderer/push_messaging/push_messaging_client.h
index 88fb761..94f8452 100644
--- a/content/renderer/push_messaging/push_messaging_client.h
+++ b/content/renderer/push_messaging/push_messaging_client.h
@@ -26,7 +26,7 @@
 }
 
 struct Manifest;
-struct PushSubscriptionOptionsParams;
+struct WebPushSubscriptionOptions;
 struct WebPushSubscriptionOptions;
 }  // namespace blink
 
@@ -59,7 +59,7 @@
 
   void DoSubscribe(
       int64_t service_worker_registration_id,
-      const blink::PushSubscriptionOptionsParams& options,
+      const blink::WebPushSubscriptionOptions& options,
       bool user_gesture,
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks);
 
@@ -67,7 +67,7 @@
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
       blink::mojom::PushRegistrationStatus status,
       const base::Optional<GURL>& endpoint,
-      const base::Optional<blink::PushSubscriptionOptionsParams>& options,
+      const base::Optional<blink::WebPushSubscriptionOptions>& options,
       const base::Optional<std::vector<uint8_t>>& p256dh,
       const base::Optional<std::vector<uint8_t>>& auth);
 
diff --git a/content/renderer/push_messaging/push_provider.cc b/content/renderer/push_messaging/push_provider.cc
index 05d6fcb..ccf42a0 100644
--- a/content/renderer/push_messaging/push_provider.cc
+++ b/content/renderer/push_messaging/push_provider.cc
@@ -16,10 +16,9 @@
 #include "content/public/common/service_names.mojom.h"
 #include "content/renderer/push_messaging/push_messaging_utils.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h"
-#include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/platform/web_string.h"
 
 namespace content {
@@ -77,12 +76,12 @@
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
   DCHECK(callbacks);
 
-  blink::PushSubscriptionOptionsParams content_options;
+  blink::WebPushSubscriptionOptions content_options;
   content_options.user_visible_only = options.user_visible_only;
 
   // Just treat the server key as a string of bytes and pass it to the push
   // service.
-  content_options.sender_info = options.application_server_key.Latin1();
+  content_options.application_server_key = options.application_server_key;
 
   push_messaging_manager_->Subscribe(
       ChildProcessHost::kInvalidUniqueID, service_worker_registration_id,
@@ -97,7 +96,7 @@
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
     blink::mojom::PushRegistrationStatus status,
     const base::Optional<GURL>& endpoint,
-    const base::Optional<blink::PushSubscriptionOptionsParams>& options,
+    const base::Optional<blink::WebPushSubscriptionOptions>& options,
     const base::Optional<std::vector<uint8_t>>& p256dh,
     const base::Optional<std::vector<uint8_t>>& auth) {
   DCHECK(callbacks);
@@ -114,7 +113,7 @@
 
     callbacks->OnSuccess(std::make_unique<blink::WebPushSubscription>(
         endpoint.value(), options.value().user_visible_only,
-        blink::WebString::FromLatin1(options.value().sender_info),
+        blink::WebString::FromLatin1(options.value().application_server_key),
         p256dh.value(), auth.value()));
   } else {
     callbacks->OnError(PushRegistrationStatusToWebPushError(status));
@@ -167,7 +166,7 @@
     std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
     blink::mojom::PushGetRegistrationStatus status,
     const base::Optional<GURL>& endpoint,
-    const base::Optional<blink::PushSubscriptionOptionsParams>& options,
+    const base::Optional<blink::WebPushSubscriptionOptions>& options,
     const base::Optional<std::vector<uint8_t>>& p256dh,
     const base::Optional<std::vector<uint8_t>>& auth) {
   DCHECK(callbacks);
@@ -180,7 +179,7 @@
 
     callbacks->OnSuccess(std::make_unique<blink::WebPushSubscription>(
         endpoint.value(), options.value().user_visible_only,
-        blink::WebString::FromLatin1(options.value().sender_info),
+        blink::WebString::FromLatin1(options.value().application_server_key),
         p256dh.value(), auth.value()));
   } else {
     // We are only expecting an error if we can't find a registration.
diff --git a/content/renderer/push_messaging/push_provider.h b/content/renderer/push_messaging/push_provider.h
index d6b3a39..4a09d2b 100644
--- a/content/renderer/push_messaging/push_provider.h
+++ b/content/renderer/push_messaging/push_provider.h
@@ -27,7 +27,7 @@
 enum class PushRegistrationStatus;
 }  // namespace mojom
 
-struct PushSubscriptionOptionsParams;
+struct WebPushSubscriptionOptions;
 struct WebPushSubscriptionOptions;
 }  // namespace blink
 
@@ -71,7 +71,7 @@
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
       blink::mojom::PushRegistrationStatus status,
       const base::Optional<GURL>& endpoint,
-      const base::Optional<blink::PushSubscriptionOptionsParams>& options,
+      const base::Optional<blink::WebPushSubscriptionOptions>& options,
       const base::Optional<std::vector<uint8_t>>& p256dh,
       const base::Optional<std::vector<uint8_t>>& auth);
 
@@ -85,7 +85,7 @@
       std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
       blink::mojom::PushGetRegistrationStatus status,
       const base::Optional<GURL>& endpoint,
-      const base::Optional<blink::PushSubscriptionOptionsParams>& options,
+      const base::Optional<blink::WebPushSubscriptionOptions>& options,
       const base::Optional<std::vector<uint8_t>>& p256dh,
       const base::Optional<std::vector<uint8_t>>& auth);
 
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.cc b/content/shell/browser/web_test/web_test_push_messaging_service.cc
index 2f5473b..210c950f 100644
--- a/content/shell/browser/web_test/web_test_push_messaging_service.cc
+++ b/content/shell/browser/web_test/web_test_push_messaging_service.cc
@@ -13,7 +13,7 @@
 #include "content/shell/browser/web_test/web_test_browser_context.h"
 #include "content/shell/browser/web_test/web_test_content_browser_client.h"
 #include "content/shell/browser/web_test/web_test_permission_manager.h"
-#include "third_party/blink/public/common/push_messaging/push_subscription_options_params.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 
@@ -59,7 +59,7 @@
     int64_t service_worker_registration_id,
     int renderer_id,
     int render_frame_id,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     bool user_gesture,
     RegisterCallback callback) {
   SubscribeFromWorker(requesting_origin, service_worker_registration_id,
@@ -69,7 +69,7 @@
 void WebTestPushMessagingService::SubscribeFromWorker(
     const GURL& requesting_origin,
     int64_t service_worker_registration_id,
-    const blink::PushSubscriptionOptionsParams& options,
+    const blink::WebPushSubscriptionOptions& options,
     RegisterCallback callback) {
   blink::mojom::PermissionStatus permission_status =
       WebTestContentBrowserClient::Get()
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.h b/content/shell/browser/web_test/web_test_push_messaging_service.h
index e8b8446..fb36035f 100644
--- a/content/shell/browser/web_test/web_test_push_messaging_service.h
+++ b/content/shell/browser/web_test/web_test_push_messaging_service.h
@@ -14,7 +14,7 @@
 #include "content/public/browser/push_messaging_service.h"
 
 namespace blink {
-struct PushSubscriptionOptionsParams;
+struct WebPushSubscriptionOptions;
 }  // namespace blink
 
 namespace content {
@@ -26,17 +26,16 @@
 
   // PushMessagingService implementation:
   GURL GetEndpoint(bool standard_protocol) const override;
-  void SubscribeFromDocument(
-      const GURL& requesting_origin,
-      int64_t service_worker_registration_id,
-      int renderer_id,
-      int render_frame_id,
-      const blink::PushSubscriptionOptionsParams& options,
-      bool user_gesture,
-      RegisterCallback callback) override;
+  void SubscribeFromDocument(const GURL& requesting_origin,
+                             int64_t service_worker_registration_id,
+                             int renderer_id,
+                             int render_frame_id,
+                             const blink::WebPushSubscriptionOptions& options,
+                             bool user_gesture,
+                             RegisterCallback callback) override;
   void SubscribeFromWorker(const GURL& requesting_origin,
                            int64_t service_worker_registration_id,
-                           const blink::PushSubscriptionOptionsParams& options,
+                           const blink::WebPushSubscriptionOptions& options,
                            RegisterCallback callback) override;
   void GetSubscriptionInfo(const GURL& origin,
                            int64_t service_worker_registration_id,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 061caf6..0337e883 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -70,6 +70,7 @@
     "../browser/service_worker/test_service_worker_observer.h",
     "../browser/web_package/mock_signed_exchange_handler.cc",
     "../browser/web_package/mock_signed_exchange_handler.h",
+    "../browser/web_package/signed_exchange_test_utils.h",
     "../public/test/audio_service_test_helper.cc",
     "../public/test/audio_service_test_helper.h",
     "../public/test/background_sync_test_util.cc",
diff --git a/content/test/data/sxg/generate-test-certs.sh b/content/test/data/sxg/generate-test-certs.sh
index d4e0229d..48beafb 100755
--- a/content/test/data/sxg/generate-test-certs.sh
+++ b/content/test/data/sxg/generate-test-certs.sh
@@ -78,7 +78,7 @@
   -out secp384r1-sha256.public.pem
 
 echo
-echo "Update the test hashes in signed_exchange_certificate_chain_unittest.cc"
+echo "Update the test hashes in signed_exchange_test_utils.h"
 echo "with the followings:"
 echo "===="
 
diff --git a/device/BUILD.gn b/device/BUILD.gn
index d557ff0..8f8836fd 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -193,6 +193,7 @@
       "//device/usb:test_support",
       "//device/usb/mojo",
       "//device/usb/public/cpp",
+      "//device/usb/public/cpp:test_support",
       "//device/usb/public/mojom",
       "//net:test_support",
     ]
diff --git a/device/usb/mojo/device_manager_impl.cc b/device/usb/mojo/device_manager_impl.cc
index 4cc28464..f4615aca 100644
--- a/device/usb/mojo/device_manager_impl.cc
+++ b/device/usb/mojo/device_manager_impl.cc
@@ -176,8 +176,9 @@
 
   std::vector<mojom::UsbDeviceInfoPtr> device_infos;
   for (const auto& device : devices) {
-    if (UsbDeviceFilterMatchesAny(filters, *device)) {
-      device_infos.push_back(mojom::UsbDeviceInfo::From(*device));
+    mojom::UsbDeviceInfoPtr device_info = mojom::UsbDeviceInfo::From(*device);
+    if (UsbDeviceFilterMatchesAny(filters, *device_info)) {
+      device_infos.push_back(std::move(device_info));
     }
   }
 
diff --git a/device/usb/mojo/type_converters.cc b/device/usb/mojo/type_converters.cc
index b2ee0025..9f71bf3 100644
--- a/device/usb/mojo/type_converters.cc
+++ b/device/usb/mojo/type_converters.cc
@@ -22,7 +22,8 @@
     device::UsbEndpointDescriptor>::Convert(const device::UsbEndpointDescriptor&
                                                 endpoint) {
   auto info = device::mojom::UsbEndpointInfo::New();
-  info->endpoint_number = device::ConvertEndpointAddressToNumber(endpoint);
+  info->endpoint_number =
+      device::ConvertEndpointAddressToNumber(endpoint.address);
   info->direction = endpoint.direction;
   info->type = endpoint.transfer_type;
   info->packet_size = static_cast<uint32_t>(endpoint.maximum_packet_size);
diff --git a/device/usb/public/cpp/BUILD.gn b/device/usb/public/cpp/BUILD.gn
index 167a632..196a0d58 100644
--- a/device/usb/public/cpp/BUILD.gn
+++ b/device/usb/public/cpp/BUILD.gn
@@ -10,6 +10,8 @@
   ]
 
   deps = [
+    # TODO(donna.wu@intel.com): remove this when usb_ids.h/cpp have been
+    # moved to the public folder.
     "//device/usb",
     "//device/usb/public/mojom",
   ]
diff --git a/device/usb/public/cpp/usb_utils.cc b/device/usb/public/cpp/usb_utils.cc
index 653b7a27..6aa4f450 100644
--- a/device/usb/public/cpp/usb_utils.cc
+++ b/device/usb/public/cpp/usb_utils.cc
@@ -7,44 +7,10 @@
 #include <utility>
 
 #include "device/usb/public/mojom/device_enumeration_options.mojom.h"
-#include "device/usb/usb_descriptors.h"
-#include "device/usb/usb_device.h"
 
 namespace device {
 
 bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter,
-                            const UsbDevice& device) {
-  if (filter.has_vendor_id) {
-    if (device.vendor_id() != filter.vendor_id)
-      return false;
-
-    if (filter.has_product_id && device.product_id() != filter.product_id)
-      return false;
-  }
-
-  if (filter.serial_number && device.serial_number() != *filter.serial_number)
-    return false;
-
-  if (filter.has_class_code) {
-    for (const UsbConfigDescriptor& config : device.configurations()) {
-      for (const UsbInterfaceDescriptor& iface : config.interfaces) {
-        if (iface.interface_class == filter.class_code &&
-            (!filter.has_subclass_code ||
-             (iface.interface_subclass == filter.subclass_code &&
-              (!filter.has_protocol_code ||
-               iface.interface_protocol == filter.protocol_code)))) {
-          return true;
-        }
-      }
-    }
-
-    return false;
-  }
-
-  return true;
-}
-
-bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter,
                             const mojom::UsbDeviceInfo& device_info) {
   if (filter.has_vendor_id) {
     if (device_info.vendor_id != filter.vendor_id)
@@ -82,19 +48,6 @@
 
 bool UsbDeviceFilterMatchesAny(
     const std::vector<mojom::UsbDeviceFilterPtr>& filters,
-    const UsbDevice& device) {
-  if (filters.empty())
-    return true;
-
-  for (const auto& filter : filters) {
-    if (UsbDeviceFilterMatches(*filter, device))
-      return true;
-  }
-  return false;
-}
-
-bool UsbDeviceFilterMatchesAny(
-    const std::vector<mojom::UsbDeviceFilterPtr>& filters,
     const mojom::UsbDeviceInfo& device_info) {
   if (filters.empty())
     return true;
@@ -120,8 +73,8 @@
   return packets;
 }
 
-uint8_t ConvertEndpointAddressToNumber(const UsbEndpointDescriptor& endpoint) {
-  return endpoint.address & 0x0F;
+uint8_t ConvertEndpointAddressToNumber(uint8_t address) {
+  return address & 0x0F;
 }
 
 uint8_t ConvertEndpointNumberToAddress(
diff --git a/device/usb/public/cpp/usb_utils.h b/device/usb/public/cpp/usb_utils.h
index 0a56214..55a1770 100644
--- a/device/usb/public/cpp/usb_utils.h
+++ b/device/usb/public/cpp/usb_utils.h
@@ -12,28 +12,18 @@
 
 namespace device {
 
-class UsbDevice;
-struct UsbEndpointDescriptor;
-
-bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter,
-                            const UsbDevice& device);
-
 bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter,
                             const mojom::UsbDeviceInfo& device_info);
 
 bool UsbDeviceFilterMatchesAny(
     const std::vector<mojom::UsbDeviceFilterPtr>& filters,
-    const UsbDevice& device);
-
-bool UsbDeviceFilterMatchesAny(
-    const std::vector<mojom::UsbDeviceFilterPtr>& filters,
     const mojom::UsbDeviceInfo& device_info);
 
 std::vector<mojom::UsbIsochronousPacketPtr> BuildIsochronousPacketArray(
     const std::vector<uint32_t>& packet_lengths,
     mojom::UsbTransferStatus status);
 
-uint8_t ConvertEndpointAddressToNumber(const UsbEndpointDescriptor& endpoint);
+uint8_t ConvertEndpointAddressToNumber(uint8_t address);
 
 uint8_t ConvertEndpointNumberToAddress(
     const mojom::UsbEndpointInfo& mojo_endpoint);
diff --git a/device/usb/public/cpp/usb_utils_unittest.cc b/device/usb/public/cpp/usb_utils_unittest.cc
index dbae67c..154a843 100644
--- a/device/usb/public/cpp/usb_utils_unittest.cc
+++ b/device/usb/public/cpp/usb_utils_unittest.cc
@@ -6,11 +6,9 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/strings/utf_string_conversions.h"
-#include "device/usb/mock_usb_device.h"
-#include "device/usb/mojo/type_converters.h"
+#include "device/usb/public/cpp/fake_usb_device_info.h"
 #include "device/usb/public/cpp/usb_utils.h"
 #include "device/usb/public/mojom/device_enumeration_options.mojom.h"
-#include "device/usb/usb_descriptors.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,46 +18,68 @@
 
 using testing::Return;
 
-const uint8_t kEndpointAddress_1 = 0x81;
-const uint8_t kEndpointAddress_2 = 0x02;
-
 class UsbUtilsTest : public testing::Test {
  public:
   void SetUp() override {
-    UsbConfigDescriptor config(1, false, false, 0);
-    UsbInterfaceDescriptor interface(1, 0, 0xff, 0x42, 0x01);
+    std::vector<mojom::UsbConfigurationInfoPtr> configs;
+    configs.push_back(mojom::UsbConfigurationInfo::New());
+    configs[0]->interfaces.push_back(mojom::UsbInterfaceInfo::New());
+
+    auto alternate = mojom::UsbAlternateInterfaceInfo::New();
+    alternate->alternate_setting = 0;
+    alternate->class_code = 0xff;
+    alternate->subclass_code = 0x42;
+    alternate->protocol_code = 0x01;
+
     // Endpoint 1 IN
-    interface.endpoints.emplace_back(kEndpointAddress_1, 0x01, 0x0400, 0x08);
+    auto endpoint_1 = mojom::UsbEndpointInfo::New();
+    endpoint_1->endpoint_number = 0x01;
+    endpoint_1->direction = mojom::UsbTransferDirection::INBOUND;
+    alternate->endpoints.push_back(std::move(endpoint_1));
+
     // Endpoint 2 OUT
-    interface.endpoints.emplace_back(kEndpointAddress_2, 0x11, 0x0400, 0x08);
+    auto endpoint_2 = mojom::UsbEndpointInfo::New();
+    endpoint_2->endpoint_number = 0x02;
+    endpoint_2->direction = mojom::UsbTransferDirection::OUTBOUND;
+    alternate->endpoints.push_back(std::move(endpoint_2));
 
-    config.interfaces.push_back(interface);
+    configs[0]->interfaces[0]->alternates.push_back(
+        std::move(std::move(alternate)));
 
-    android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, "Google Inc.", "Nexus 5",
-                                       "ABC123", {config});
+    android_phone_ =
+        new FakeUsbDeviceInfo(/*vendor_id*/ 0x18d1,
+                              /*vendor_id*/ 0x4ee2,
+                              /*manufacturer_string*/ "Google Inc.",
+                              /*product_string*/ "Nexus 5",
+                              /*serial_number*/ "ABC123", std::move(configs));
   }
 
  protected:
-  scoped_refptr<MockUsbDevice> android_phone_;
+  const mojom::UsbDeviceInfo& GetPhoneInfo() {
+    EXPECT_TRUE(android_phone_);
+    return android_phone_->GetDeviceInfo();
+  }
+
+  scoped_refptr<FakeUsbDeviceInfo> android_phone_;
 };
 
 TEST_F(UsbUtilsTest, MatchAny) {
   auto filter = mojom::UsbDeviceFilter::New();
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchVendorId) {
   auto filter = mojom::UsbDeviceFilter::New();
   filter->has_vendor_id = true;
   filter->vendor_id = 0x18d1;
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchVendorIdNegative) {
   auto filter = mojom::UsbDeviceFilter::New();
   filter->has_vendor_id = true;
   filter->vendor_id = 0x1d6b;
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchProductId) {
@@ -68,7 +88,7 @@
   filter->vendor_id = 0x18d1;
   filter->has_product_id = true;
   filter->product_id = 0x4ee2;
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchProductIdNegative) {
@@ -77,21 +97,21 @@
   filter->vendor_id = 0x18d1;
   filter->has_product_id = true;
   filter->product_id = 0x4ee1;
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchInterfaceClass) {
   auto filter = mojom::UsbDeviceFilter::New();
   filter->has_class_code = true;
   filter->class_code = 0xff;
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchInterfaceClassNegative) {
   auto filter = mojom::UsbDeviceFilter::New();
   filter->has_class_code = true;
   filter->class_code = 0xe0;
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchInterfaceSubclass) {
@@ -100,7 +120,7 @@
   filter->class_code = 0xff;
   filter->has_subclass_code = true;
   filter->subclass_code = 0x42;
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchInterfaceSubclassNegative) {
@@ -109,7 +129,7 @@
   filter->class_code = 0xff;
   filter->has_subclass_code = true;
   filter->subclass_code = 0x01;
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchInterfaceProtocol) {
@@ -120,7 +140,7 @@
   filter->subclass_code = 0x42;
   filter->has_protocol_code = true;
   filter->protocol_code = 0x01;
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchInterfaceProtocolNegative) {
@@ -131,26 +151,26 @@
   filter->subclass_code = 0x42;
   filter->has_protocol_code = true;
   filter->protocol_code = 0x02;
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchSerialNumber) {
   auto filter = mojom::UsbDeviceFilter::New();
   filter->serial_number = base::ASCIIToUTF16("ABC123");
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
   filter->has_vendor_id = true;
   filter->vendor_id = 0x18d1;
-  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_TRUE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
   filter->vendor_id = 0x18d2;
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
   filter->vendor_id = 0x18d1;
   filter->serial_number = base::ASCIIToUTF16("DIFFERENT");
-  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, *android_phone_));
+  EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchAnyEmptyList) {
   std::vector<mojom::UsbDeviceFilterPtr> filters;
-  ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, *android_phone_));
+  ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchesAnyVendorId) {
@@ -158,7 +178,7 @@
   filters.push_back(mojom::UsbDeviceFilter::New());
   filters.back()->has_vendor_id = true;
   filters.back()->vendor_id = 0x18d1;
-  ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, *android_phone_));
+  ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, MatchesAnyVendorIdNegative) {
@@ -166,30 +186,23 @@
   filters.push_back(mojom::UsbDeviceFilter::New());
   filters.back()->has_vendor_id = true;
   filters.back()->vendor_id = 0x1d6b;
-  ASSERT_FALSE(UsbDeviceFilterMatchesAny(filters, *android_phone_));
+  ASSERT_FALSE(UsbDeviceFilterMatchesAny(filters, GetPhoneInfo()));
 }
 
 TEST_F(UsbUtilsTest, EndpointDirectionNumberConversion) {
-  UsbInterfaceDescriptor native_interface =
-      android_phone_->configurations()[0].interfaces[0];
-  EXPECT_EQ(2u, native_interface.endpoints.size());
+  auto& alternate =
+      GetPhoneInfo().configurations[0]->interfaces[0]->alternates[0];
+  EXPECT_EQ(2u, alternate->endpoints.size());
 
-  // Check Endpoint 1 IN
-  auto& native_endpoint_1 = native_interface.endpoints[0];
-  auto mojo_endpoint_1 =
-      device::mojom::UsbEndpointInfo::From(native_endpoint_1);
-
-  ASSERT_EQ(ConvertEndpointAddressToNumber(native_endpoint_1), 0x01);
-  ASSERT_EQ(ConvertEndpointNumberToAddress(*mojo_endpoint_1),
-            kEndpointAddress_1);
+  // Check Endpoint 1 INs
+  auto& mojo_endpoint_1 = alternate->endpoints[0];
+  ASSERT_EQ(ConvertEndpointNumberToAddress(*mojo_endpoint_1), 0x81);
+  ASSERT_EQ(0x01, ConvertEndpointAddressToNumber(0x81));
 
   // Check Endpoint 2 OUT
-  auto& native_endpoint_2 = native_interface.endpoints[1];
-  auto mojo_endpoint_2 =
-      device::mojom::UsbEndpointInfo::From(native_endpoint_2);
-  ASSERT_EQ(ConvertEndpointAddressToNumber(native_endpoint_2), 0x02);
-  ASSERT_EQ(ConvertEndpointNumberToAddress(*mojo_endpoint_2),
-            kEndpointAddress_2);
+  auto& mojo_endpoint_2 = alternate->endpoints[1];
+  ASSERT_EQ(ConvertEndpointNumberToAddress(*mojo_endpoint_2), 0x02);
+  ASSERT_EQ(0x02, ConvertEndpointAddressToNumber(0x02));
 }
 
 }  // namespace
diff --git a/docs/origin_trials_integration.md b/docs/origin_trials_integration.md
index f244a2e..028d12a 100644
--- a/docs/origin_trials_integration.md
+++ b/docs/origin_trials_integration.md
@@ -71,6 +71,63 @@
 }
 ```
 
+### Web Feature Counting
+
+Once the feature is created, in order to run the origin trial you need to track
+how often users use your feature. You can do it in two ways.
+
+#### Increment counter in your c++ code.
+
+1. Add your feature counter to end of [web\_feature.mojom]:
+
+```
+enum WebFeature {
+  // ...
+  kLastFeatureBeforeYours = 1235,
+  // Here, increment the last feature count before yours by 1.
+  kMyFeature = 1236,
+
+  kNumberOfFeatures,  // This enum value must be last.
+};
+```
+2. Run [update\_use\_counter\_feature\_enum.py] to update the UMA mapping.
+
+3. Increment your feature counter in c++ code.
+```c++
+#include "third_party/blink/renderer/core/frame/use_counter.h"
+
+// ...
+
+  if (OriginTrials::myFeatureEnabled()) {
+    UseCounter::Count(context, WebFeature::kMyFeature);
+  }
+```
+
+#### Update counter with \[Measure\] IDL attribute
+
+1. Add \[[Measure]\] IDL attribute
+```
+partial interface Navigator {
+  [OriginTrialEnabled=MyFeature, Measure]
+  readonly attribute MyFeatureManager myFeature;
+```
+
+2. The code to increment your feature counter will be generated in V8
+    automatically. But it requires you to follow \[[Measure]\] IDL attribute
+    naming convention when you will add your feature counter to
+    [web\_feature.mojom].
+```
+enum WebFeature {
+  // ...
+  kLastFeatureBeforeYours = 1235,
+  // Here, increment the last feature count before yours by 1.
+  kV8Navigator_MyFeature_AttributeGetter = 1236,
+
+  kNumberOfFeatures,  // This enum value must be last.
+};
+```
+
+
 **NOTE:** Your feature implementation must not persist the result of the enabled
 check. Your code should simply call `OriginTrials::myFeatureEnabled()` as often
 as necessary to gate access to your feature.
@@ -139,3 +196,6 @@
 [origin_trials/webexposed]: /third_party/blink/web_tests/http/tests/origin_trials/webexposed/
 [runtime\_enabled\_features.json5]: /third_party/blink/renderer/platform/runtime_enabled_features.json5
 [trial_token_unittest.cc]: /third_party/blink/common/origin_trials/trial_token_unittest.cc
+[web\_feature.mojom]: /third_party/blink/public/mojom/web_feature/web_feature.mojom
+[update\_use\_counter\_feature\_enum.py]: /tools/metrics/histograms/update_use_counter_feature_enum.py
+[Measure]: /third_party/blink/renderer/bindings/IDLExtendedAttributes.md#Measure_i_m_a_c
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc
index a97320d1..23e4329 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc
@@ -131,6 +131,13 @@
       weak_factory_.GetWeakPtr(), element_instance_id, is_full_page_plugin));
 }
 
+void MimeHandlerViewAttachHelper::GuestEmbedderFrameGone(
+    int32_t element_instance_id) {
+  auto it = pending_guests_.find(element_instance_id);
+  if (it != pending_guests_.end())
+    pending_guests_.erase(it);
+}
+
 // static
 void MimeHandlerViewAttachHelper::CreateFullPageMimeHandlerView(
     int32_t frame_tree_node_id,
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h
index 5ddf78b..7fbf55c 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h
@@ -65,6 +65,16 @@
                                 int32_t element_instance_id,
                                 bool is_full_page_plugin);
 
+  // Called when MimeHandlerViewGuest is about to be destroyed due to its
+  // embedder frame going away. This is specifically relevant during attaching;
+  // from the time the GuestView starts the attach process
+  // (AttachToOuterWebContents) to when the inner WebContents of GuestView
+  // attaches to the outer WebContents, there is a gap where the embedder
+  // RenderFrameHost (parent frame to the outer contents frame) can go away.
+  // MimeHandlerViewAttachHelper should be notified to remove the guest from
+  // |pending_guests_|.
+  void GuestEmbedderFrameGone(int32_t element_instance_id);
+
  private:
   // Called after the content layer finishes preparing a frame for attaching to
   // the embedder WebContents. If |plugin_rfh| is nullptr then attaching is not
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
index 4162ff0..aaf55f9f 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.cc
@@ -25,6 +25,7 @@
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h"
 #include "extensions/browser/process_manager.h"
@@ -109,6 +110,7 @@
   // Before attaching is complete, the instance ID is not valid.
   if (content::MimeHandlerViewMode::UsesCrossProcessFrame() &&
       element_instance_id() != guest_view::kInstanceIDNone) {
+    // If we are awaiting attaching to outer WebContents
     if (GetEmbedderFrame() && GetEmbedderFrame()->GetParent()) {
       // TODO(ekaramad): This should only be needed if the embedder frame is in
       // a plugin element (https://crbug.com/957373).
@@ -367,6 +369,8 @@
                                                     int routing_id) {
   if (process_id == embedder_frame_process_id_ &&
       routing_id == embedder_frame_routing_id_) {
+    MimeHandlerViewAttachHelper::Get(embedder_frame_process_id_)
+        ->GuestEmbedderFrameGone(element_instance_id());
     Destroy(/*also_delete=*/true);
   }
 }
diff --git a/extensions/renderer/app_window_custom_bindings.cc b/extensions/renderer/app_window_custom_bindings.cc
index fa2c5fd5..ed2658a3 100644
--- a/extensions/renderer/app_window_custom_bindings.cc
+++ b/extensions/renderer/app_window_custom_bindings.cc
@@ -92,7 +92,6 @@
     return;
   }
 
-  DCHECK(!app_frame->GetWebFrame()->GetProvisionalDocumentLoader());
   blink::WebDocumentLoader* loader =
       app_frame->GetWebFrame()->GetDocumentLoader();
   if (!loader) {
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index 778a6e1..3b8edfc 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -79,17 +79,7 @@
     const blink::WebLocalFrame* frame) {
   auto& map = FrameDocumentLoaderMap();
   auto it = map.find(frame);
-  blink::WebDocumentLoader* loader = frame->GetDocumentLoader();
-  if (it != map.end()) {
-    loader = it->second;
-    // This and next are temporary DCHECKs which verify that
-    // document loader we have in the map is the same as
-    // (soon to be removed) GetProvisionalDocumentLoader.
-    DCHECK_EQ(loader, frame->GetProvisionalDocumentLoader());
-  } else {
-    DCHECK(!frame->GetProvisionalDocumentLoader());
-  }
-  return loader;
+  return it == map.end() ? frame->GetDocumentLoader() : it->second;
 }
 
 }  // namespace
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 676a15f..f0e7bfa8 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -172,6 +172,21 @@
   }
 }
 
+# Enables use of Goma RBE Prod backend in a builder, with Arbitrary Toolchain
+# Support.
+builder_mixins {
+  name: "goma-rbe-prod-ats"
+  recipe {
+    properties_j: <<END
+    $build/goma: {
+      "server_host": "goma.chromium.org",
+      "rpc_extra_params": "?prod",
+      "enable_ats": true
+    }
+    END
+  }
+}
+
 builder_mixins {
   name: "android"
   dimensions: "os:Android"
@@ -2884,26 +2899,26 @@
     builders {
       name: "Chromium Win Goma RBE Prod"
       dimensions: "os:Windows-10"
-      mixins: "goma-ats"
       mixins: "goma-ci"
+      mixins: "goma-rbe-prod-ats"
     }
     builders {
       name: "Chromium Win Goma RBE Prod (clobber)"
       dimensions: "os:Windows-10"
-      mixins: "goma-ats"
       mixins: "goma-ci"
+      mixins: "goma-rbe-prod-ats"
     }
     builders {
       name: "Chromium Win Goma RBE Prod (dbg)"
       dimensions: "os:Windows-10"
-      mixins: "goma-ats"
       mixins: "goma-ci"
+      mixins: "goma-rbe-prod-ats"
     }
     builders {
       name: "Chromium Win Goma RBE Prod (dbg) (clobber)"
       dimensions: "os:Windows-10"
-      mixins: "goma-ats"
       mixins: "goma-ci"
+      mixins: "goma-rbe-prod-ats"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE ToT"
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn
index 1ff3fb41..316e67c 100644
--- a/ios/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -164,14 +164,3 @@
   ]
   libs = [ "XCTest.framework" ]
 }
-
-source_set("hooks") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources = [
-    "tab_grid_egtests_hook.mm",
-  ]
-  deps = [
-    "//ios/chrome/app:tests_hook",
-  ]
-}
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm
deleted file mode 100644
index 02b2db70..0000000
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm
+++ /dev/null
@@ -1,49 +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.
-
-#include "ios/chrome/app/tests_hook.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace tests_hook {
-
-bool DisableAppGroupAccess() {
-  return true;
-}
-
-bool DisableContentSuggestions() {
-  return true;
-}
-
-bool DisableContextualSearch() {
-  return true;
-}
-
-bool DisableFirstRun() {
-  return true;
-}
-
-bool DisableGeolocation() {
-  return true;
-}
-
-bool DisableSigninRecallPromo() {
-  return true;
-}
-
-bool DisableUpdateService() {
-  return true;
-}
-
-void SetUpTestsIfPresent() {
-  // No-op for Earl Grey.
-}
-
-void RunTestsIfPresent() {
-  // No-op for Earl Grey.
-}
-
-}  // namespace tests_hook
diff --git a/ios/chrome/common/string_util.mm b/ios/chrome/common/string_util.mm
index 5b4aed3c..56153fa 100644
--- a/ios/chrome/common/string_util.mm
+++ b/ios/chrome/common/string_util.mm
@@ -114,15 +114,15 @@
         componentsSeparatedByCharactersInSet:GraphicCharactersSet()]
         componentsJoinedByString:@" "];
   }
-  NSMutableArray* spaceSeparatedCompoments =
+  NSMutableArray* spaceSeparatedComponents =
       [[cleanString componentsSeparatedByCharactersInSet:wspace] mutableCopy];
   ArrayFilterProcedure filter = ^(id object, NSUInteger index, BOOL* stop) {
     return [object isEqualToString:@""];
   };
-  [spaceSeparatedCompoments
-      removeObjectsAtIndexes:[spaceSeparatedCompoments
+  [spaceSeparatedComponents
+      removeObjectsAtIndexes:[spaceSeparatedComponents
                                  indexesOfObjectsPassingTest:filter]];
-  cleanString = [spaceSeparatedCompoments componentsJoinedByString:@" "];
+  cleanString = [spaceSeparatedComponents componentsJoinedByString:@" "];
   return cleanString;
 }
 
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 063080a..d95ab4f5 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -84,7 +84,6 @@
     ":test_support",
     "//ios/chrome/browser/ui/tab_grid:eg_tests",
   ]
-  hooks_target = "//ios/chrome/browser/ui/tab_grid:hooks"
 }
 
 chrome_ios_eg_test("ios_chrome_ui_egtests") {
diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc
index 1001112..d1b75a9 100644
--- a/media/audio/audio_manager_unittest.cc
+++ b/media/audio/audio_manager_unittest.cc
@@ -723,6 +723,8 @@
   AudioOutputStream* stream =
       audio_manager_->MakeAudioOutputStreamProxy(params, "");
   ASSERT_TRUE(stream);
+
+  stream->Close();
 }
 
 #if defined(OS_MACOSX) || defined(USE_CRAS)
diff --git a/media/audio/pulse/audio_manager_pulse.cc b/media/audio/pulse/audio_manager_pulse.cc
index 3700506..d6cdac7 100644
--- a/media/audio/pulse/audio_manager_pulse.cc
+++ b/media/audio/pulse/audio_manager_pulse.cc
@@ -306,7 +306,8 @@
 
   manager->native_input_sample_rate_ = info->sample_spec.rate;
   manager->native_channel_count_ = info->sample_spec.channels;
-  manager->default_source_name_ = info->default_source_name;
+  if (info->default_source_name)
+    manager->default_source_name_ = info->default_source_name;
   pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
 }
 
diff --git a/media/audio/pulse/pulse_util.cc b/media/audio/pulse/pulse_util.cc
index f2e686a..97debbd 100644
--- a/media/audio/pulse/pulse_util.cc
+++ b/media/audio/pulse/pulse_util.cc
@@ -162,8 +162,10 @@
                                 const pa_server_info* info,
                                 void* userdata) {
   DefaultDevicesData* data = static_cast<DefaultDevicesData*>(userdata);
-  data->input_ = info->default_source_name;
-  data->output_ = info->default_sink_name;
+  if (info->default_source_name)
+    data->input_ = info->default_source_name;
+  if (info->default_sink_name)
+    data->output_ = info->default_sink_name;
   pa_threaded_mainloop_signal(data->loop_, 0);
 }
 
diff --git a/media/base/fake_audio_worker_unittest.cc b/media/base/fake_audio_worker_unittest.cc
index 91c3be11..df0363d6 100644
--- a/media/base/fake_audio_worker_unittest.cc
+++ b/media/base/fake_audio_worker_unittest.cc
@@ -116,7 +116,10 @@
 }
 
 // Ensure the time between callbacks is sane.
-TEST_F(FakeAudioWorkerTest, TimeBetweenCallbacks) {
+//
+// TODO(https://crbug.com/960729): Test is flaky because its behavior depends on
+// real wallclock time. Need to mock time to fix this.
+TEST_F(FakeAudioWorkerTest, DISABLED_TimeBetweenCallbacks) {
   message_loop_.task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&FakeAudioWorkerTest::TimeCallbacksOnAudioThread,
diff --git a/media/base/video_frame_layout.cc b/media/base/video_frame_layout.cc
index 27121ce2..cfa26f8 100644
--- a/media/base/video_frame_layout.cc
+++ b/media/base/video_frame_layout.cc
@@ -108,7 +108,8 @@
     const gfx::Size& coded_size,
     std::vector<Plane> planes,
     std::vector<size_t> buffer_sizes,
-    size_t buffer_addr_align) {
+    size_t buffer_addr_align,
+    uint64_t modifier) {
   // NOTE: Even if format is UNKNOWN, it is valid if coded_sizes is not Empty().
   // TODO(crbug.com/896135): Return base::nullopt,
   // if (format != PIXEL_FORMAT_UNKNOWN || !coded_sizes.IsEmpty())
@@ -117,19 +118,21 @@
   // TODO(crbug.com/896135): Return base::nullopt,
   // if (buffer_sizes.size() > planes.size())
   return VideoFrameLayout(format, coded_size, std::move(planes),
-                          std::move(buffer_sizes), buffer_addr_align);
+                          std::move(buffer_sizes), buffer_addr_align, modifier);
 }
 
 VideoFrameLayout::VideoFrameLayout(VideoPixelFormat format,
                                    const gfx::Size& coded_size,
                                    std::vector<Plane> planes,
                                    std::vector<size_t> buffer_sizes,
-                                   size_t buffer_addr_align)
+                                   size_t buffer_addr_align,
+                                   uint64_t modifier)
     : format_(format),
       coded_size_(coded_size),
       planes_(std::move(planes)),
       buffer_sizes_(std::move(buffer_sizes)),
-      buffer_addr_align_(buffer_addr_align) {}
+      buffer_addr_align_(buffer_addr_align),
+      modifier_(modifier) {}
 
 VideoFrameLayout::~VideoFrameLayout() = default;
 VideoFrameLayout::VideoFrameLayout(const VideoFrameLayout&) = default;
@@ -143,15 +146,13 @@
 
 std::ostream& operator<<(std::ostream& ostream,
                          const VideoFrameLayout::Plane& plane) {
-  ostream << "(" << plane.stride << ", " << plane.offset << ", "
-          << plane.modifier << ")";
+  ostream << "(" << plane.stride << ", " << plane.offset << ")";
   return ostream;
 }
 
 bool VideoFrameLayout::Plane::operator==(
     const VideoFrameLayout::Plane& rhs) const {
-  return stride == rhs.stride && offset == rhs.offset &&
-         modifier == rhs.modifier;
+  return stride == rhs.stride && offset == rhs.offset;
 }
 
 bool VideoFrameLayout::Plane::operator!=(
@@ -162,7 +163,8 @@
 bool VideoFrameLayout::operator==(const VideoFrameLayout& rhs) const {
   return format_ == rhs.format_ && coded_size_ == rhs.coded_size_ &&
          planes_ == rhs.planes_ && buffer_sizes_ == rhs.buffer_sizes_ &&
-         buffer_addr_align_ == rhs.buffer_addr_align_;
+         buffer_addr_align_ == rhs.buffer_addr_align_ &&
+         modifier_ == rhs.modifier_;
 }
 
 bool VideoFrameLayout::operator!=(const VideoFrameLayout& rhs) const {
@@ -173,9 +175,9 @@
                          const VideoFrameLayout& layout) {
   ostream << "VideoFrameLayout(format: " << layout.format()
           << ", coded_size: " << layout.coded_size().ToString()
-          << ", planes (stride, offset, modifier): "
-          << VectorToString(layout.planes())
-          << ", buffer_sizes: " << VectorToString(layout.buffer_sizes()) << ")";
+          << ", planes (stride, offset): " << VectorToString(layout.planes())
+          << ", buffer_sizes: " << VectorToString(layout.buffer_sizes())
+          << ", modifier: " << layout.modifier() << ")";
   return ostream;
 }
 
diff --git a/media/base/video_frame_layout.h b/media/base/video_frame_layout.h
index 7dc9bf5..b328490 100644
--- a/media/base/video_frame_layout.h
+++ b/media/base/video_frame_layout.h
@@ -39,8 +39,6 @@
   struct Plane {
     Plane() = default;
     Plane(int32_t stride, size_t offset) : stride(stride), offset(offset) {}
-    Plane(int32_t stride, size_t offset, uint64_t modifier)
-        : stride(stride), offset(offset), modifier(modifier) {}
 
     bool operator==(const Plane& rhs) const;
     bool operator!=(const Plane& rhs) const;
@@ -52,13 +50,6 @@
     // Offset of a plane, which stands for the offset of a start point of a
     // color plane from a buffer fd.
     size_t offset = 0;
-
-    // Modifier of a plane. The modifier is retrieved from GBM library. This can
-    // be a different value from kNoModifier only if the VideoFrame is created
-    // by using NativePixmap.
-    // TODO(crbug.com/914700): All planes share the modifier. Move the modifier
-    // variable from VideoFrameLayout::Plane to VideoFrameLayout.
-    uint64_t modifier = gfx::NativePixmapHandle::kNoModifier;
   };
 
   // Factory functions.
@@ -89,7 +80,8 @@
       const gfx::Size& coded_size,
       std::vector<Plane> planes,
       std::vector<size_t> buffer_sizes = {},
-      size_t buffer_addr_align = kBufferAddressAlignment);
+      size_t buffer_addr_align = kBufferAddressAlignment,
+      uint64_t modifier = gfx::NativePixmapHandle::kNoModifier);
 
   VideoFrameLayout() = delete;
   VideoFrameLayout(const VideoFrameLayout&);
@@ -118,16 +110,17 @@
   bool operator!=(const VideoFrameLayout& rhs) const;
 
   // Returns the required memory alignment for buffers.
-  size_t buffer_addr_align() const {
-    return buffer_addr_align_;
-  }
+  size_t buffer_addr_align() const { return buffer_addr_align_; }
+  // Return the modifier of buffers.
+  uint64_t modifier() const { return modifier_; }
 
  private:
   VideoFrameLayout(VideoPixelFormat format,
                    const gfx::Size& coded_size,
                    std::vector<Plane> planes,
                    std::vector<size_t> buffer_sizes,
-                   size_t buffer_addr_align);
+                   size_t buffer_addr_align,
+                   uint64_t modifier);
 
   VideoPixelFormat format_;
 
@@ -149,6 +142,11 @@
   // allocating physical memory for the buffer, so it doesn't need to be
   // serialized when frames are passed through Mojo.
   size_t buffer_addr_align_;
+
+  // Modifier of buffers. The modifier is retrieved from GBM library. This
+  // can be a different value from kNoModifier only if the VideoFrame is created
+  // by using NativePixmap.
+  uint64_t modifier_;
 };
 
 // Outputs VideoFrameLayout::Plane to stream.
diff --git a/media/base/video_frame_layout_unittest.cc b/media/base/video_frame_layout_unittest.cc
index 790b6ec..a83cf95 100644
--- a/media/base/video_frame_layout_unittest.cc
+++ b/media/base/video_frame_layout_unittest.cc
@@ -240,9 +240,9 @@
       std::to_string(gfx::NativePixmapHandle::kNoModifier);
   EXPECT_EQ(ostream.str(),
             "VideoFrameLayout(format: PIXEL_FORMAT_I420, coded_size: 320x180, "
-            "planes (stride, offset, modifier): [(384, 0, " +
-                kNoModifier + "), (192, 0, " + kNoModifier + "), (192, 0, " +
-                kNoModifier + ")], buffer_sizes: [73728, 18432, 18432])");
+            "planes (stride, offset): [(384, 0), (192, 0), (192, 0)], "
+            "buffer_sizes: [73728, 18432, 18432], modifier: " +
+                kNoModifier + ")");
 }
 
 TEST(VideoFrameLayout, ToStringOneBuffer) {
@@ -261,8 +261,9 @@
       std::to_string(gfx::NativePixmapHandle::kNoModifier);
   EXPECT_EQ(ostream.str(),
             "VideoFrameLayout(format: PIXEL_FORMAT_NV12, coded_size: 320x180, "
-            "planes (stride, offset, modifier): [(384, 100, " +
-                kNoModifier + ")], buffer_sizes: [122880])");
+            "planes (stride, offset): [(384, 100)], buffer_sizes: [122880], "
+            "modifier: " +
+                kNoModifier + ")");
 }
 
 TEST(VideoFrameLayout, ToStringNoBufferInfo) {
@@ -276,9 +277,9 @@
       std::to_string(gfx::NativePixmapHandle::kNoModifier);
   EXPECT_EQ(ostream.str(),
             "VideoFrameLayout(format: PIXEL_FORMAT_NV12, coded_size: 320x180, "
-            "planes (stride, offset, modifier): [(0, 0, " +
-                kNoModifier + "), (0, 0, " + kNoModifier +
-                ")], buffer_sizes: [])");
+            "planes (stride, offset): [(0, 0), (0, 0)], buffer_sizes: [], "
+            "modifier: " +
+                kNoModifier + ")");
 }
 
 TEST(VideoFrameLayout, EqualOperator) {
@@ -287,22 +288,23 @@
   std::vector<size_t> offsets = {0, 100, 200};
   std::vector<size_t> buffer_sizes = {73728, 18432, 18432};
   const size_t align = VideoFrameLayout::kBufferAddressAlignment;
+  const uint64_t modifier = 1;
 
   auto layout = VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
-      buffer_sizes, align);
+      buffer_sizes, align, modifier);
   ASSERT_TRUE(layout.has_value());
 
   auto same_layout = VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
-      buffer_sizes, align);
+      buffer_sizes, align, modifier);
   ASSERT_TRUE(same_layout.has_value());
   EXPECT_EQ(*layout, *same_layout);
 
   std::vector<size_t> another_buffer_sizes = {73728};
   auto different_layout = VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
-      another_buffer_sizes, align);
+      another_buffer_sizes, align, modifier);
   ASSERT_TRUE(different_layout.has_value());
   EXPECT_NE(*layout, *different_layout);
 
@@ -312,6 +314,13 @@
       buffer_sizes, another_align);
   ASSERT_TRUE(different_layout.has_value());
   EXPECT_NE(*layout, *different_layout);
+
+  const size_t another_modifier = 2;
+  different_layout = VideoFrameLayout::CreateWithPlanes(
+      PIXEL_FORMAT_I420, coded_size, CreatePlanes(strides, offsets),
+      buffer_sizes, align, another_modifier);
+  ASSERT_TRUE(different_layout.has_value());
+  EXPECT_NE(*layout, *different_layout);
 }
 
 }  // namespace media
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 9dccf52..b716114 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -283,9 +283,9 @@
     deps += [
       "//build/config/linux/libdrm",
       "//chromeos/dbus/power",
+      "//components/chromeos_camera:mojo_mjpeg_decode_accelerator",
       "//components/chromeos_camera/common",
       "//media/capture/video/chromeos/mojo:cros_camera",
-      "//media/mojo/clients:cros_mjpeg_decode_accelerator",
       "//third_party/libsync",
     ]
   }
diff --git a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
index c70e8ca..1503bd84 100644
--- a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
+++ b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
@@ -6,8 +6,8 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator.h"
 #include "media/base/media_switches.h"
-#include "media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.h"
 
 namespace media {
 
@@ -22,7 +22,8 @@
       send_log_message_cb_(std::move(send_log_message_cb)),
       has_received_decoded_frame_(false),
       next_bitstream_buffer_id_(0),
-      in_buffer_id_(media::MjpegDecodeAccelerator::kInvalidBitstreamBufferId),
+      in_buffer_id_(
+          chromeos_camera::MjpegDecodeAccelerator::kInvalidBitstreamBufferId),
       decoder_status_(INIT_PENDING),
       weak_ptr_factory_(this) {}
 
@@ -147,9 +148,10 @@
   // base::Unretained is safe because |decoder_| is deleted on
   // |decoder_task_runner_|.
   decoder_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&media::MjpegDecodeAccelerator::Decode,
-                                base::Unretained(decoder_.get()), in_buffer,
-                                std::move(out_frame)));
+      FROM_HERE,
+      base::BindOnce(&chromeos_camera::MjpegDecodeAccelerator::Decode,
+                     base::Unretained(decoder_.get()), in_buffer,
+                     std::move(out_frame)));
 }
 
 void VideoCaptureJpegDecoderImpl::VideoFrameReady(int32_t bitstream_buffer_id) {
@@ -171,7 +173,8 @@
                << ", expected " << in_buffer_id_;
     return;
   }
-  in_buffer_id_ = media::MjpegDecodeAccelerator::kInvalidBitstreamBufferId;
+  in_buffer_id_ =
+      chromeos_camera::MjpegDecodeAccelerator::kInvalidBitstreamBufferId;
 
   std::move(decode_done_closure_).Run();
 
@@ -181,7 +184,7 @@
 
 void VideoCaptureJpegDecoderImpl::NotifyError(
     int32_t bitstream_buffer_id,
-    media::MjpegDecodeAccelerator::Error error) {
+    chromeos_camera::MjpegDecodeAccelerator::Error error) {
   DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
   LOG(ERROR) << "Decode error, bitstream_buffer_id=" << bitstream_buffer_id
              << ", error=" << error;
@@ -199,7 +202,7 @@
   jpeg_decoder_factory_.Run(mojo::MakeRequest(&remote_decoder));
 
   base::AutoLock lock(lock_);
-  decoder_ = std::make_unique<media::CrOSMojoMjpegDecodeAccelerator>(
+  decoder_ = std::make_unique<chromeos_camera::MojoMjpegDecodeAccelerator>(
       decoder_task_runner_, remote_decoder.PassInterface());
 
   decoder_->InitializeAsync(
diff --git a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h
index 8bdeaf3..86b14df 100644
--- a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h
+++ b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h
@@ -15,12 +15,12 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "components/chromeos_camera/mojo_mjpeg_decode_accelerator.h"
 #include "gpu/config/gpu_info.h"
 #include "media/capture/capture_export.h"
 #include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
 #include "media/capture/video/chromeos/video_capture_jpeg_decoder.h"
 #include "media/capture/video/video_capture_device_factory.h"
-#include "media/mojo/clients/cros_mojo_mjpeg_decode_accelerator.h"
 
 namespace media {
 
@@ -35,7 +35,7 @@
 // media::VideoCaptureJpegDecoder methods may be called from any thread.
 class CAPTURE_EXPORT VideoCaptureJpegDecoderImpl
     : public VideoCaptureJpegDecoder,
-      public MjpegDecodeAccelerator::Client {
+      public chromeos_camera::MjpegDecodeAccelerator::Client {
  public:
   VideoCaptureJpegDecoderImpl(
       MojoMjpegDecodeAcceleratorFactoryCB jpeg_decoder_factory,
@@ -55,11 +55,12 @@
       base::TimeDelta timestamp,
       media::VideoCaptureDevice::Client::Buffer out_buffer) override;
 
-  // MjpegDecodeAccelerator::Client implementation.
+  // chromeos_camera::MjpegDecodeAccelerator::Client implementation.
   // These will be called on |decoder_task_runner|.
   void VideoFrameReady(int32_t buffer_id) override;
-  void NotifyError(int32_t buffer_id,
-                   media::MjpegDecodeAccelerator::Error error) override;
+  void NotifyError(
+      int32_t buffer_id,
+      chromeos_camera::MjpegDecodeAccelerator::Error error) override;
 
  private:
   void FinishInitialization();
@@ -77,7 +78,7 @@
   scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_;
 
   // The underlying JPEG decode accelerator.
-  std::unique_ptr<media::MjpegDecodeAccelerator> decoder_;
+  std::unique_ptr<chromeos_camera::MjpegDecodeAccelerator> decoder_;
 
   // The callback to run when decode succeeds.
   const DecodeDoneCB decode_done_cb_;
diff --git a/media/filters/video_renderer_algorithm.cc b/media/filters/video_renderer_algorithm.cc
index fa31cc30..115c6382 100644
--- a/media/filters/video_renderer_algorithm.cc
+++ b/media/filters/video_renderer_algorithm.cc
@@ -14,13 +14,12 @@
 const int kMaxOutOfOrderFrameLogs = 10;
 
 VideoRendererAlgorithm::ReadyFrame::ReadyFrame(
-    const scoped_refptr<VideoFrame>& ready_frame)
-    : frame(ready_frame),
+    scoped_refptr<VideoFrame> ready_frame)
+    : frame(std::move(ready_frame)),
       has_estimated_end_time(true),
       ideal_render_count(0),
       render_count(0),
-      drop_count(0) {
-}
+      drop_count(0) {}
 
 VideoRendererAlgorithm::ReadyFrame::ReadyFrame(const ReadyFrame& other) =
     default;
@@ -331,8 +330,7 @@
   return allocation_size;
 }
 
-void VideoRendererAlgorithm::EnqueueFrame(
-    const scoped_refptr<VideoFrame>& frame) {
+void VideoRendererAlgorithm::EnqueueFrame(scoped_refptr<VideoFrame> frame) {
   DCHECK(frame);
   DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM));
 
diff --git a/media/filters/video_renderer_algorithm.h b/media/filters/video_renderer_algorithm.h
index e2fe47c..c6ae18fb 100644
--- a/media/filters/video_renderer_algorithm.h
+++ b/media/filters/video_renderer_algorithm.h
@@ -103,7 +103,7 @@
   // time of the frame based on previous frames or the value of
   // VideoFrameMetadata::FRAME_DURATION if no previous frames, so that
   // EffectiveFramesQueued() is relatively accurate immediately after this call.
-  void EnqueueFrame(const scoped_refptr<VideoFrame>& frame);
+  void EnqueueFrame(scoped_refptr<VideoFrame> frame);
 
   // Removes all frames from the |frame_queue_| and clears predictors.  The
   // algorithm will be as if freshly constructed after this call.  By default
@@ -181,7 +181,7 @@
 
   // Metadata container for enqueued frames.  See |frame_queue_| below.
   struct ReadyFrame {
-    ReadyFrame(const scoped_refptr<VideoFrame>& frame);
+    ReadyFrame(scoped_refptr<VideoFrame> frame);
     ReadyFrame(const ReadyFrame& other);
     ~ReadyFrame();
 
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 6eed6db..02e37b11 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -52,14 +52,8 @@
   sources = [
     "command_buffer_helper.cc",
     "command_buffer_helper.h",
-    "fake_mjpeg_decode_accelerator.cc",
-    "fake_mjpeg_decode_accelerator.h",
     "gles2_decoder_helper.cc",
     "gles2_decoder_helper.h",
-    "gpu_jpeg_encode_accelerator_factory.cc",
-    "gpu_jpeg_encode_accelerator_factory.h",
-    "gpu_mjpeg_decode_accelerator_factory.cc",
-    "gpu_mjpeg_decode_accelerator_factory.h",
     "gpu_video_accelerator_util.cc",
     "gpu_video_accelerator_util.h",
     "gpu_video_decode_accelerator_factory.cc",
@@ -510,79 +504,6 @@
   }
 }
 
-if (use_v4l2_codec || use_vaapi) {
-  test("jpeg_encode_accelerator_unittest") {
-    deps = [
-      "test:helpers",
-      "//base",
-      "//base/test:test_support",
-      "//media:test_support",
-      "//media/gpu",
-      "//mojo/core/embedder",
-      "//testing/gtest",
-      "//third_party:jpeg",
-      "//third_party/libyuv",
-      "//ui/base",
-      "//ui/gfx",
-      "//ui/gfx:test_support",
-      "//ui/gfx/geometry",
-      "//ui/gl",
-      "//ui/gl:test_support",
-    ]
-    configs += [ "//third_party/libyuv:libyuv_config" ]
-    sources = [
-      "jpeg_encode_accelerator_unittest.cc",
-    ]
-    if (use_x11) {
-      deps += [ "//ui/gfx/x" ]
-    }
-    if (use_ozone) {
-      deps += [ "//ui/ozone" ]
-    }
-  }
-}
-
-if (is_chromeos || is_linux) {
-  test("jpeg_decode_accelerator_unittest") {
-    deps = [
-      "test:helpers",
-      "//base",
-      "//media:test_support",
-      "//media/gpu",
-      "//media/mojo/services",
-      "//mojo/core/embedder",
-      "//testing/gtest",
-      "//third_party/libyuv",
-      "//ui/base",
-      "//ui/gfx",
-      "//ui/gfx:test_support",
-      "//ui/gfx/geometry",
-      "//ui/gl",
-      "//ui/gl:test_support",
-    ]
-    configs += [ "//third_party/libyuv:libyuv_config" ]
-    sources = [
-      "jpeg_decode_accelerator_unittest.cc",
-    ]
-    data = [
-      "//media/test/data/peach_pi-1280x720.jpg",
-      "//media/test/data/peach_pi-40x23.jpg",
-      "//media/test/data/peach_pi-41x22.jpg",
-      "//media/test/data/peach_pi-41x23.jpg",
-    ]
-    if (use_vaapi) {
-      deps += [ "//media/gpu/vaapi:jpeg_decoder_unit_test" ]
-      data += [ "//media/test/data/pixel-1280x720.jpg" ]
-    }
-    if (use_x11) {
-      deps += [ "//ui/gfx/x" ]
-    }
-    if (use_ozone) {
-      deps += [ "//ui/ozone" ]
-    }
-  }
-}
-
 static_library("test_support") {
   visibility = [ "//media:test_support" ]
   testonly = true
diff --git a/media/gpu/DEPS b/media/gpu/DEPS
index b01fca3..a50fdb9 100644
--- a/media/gpu/DEPS
+++ b/media/gpu/DEPS
@@ -19,5 +19,9 @@
 
   # SharedImageVideo uses it.
   "+components/viz/common/resources/resource_format_utils.h",
-  "+components/viz/common/resources/resource_sizes.h"
+  "+components/viz/common/resources/resource_sizes.h",
+
+  # Chrome OS specific JEA/MJDA.
+  "+components/chromeos_camera/jpeg_encode_accelerator.h",
+  "+components/chromeos_camera/mjpeg_decode_accelerator.h",
 ]
diff --git a/media/gpu/ipc/common/media_messages.h b/media/gpu/ipc/common/media_messages.h
index cbb7fd3d..3f47f08 100644
--- a/media/gpu/ipc/common/media_messages.h
+++ b/media/gpu/ipc/common/media_messages.h
@@ -14,7 +14,6 @@
 #include "ipc/param_traits_macros.h"
 #include "media/base/overlay_info.h"
 #include "media/gpu/ipc/common/media_param_traits.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 #include "media/video/video_decode_accelerator.h"
 #include "ui/gfx/ipc/color/gfx_param_traits.h"
 #include "ui/gfx/ipc/gfx_param_traits.h"
diff --git a/media/gpu/linux/platform_video_frame_utils.cc b/media/gpu/linux/platform_video_frame_utils.cc
index 9845d91..b9826ce 100644
--- a/media/gpu/linux/platform_video_frame_utils.cc
+++ b/media/gpu/linux/platform_video_frame_utils.cc
@@ -47,18 +47,15 @@
   for (size_t i = 0; i < num_planes; ++i) {
     planes[i].stride = pixmap->GetDmaBufPitch(i);
     planes[i].offset = pixmap->GetDmaBufOffset(i);
-    // TODO(crbug.com/957381): Move the modifier variable to NativePixmapHandle
-    // from NativePixmapPlane.
-    // TODO(crbug.com/914700): Move the modifier vairable from
-    // VideoFrameLayout::Plane to VideoFrameLayout.
-    planes[i].modifier = pixmap->GetBufferFormatModifier();
     buffer_sizes[i] = planes[i].offset +
                       planes[i].stride * VideoFrame::Rows(i, pixel_format,
                                                           coded_size.height());
   }
-
   auto layout = VideoFrameLayout::CreateWithPlanes(
-      pixel_format, coded_size, std::move(planes), std::move(buffer_sizes));
+      pixel_format, coded_size, std::move(planes), std::move(buffer_sizes),
+      VideoFrameLayout::kBufferAddressAlignment,
+      pixmap->GetBufferFormatModifier());
+
   if (!layout)
     return nullptr;
 
@@ -116,11 +113,7 @@
   const size_t num_planes = VideoFrame::NumPlanes(video_frame->format());
   const size_t num_buffers = video_frame->layout().buffer_sizes().size();
   DCHECK_EQ(video_frame->layout().planes().size(), num_planes);
-  // TODO(crbug.com/914700): Move the modifier variable from
-  // VideoFrameLayout::Plane to VideoFrameLayout.
-  handle.native_pixmap_handle.modifier =
-      video_frame->layout().planes()[0].modifier;
-  // TODO(crbug.com/946880): Handles case that num_planes mismatches num_buffers
+  handle.native_pixmap_handle.modifier = video_frame->layout().modifier();
   for (size_t i = 0; i < num_planes; ++i) {
     const auto& plane = video_frame->layout().planes()[i];
     size_t buffer_size = 0;
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.cc b/media/gpu/test/video_player/test_vda_video_decoder.cc
index 19fba7ee..989e915 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.cc
+++ b/media/gpu/test/video_player/test_vda_video_decoder.cc
@@ -61,9 +61,7 @@
   weak_this_factory_.InvalidateWeakPtrs();
 
   // Delete all video frames and related textures.
-  frame_renderer_->AcquireGLContext();
   video_frames_.clear();
-  frame_renderer_->ReleaseGLContext();
 }
 
 std::string TestVDAVideoDecoder::GetDisplayName() const {
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 25b5c0d..03fcb34 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -37,10 +37,6 @@
     "v4l2_h264_accelerator.h",
     "v4l2_image_processor.cc",
     "v4l2_image_processor.h",
-    "v4l2_jpeg_encode_accelerator.cc",
-    "v4l2_jpeg_encode_accelerator.h",
-    "v4l2_mjpeg_decode_accelerator.cc",
-    "v4l2_mjpeg_decode_accelerator.h",
     "v4l2_slice_video_decode_accelerator.cc",
     "v4l2_slice_video_decode_accelerator.h",
     "v4l2_video_decode_accelerator.cc",
@@ -83,6 +79,20 @@
   if (use_v4lplugin) {
     deps += [ ":libv4l2_stubs" ]
   }
+
+  if (is_chromeos) {
+    sources += [
+      "v4l2_jpeg_encode_accelerator.cc",
+      "v4l2_jpeg_encode_accelerator.h",
+      "v4l2_mjpeg_decode_accelerator.cc",
+      "v4l2_mjpeg_decode_accelerator.h",
+    ]
+
+    deps += [
+      "//components/chromeos_camera:jpeg_encode_accelerator",
+      "//components/chromeos_camera:mjpeg_decode_accelerator",
+    ]
+  }
 }
 
 source_set("unit_test") {
diff --git a/media/gpu/v4l2/v4l2_device_unittest.cc b/media/gpu/v4l2/v4l2_device_unittest.cc
index 8f257021..f6c3412 100644
--- a/media/gpu/v4l2/v4l2_device_unittest.cc
+++ b/media/gpu/v4l2/v4l2_device_unittest.cc
@@ -78,20 +78,20 @@
   ASSERT_TRUE(layout.has_value());
   EXPECT_EQ(PIXEL_FORMAT_NV12, layout->format());
   EXPECT_EQ(gfx::Size(300, 180), layout->coded_size());
-  constexpr uint64_t kNoModifier = gfx::NativePixmapHandle::kNoModifier;
   std::vector<VideoFrameLayout::Plane> expected_planes(
-      {{320, 0u, kNoModifier}, {320, 57600u, kNoModifier}});
+      {{320, 0u}, {320, 57600u}});
   EXPECT_EQ(expected_planes, layout->planes());
   EXPECT_EQ(std::vector<size_t>({86400u}), layout->buffer_sizes());
   EXPECT_EQ(86400u, layout->GetTotalBufferSize());
   std::ostringstream ostream;
   ostream << *layout;
-  const std::string kNoModifierStr = std::to_string(kNoModifier);
+  const std::string kNoModifierStr =
+      std::to_string(gfx::NativePixmapHandle::kNoModifier);
   EXPECT_EQ(ostream.str(),
             "VideoFrameLayout(format: PIXEL_FORMAT_NV12, coded_size: 300x180, "
-            "planes (stride, offset, modifier): [(320, 0, " +
-                kNoModifierStr + "), (320, 57600, " + kNoModifierStr +
-                ")], buffer_sizes: [86400])");
+            "planes (stride, offset): [(320, 0), (320, 57600)], buffer_sizes: "
+            "[86400], modifier: " +
+                kNoModifierStr + ")");
 }
 
 // Test V4L2FormatToVideoFrameLayout with YUV420 pixelformat, which has one
@@ -103,23 +103,20 @@
   ASSERT_TRUE(layout.has_value());
   EXPECT_EQ(PIXEL_FORMAT_I420, layout->format());
   EXPECT_EQ(gfx::Size(300, 180), layout->coded_size());
-  constexpr uint64_t kNoModifier = gfx::NativePixmapHandle::kNoModifier;
   std::vector<VideoFrameLayout::Plane> expected_planes(
-      {{320, 0u, kNoModifier},
-       {160, 57600u, kNoModifier},
-       {160, 72000u, kNoModifier}});
+      {{320, 0u}, {160, 57600u}, {160, 72000u}});
   EXPECT_EQ(expected_planes, layout->planes());
   EXPECT_EQ(std::vector<size_t>({86400u}), layout->buffer_sizes());
   EXPECT_EQ(86400u, layout->GetTotalBufferSize());
   std::ostringstream ostream;
   ostream << *layout;
-  const std::string kNoModifierStr = std::to_string(kNoModifier);
+  const std::string kNoModifierStr =
+      std::to_string(gfx::NativePixmapHandle::kNoModifier);
   EXPECT_EQ(ostream.str(),
             "VideoFrameLayout(format: PIXEL_FORMAT_I420, coded_size: 300x180, "
-            "planes (stride, offset, modifier): [(320, 0, " +
-                kNoModifierStr + "), (160, 57600, " + kNoModifierStr +
-                "), (160, 72000, " + kNoModifierStr +
-                ")], buffer_sizes: [86400])");
+            "planes (stride, offset): [(320, 0), (160, 57600), (160, 72000)], "
+            "buffer_sizes: [86400], modifier: " +
+                kNoModifierStr + ")");
 }
 
 // Test V4L2FormatToVideoFrameLayout with single planar v4l2_format.
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
index 0298556..28f7343 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
@@ -965,8 +965,9 @@
   client_->NotifyError(buffer_id, status);
 }
 
-JpegEncodeAccelerator::Status V4L2JpegEncodeAccelerator::Initialize(
-    Client* client) {
+chromeos_camera::JpegEncodeAccelerator::Status
+V4L2JpegEncodeAccelerator::Initialize(
+    chromeos_camera::JpegEncodeAccelerator::Client* client) {
   DCHECK(child_task_runner_->BelongsToCurrentThread());
 
   std::unique_ptr<EncodedInstance> encoded_device(new EncodedInstance(this));
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
index a380330..653e8f5 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.h
@@ -17,13 +17,13 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/unaligned_shared_memory.h"
 #include "media/base/video_frame.h"
 #include "media/filters/jpeg_parser.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/v4l2/v4l2_device.h"
-#include "media/video/jpeg_encode_accelerator.h"
 
 namespace {
 
@@ -46,14 +46,15 @@
 namespace media {
 
 class MEDIA_GPU_EXPORT V4L2JpegEncodeAccelerator
-    : public JpegEncodeAccelerator {
+    : public chromeos_camera::JpegEncodeAccelerator {
  public:
   V4L2JpegEncodeAccelerator(
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
   ~V4L2JpegEncodeAccelerator() override;
 
   // JpegEncodeAccelerator implementation.
-  JpegEncodeAccelerator::Status Initialize(Client* client) override;
+  chromeos_camera::JpegEncodeAccelerator::Status Initialize(
+      chromeos_camera::JpegEncodeAccelerator::Client* client) override;
   size_t GetMaxCodedBufferSize(const gfx::Size& picture_size) override;
   void Encode(scoped_refptr<media::VideoFrame> video_frame,
               int quality,
@@ -266,7 +267,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   // The client of this class.
-  Client* client_;
+  chromeos_camera::JpegEncodeAccelerator::Client* client_;
 
   // Thread to communicate with the device.
   base::Thread encoder_thread_;
diff --git a/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc b/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc
index 7b4f30f..a6b3f038 100644
--- a/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc
@@ -201,7 +201,8 @@
                                 weak_ptr_, bitstream_buffer_id, error));
 }
 
-bool V4L2MjpegDecodeAccelerator::Initialize(Client* client) {
+bool V4L2MjpegDecodeAccelerator::Initialize(
+    chromeos_camera::MjpegDecodeAccelerator::Client* client) {
   DCHECK(child_task_runner_->BelongsToCurrentThread());
 
   if (!device_->Open(V4L2Device::Type::kJpegDecoder, V4L2_PIX_FMT_JPEG)) {
diff --git a/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.h b/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.h
index c41dd7e..edd6ff8 100644
--- a/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.h
@@ -17,17 +17,17 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/unaligned_shared_memory.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/v4l2/v4l2_device.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 
 namespace media {
 
 class MEDIA_GPU_EXPORT V4L2MjpegDecodeAccelerator
-    : public MjpegDecodeAccelerator {
+    : public chromeos_camera::MjpegDecodeAccelerator {
  public:
   V4L2MjpegDecodeAccelerator(
       const scoped_refptr<V4L2Device>& device,
@@ -35,7 +35,8 @@
   ~V4L2MjpegDecodeAccelerator() override;
 
   // MjpegDecodeAccelerator implementation.
-  bool Initialize(Client* client) override;
+  bool Initialize(
+      chromeos_camera::MjpegDecodeAccelerator::Client* client) override;
   void Decode(const BitstreamBuffer& bitstream_buffer,
               const scoped_refptr<VideoFrame>& video_frame) override;
   bool IsSupported() override;
@@ -146,7 +147,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   // The client of this class.
-  Client* client_;
+  chromeos_camera::MjpegDecodeAccelerator::Client* client_;
 
   // The V4L2Device this class is operating upon.
   scoped_refptr<V4L2Device> device_;
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 0ebd327..65c4feb 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -45,12 +45,8 @@
     "vaapi_jpeg_decode_accelerator_worker.h",
     "vaapi_jpeg_decoder.cc",
     "vaapi_jpeg_decoder.h",
-    "vaapi_jpeg_encode_accelerator.cc",
-    "vaapi_jpeg_encode_accelerator.h",
     "vaapi_jpeg_encoder.cc",
     "vaapi_jpeg_encoder.h",
-    "vaapi_mjpeg_decode_accelerator.cc",
-    "vaapi_mjpeg_decode_accelerator.h",
     "vaapi_picture.cc",
     "vaapi_picture.h",
     "vaapi_picture_factory.cc",
@@ -87,6 +83,20 @@
     "//ui/gfx/geometry",
   ]
 
+  if (is_chromeos) {
+    sources += [
+      "vaapi_jpeg_encode_accelerator.cc",
+      "vaapi_jpeg_encode_accelerator.h",
+      "vaapi_mjpeg_decode_accelerator.cc",
+      "vaapi_mjpeg_decode_accelerator.h",
+    ]
+
+    deps += [
+      "//components/chromeos_camera:jpeg_encode_accelerator",
+      "//components/chromeos_camera:mjpeg_decode_accelerator",
+    ]
+  }
+
   if (is_linux) {
     configs += [ "//build/config/linux/libva" ]
     deps += [ "//media/gpu/linux" ]
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index 26a6060..9443d3f 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -233,8 +233,9 @@
   client_->VideoFrameReady(buffer_id, encoded_picture_size);
 }
 
-JpegEncodeAccelerator::Status VaapiJpegEncodeAccelerator::Initialize(
-    JpegEncodeAccelerator::Client* client) {
+chromeos_camera::JpegEncodeAccelerator::Status
+VaapiJpegEncodeAccelerator::Initialize(
+    chromeos_camera::JpegEncodeAccelerator::Client* client) {
   VLOGF(2);
   DCHECK(task_runner_->BelongsToCurrentThread());
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
index 0f94d30..ea10ddb 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h
@@ -10,11 +10,11 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
+#include "components/chromeos_camera/jpeg_encode_accelerator.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/unaligned_shared_memory.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/vaapi/vaapi_wrapper.h"
-#include "media/video/jpeg_encode_accelerator.h"
 
 namespace media {
 
@@ -28,14 +28,15 @@
 // a weak this can be run on the encoder thread because it can assume
 // VaapiJpegEncodeAccelerator is still alive.
 class MEDIA_GPU_EXPORT VaapiJpegEncodeAccelerator
-    : public JpegEncodeAccelerator {
+    : public chromeos_camera::JpegEncodeAccelerator {
  public:
   explicit VaapiJpegEncodeAccelerator(
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
   ~VaapiJpegEncodeAccelerator() override;
 
   // JpegEncodeAccelerator implementation.
-  Status Initialize(JpegEncodeAccelerator::Client* client) override;
+  chromeos_camera::JpegEncodeAccelerator::Status Initialize(
+      chromeos_camera::JpegEncodeAccelerator::Client* client) override;
   size_t GetMaxCodedBufferSize(const gfx::Size& picture_size) override;
 
   // Currently only I420 format is supported for |video_frame|.
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index 07ec901..0b3253fb 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -45,23 +45,23 @@
 }
 
 static void ReportToVAJDAResponseToClientUMA(
-    MjpegDecodeAccelerator::Error response) {
+    chromeos_camera::MjpegDecodeAccelerator::Error response) {
   UMA_HISTOGRAM_ENUMERATION(
       "Media.VAJDA.ResponseToClient", response,
-      MjpegDecodeAccelerator::Error::MJDA_ERROR_CODE_MAX + 1);
+      chromeos_camera::MjpegDecodeAccelerator::Error::MJDA_ERROR_CODE_MAX + 1);
 }
 
-static MjpegDecodeAccelerator::Error VaapiJpegDecodeStatusToError(
-    VaapiJpegDecodeStatus status) {
+static chromeos_camera::MjpegDecodeAccelerator::Error
+VaapiJpegDecodeStatusToError(VaapiJpegDecodeStatus status) {
   switch (status) {
     case VaapiJpegDecodeStatus::kSuccess:
-      return MjpegDecodeAccelerator::Error::NO_ERRORS;
+      return chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS;
     case VaapiJpegDecodeStatus::kParseJpegFailed:
-      return MjpegDecodeAccelerator::Error::PARSE_JPEG_FAILED;
+      return chromeos_camera::MjpegDecodeAccelerator::Error::PARSE_JPEG_FAILED;
     case VaapiJpegDecodeStatus::kUnsupportedSubsampling:
-      return MjpegDecodeAccelerator::Error::UNSUPPORTED_JPEG;
+      return chromeos_camera::MjpegDecodeAccelerator::Error::UNSUPPORTED_JPEG;
     default:
-      return MjpegDecodeAccelerator::Error::PLATFORM_FAILURE;
+      return chromeos_camera::MjpegDecodeAccelerator::Error::PLATFORM_FAILURE;
   }
 }
 
@@ -94,7 +94,7 @@
   VLOGF(1) << "Notifying of error " << error;
   // |error| shouldn't be NO_ERRORS because successful decodes should be handled
   // by VideoFrameReady().
-  DCHECK_NE(MjpegDecodeAccelerator::Error::NO_ERRORS, error);
+  DCHECK_NE(chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS, error);
   ReportToVAJDAResponseToClientUMA(error);
   DCHECK(client_);
   client_->NotifyError(bitstream_buffer_id, error);
@@ -102,7 +102,8 @@
 
 void VaapiMjpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  ReportToVAJDAResponseToClientUMA(MjpegDecodeAccelerator::Error::NO_ERRORS);
+  ReportToVAJDAResponseToClientUMA(
+      chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS);
   client_->VideoFrameReady(bitstream_buffer_id);
 }
 
@@ -122,7 +123,8 @@
   decoder_thread_.Stop();
 }
 
-bool VaapiMjpegDecodeAccelerator::Initialize(Client* client) {
+bool VaapiMjpegDecodeAccelerator::Initialize(
+    chromeos_camera::MjpegDecodeAccelerator::Client* client) {
   VLOGF(2);
   DCHECK(task_runner_->BelongsToCurrentThread());
 
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.h b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.h
index 06b03c2..786bb2a5 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.h
@@ -13,9 +13,9 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -37,14 +37,15 @@
 // stopped during |this->Destroy()|, so any tasks posted to the decoder thread
 // can assume |*this| is still alive.  See |weak_this_| below for more details.
 class MEDIA_GPU_EXPORT VaapiMjpegDecodeAccelerator
-    : public MjpegDecodeAccelerator {
+    : public chromeos_camera::MjpegDecodeAccelerator {
  public:
   VaapiMjpegDecodeAccelerator(
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
   ~VaapiMjpegDecodeAccelerator() override;
 
-  // MjpegDecodeAccelerator implementation.
-  bool Initialize(MjpegDecodeAccelerator::Client* client) override;
+  // chromeos_camera::MjpegDecodeAccelerator implementation.
+  bool Initialize(
+      chromeos_camera::MjpegDecodeAccelerator::Client* client) override;
   void Decode(const BitstreamBuffer& bitstream_buffer,
               const scoped_refptr<VideoFrame>& video_frame) override;
   bool IsSupported() override;
@@ -77,7 +78,7 @@
   const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   // The client of this class.
-  Client* client_;
+  chromeos_camera::MjpegDecodeAccelerator::Client* client_;
 
   VaapiJpegDecoder decoder_;
 
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index bd1a74f..c0a5a9d 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -64,18 +64,9 @@
                             VAVDA_DECODER_FAILURES_MAX + 1);
 }
 
-// Returns true if the CPU is an Intel Kaby/Gemini/Sky Lake or later.
-// Cpu platform id's are referenced from the following file in kernel source
-// arch/x86/include/asm/intel-family.h
-bool IsKabyLakeOrLater() {
-  constexpr int kPentiumAndLaterFamily = 0x06;
-  constexpr int kFirstKabyLakeModelId = 0x8E;
-  static base::CPU cpuid;
-  static bool is_kaby_lake_or_later =
-      cpuid.family() == kPentiumAndLaterFamily &&
-      cpuid.model() >= kFirstKabyLakeModelId;
-  return is_kaby_lake_or_later;
-}
+// Returns true if the CPU is an Intel Gemini Lake or later (including Kaby
+// Lake) Cpu platform id's are referenced from the following file in kernel
+// source arch/x86/include/asm/intel-family.h
 bool IsGeminiLakeOrLater() {
   constexpr int kPentiumAndLaterFamily = 0x06;
   constexpr int kGeminiLakeModelId = 0x7A;
@@ -527,7 +518,7 @@
     requested_num_pics_ = num_pics - num_reference_frames + 1;
   } else {
     requested_num_reference_frames_ = 0;
-    requested_num_pics_ = num_pics;
+    requested_num_pics_ = num_pics + num_extra_pics_;
   }
 
   VLOGF(2) << " |requested_num_pics_| = " << requested_num_pics_
@@ -1069,19 +1060,23 @@
 }
 
 VaapiVideoDecodeAccelerator::BufferAllocationMode
-VaapiVideoDecodeAccelerator::DecideBufferAllocationMode() const {
+VaapiVideoDecodeAccelerator::DecideBufferAllocationMode() {
   // TODO(crbug.com/912295): Enable a better BufferAllocationMode for IMPORT
   // |output_mode_| as well.
   if (output_mode_ == VideoDecodeAccelerator::Config::OutputMode::IMPORT)
     return BufferAllocationMode::kNormal;
 
-  // On KabyLake, GeminiLake and later we can pass to libva the client's
+  // On Gemini Lake, Kaby Lake and later we can pass to libva the client's
   // PictureBuffers to decode onto, which skips the use of the Vpp unit and its
   // associated format reconciliation copy, avoiding all internal buffer
-  // allocations.
-  // TODO(crbug.com/822346,crbug.com/910986): Enable other codecs/platforms.
-  if ((IsKabyLakeOrLater() || IsGeminiLakeOrLater()) &&
-      profile_ == VP9PROFILE_PROFILE0) {
+  // allocations.  This only works for H264, VP8 and VP9.
+  // TODO(crbug.com/911754): Enable for VP9 Profile 2.
+  if (IsGeminiLakeOrLater() && profile_ != VP9PROFILE_PROFILE2) {
+    // Add one to the reference frames for the one being currently egressed, and
+    // an extra allocation for both |client_| and |decoder_|, see
+    // crrev.com/c/1576560.
+    if (profile_ != VP9PROFILE_PROFILE0)
+      num_extra_pics_ = 3;
     return BufferAllocationMode::kNone;
   }
 
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.h b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
index aa4e1a9f..f4b00b4 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.h
@@ -207,7 +207,7 @@
 
   // Decides the concrete buffer allocation mode, depending on the hardware
   // platform and other parameters.
-  BufferAllocationMode DecideBufferAllocationMode() const;
+  BufferAllocationMode DecideBufferAllocationMode();
   bool IsBufferAllocationModeReducedOrSuperReduced() const;
 
   // VAVDA state.
@@ -316,6 +316,10 @@
   // Last requested number/resolution of output PictureBuffers.
   size_t requested_num_pics_;
   gfx::Size requested_pic_size_;
+  // Potential extra PictureBuffers to request, used only on
+  // BufferAllocationMode::kNone, see DecideBufferAllocationMode().
+  size_t num_extra_pics_ = 0;
+
   // Max number of reference frames needed by |decoder_|. Only used on
   // |task_runner_| and when in BufferAllocationMode::kNone.
   size_t requested_num_reference_frames_;
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index d9e29195..e98390d 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -24,11 +24,11 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
+#include "components/chromeos_camera/mjpeg_decode_accelerator.h"
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/media_gpu_export.h"
 #include "media/gpu/vaapi/va_surface.h"
-#include "media/video/mjpeg_decode_accelerator.h"
 #include "media/video/video_decode_accelerator.h"
 #include "media/video/video_encode_accelerator.h"
 #include "ui/gfx/geometry/size.h"
diff --git a/media/mojo/DEPS b/media/mojo/DEPS
index 703a133d..258ba11 100644
--- a/media/mojo/DEPS
+++ b/media/mojo/DEPS
@@ -15,9 +15,3 @@
   # media/mojo is not part of "media" target and should not use MEDIA_EXPORT.
   "-media/base/media_export.h"
 ]
-
-specific_include_rules = {
-  "cros_mojo_mjpeg_decode_accelerator\.h": [
-    "+components/chromeos_camera/common",
-  ],
-}
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index 1551732..e5d488f1 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -88,26 +88,6 @@
   }
 }
 
-if (is_chromeos) {
-  source_set("cros_mjpeg_decode_accelerator") {
-    visibility = [
-      "//content/browser",
-      "//media/capture:capture_lib",
-    ]
-
-    sources = [
-      "cros_mojo_mjpeg_decode_accelerator.cc",
-      "cros_mojo_mjpeg_decode_accelerator.h",
-    ]
-
-    deps = [
-      "//base",
-      "//components/chromeos_camera/common",
-      "//media/mojo/interfaces",
-    ]
-  }
-}
-
 source_set("unit_tests") {
   testonly = true
 
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 8f9f0ffc..70ade833 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -114,16 +114,6 @@
     ]
   }
 
-  if (is_chromeos) {
-    sources += [
-      "cros_mojo_jpeg_encode_accelerator_service.cc",
-      "cros_mojo_jpeg_encode_accelerator_service.h",
-      "cros_mojo_mjpeg_decode_accelerator_service.cc",
-      "cros_mojo_mjpeg_decode_accelerator_service.h",
-    ]
-    deps += [ "//components/chromeos_camera/common" ]
-  }
-
   if (enable_library_cdms) {
     sources += [
       "cdm_service.cc",
@@ -228,7 +218,9 @@
   }
 
   if (is_chromeos) {
-    sources += [ "cros_mojo_mjpeg_decode_accelerator_service_unittest.cc" ]
+    deps += [
+      "//components/chromeos_camera:mjpeg_decode_accelerator_service_unittest",
+    ]
   }
 }
 
diff --git a/media/mojo/services/DEPS b/media/mojo/services/DEPS
index f4d79477c..f3ef946 100644
--- a/media/mojo/services/DEPS
+++ b/media/mojo/services/DEPS
@@ -2,11 +2,5 @@
   "media_manifest\.cc": [
     "+chromecast/common/mojom",
   ],
-  "cros_mojo_jpeg_encode_accelerator_service\.h": [
-    "+components/chromeos_camera/common",
-  ],
-  "cros_mojo_mjpeg_decode_accelerator_service\.h": [
-    "+components/chromeos_camera/common",
-  ],
 }
 
diff --git a/media/video/BUILD.gn b/media/video/BUILD.gn
index 6b9bc81..10172be 100644
--- a/media/video/BUILD.gn
+++ b/media/video/BUILD.gn
@@ -29,10 +29,6 @@
     "h264_poc.h",
     "half_float_maker.cc",
     "half_float_maker.h",
-    "jpeg_encode_accelerator.cc",
-    "jpeg_encode_accelerator.h",
-    "mjpeg_decode_accelerator.cc",
-    "mjpeg_decode_accelerator.h",
     "picture.cc",
     "picture.h",
     "supported_video_decoder_config.cc",
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc
index ddfad0b..e9e7777 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
@@ -71,7 +71,7 @@
   // asynchronously posting tasks to |worker_task_runner_|, while
   // |frame_ready_cb| will be called on |media_task_runner_| once all the data
   // has been copied.
-  void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame,
+  void CreateHardwareFrame(scoped_refptr<VideoFrame> video_frame,
                            FrameReadyCB cb);
 
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
@@ -129,7 +129,7 @@
     VideoFrameCopyRequest(scoped_refptr<VideoFrame> video_frame,
                           FrameReadyCB frame_ready_cb,
                           bool passthrough)
-        : video_frame(video_frame),
+        : video_frame(std::move(video_frame)),
           frame_ready_cb(std::move(frame_ready_cb)),
           passthrough(passthrough) {}
     scoped_refptr<VideoFrame> video_frame;
@@ -144,19 +144,18 @@
 
   // Copy |video_frame| data into |frame_resources| and calls |frame_ready_cb|
   // when done.
-  void CopyVideoFrameToGpuMemoryBuffers(
-      const scoped_refptr<VideoFrame>& video_frame,
-      FrameResources* frame_resources);
+  void CopyVideoFrameToGpuMemoryBuffers(scoped_refptr<VideoFrame> video_frame,
+                                        FrameResources* frame_resources);
 
   // Called when all the data has been copied.
-  void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame,
+  void OnCopiesDone(scoped_refptr<VideoFrame> video_frame,
                     FrameResources* frame_resources);
 
   // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new
   // VideoFrame. This has to be run on |media_task_runner_| where
   // |frame_ready_cb| associated with video_frame will also be run.
   void BindAndCreateMailboxesHardwareFrameResources(
-      const scoped_refptr<VideoFrame>& video_frame,
+      scoped_refptr<VideoFrame> video_frame,
       FrameResources* frame_resources);
 
   // Return true if |resources| can be used to represent a frame for
@@ -178,7 +177,7 @@
   // |frame_copy_requests_| and attempts to start another copy if there are
   // other |frame_copy_requests_| elements.
   void CompleteCopyRequestAndMaybeStartNextCopy(
-      const scoped_refptr<VideoFrame>& video_frame);
+      scoped_refptr<VideoFrame> video_frame);
 
   // Callback called when a VideoFrame generated with GetFrameResources is no
   // longer referenced.
@@ -380,7 +379,7 @@
 void CopyRowsToNV12Buffer(int first_row,
                           int rows,
                           int bytes_per_row,
-                          const scoped_refptr<VideoFrame>& source_frame,
+                          const VideoFrame* source_frame,
                           uint8_t* dest_y,
                           int dest_stride_y,
                           uint8_t* dest_uv,
@@ -418,7 +417,7 @@
 void CopyRowsToUYVYBuffer(int first_row,
                           int rows,
                           int width,
-                          const scoped_refptr<VideoFrame>& source_frame,
+                          const VideoFrame* source_frame,
                           uint8_t* output,
                           int dest_stride,
                           base::OnceClosure done) {
@@ -451,7 +450,7 @@
                            int first_row,
                            int rows,
                            int width,
-                           const scoped_refptr<VideoFrame>& source_frame,
+                           const VideoFrame* source_frame,
                            uint8_t* output,
                            int dest_stride,
                            base::OnceClosure done) {
@@ -510,7 +509,7 @@
                           int first_row,
                           int rows,
                           int width,
-                          const scoped_refptr<VideoFrame>& source_frame,
+                          const VideoFrame* source_frame,
                           uint8_t* output,
                           int dest_stride,
                           base::OnceClosure done) {
@@ -546,7 +545,7 @@
            1 /* attenuate, meaning premultiply */);
 }
 
-gfx::Size CodedSize(const scoped_refptr<VideoFrame>& video_frame,
+gfx::Size CodedSize(const VideoFrame* video_frame,
                     GpuVideoAcceleratorFactories::OutputFormat output_format) {
   DCHECK(gfx::Rect(video_frame->coded_size())
              .Contains(video_frame->visible_rect()));
@@ -582,7 +581,7 @@
 // |frame_ready_cb|.
 // This has to be called on the thread where |media_task_runner_| is current.
 void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
-    const scoped_refptr<VideoFrame>& video_frame,
+    scoped_refptr<VideoFrame> video_frame,
     FrameReadyCB frame_ready_cb) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
   // Lazily initialize |output_format_| since VideoFrameOutputFormat() has to be
@@ -593,7 +592,7 @@
   // Bail if we have a change of GpuVideoAcceleratorFactories::OutputFormat;
   // such changes should not happen in general (see https://crbug.com/875158).
   if (output_format_ != gpu_factories_->VideoFrameOutputFormat(pixel_format)) {
-    std::move(frame_ready_cb).Run(video_frame);
+    std::move(frame_ready_cb).Run(std::move(video_frame));
     return;
   }
 
@@ -667,8 +666,8 @@
     passthrough = true;
   }
 
-  frame_copy_requests_.emplace_back(video_frame, std::move(frame_ready_cb),
-                                    passthrough);
+  frame_copy_requests_.emplace_back(std::move(video_frame),
+                                    std::move(frame_ready_cb), passthrough);
   if (frame_copy_requests_.size() == 1u)
     StartCopy();
 }
@@ -717,7 +716,7 @@
 }
 
 void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone(
-    const scoped_refptr<VideoFrame>& video_frame,
+    scoped_refptr<VideoFrame> video_frame,
     FrameResources* frame_resources) {
   for (const auto& plane_resource : frame_resources->plane_resources) {
     if (plane_resource.gpu_memory_buffer) {
@@ -733,7 +732,7 @@
   media_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources,
-                     this, video_frame, frame_resources));
+                     this, std::move(video_frame), frame_resources));
 }
 
 void GpuMemoryBufferVideoFramePool::PoolImpl::StartCopy() {
@@ -747,10 +746,10 @@
         request.passthrough
             ? nullptr
             : GetOrCreateFrameResources(
-                  CodedSize(request.video_frame, output_format_),
+                  CodedSize(request.video_frame.get(), output_format_),
                   output_format_);
     if (!frame_resources) {
-      std::move(request.frame_ready_cb).Run(request.video_frame);
+      std::move(request.frame_ready_cb).Run(std::move(request.video_frame));
       frame_copy_requests_.pop_front();
       continue;
     }
@@ -766,12 +765,12 @@
 // that will be synchronized by a barrier.
 // After the barrier is passed OnCopiesDone will be called.
 void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers(
-    const scoped_refptr<VideoFrame>& video_frame,
+    scoped_refptr<VideoFrame> video_frame,
     FrameResources* frame_resources) {
   // Compute the number of tasks to post and create the barrier.
   const size_t num_planes = VideoFrame::NumPlanes(VideoFormat(output_format_));
   const size_t planes_per_copy = PlanesPerCopy(output_format_);
-  const gfx::Size coded_size = CodedSize(video_frame, output_format_);
+  const gfx::Size coded_size = CodedSize(video_frame.get(), output_format_);
   size_t copies = 0;
   for (size_t i = 0; i < num_planes; i += planes_per_copy) {
     const int rows =
@@ -783,6 +782,7 @@
       ++copies;
   }
 
+  // |barrier| keeps refptr of |video_frame| until all copy tasks are done.
   const base::RepeatingClosure barrier = base::BarrierClosure(
       copies, base::BindOnce(&PoolImpl::OnCopiesDone, this, video_frame,
                              frame_resources));
@@ -827,32 +827,41 @@
           break;
         }
         case GpuVideoAcceleratorFactories::OutputFormat::NV12_SINGLE_GMB:
+          // Using base::Unretained(video_frame) here is safe because |barrier|
+          // keeps refptr of |video_frame| until all copy tasks are done.
           worker_task_runner_->PostTask(
               FROM_HERE,
               base::BindOnce(
                   &CopyRowsToNV12Buffer, row, rows_to_copy, coded_size.width(),
-                  video_frame, static_cast<uint8_t*>(buffer->memory(0)),
-                  buffer->stride(0), static_cast<uint8_t*>(buffer->memory(1)),
-                  buffer->stride(1), barrier));
+                  base::Unretained(video_frame.get()),
+                  static_cast<uint8_t*>(buffer->memory(0)), buffer->stride(0),
+                  static_cast<uint8_t*>(buffer->memory(1)), buffer->stride(1),
+                  barrier));
           break;
         case GpuVideoAcceleratorFactories::OutputFormat::NV12_DUAL_GMB: {
           gfx::GpuMemoryBuffer* buffer2 =
               frame_resources->plane_resources[1].gpu_memory_buffer.get();
+          // Using base::Unretained(video_frame) here is safe because |barrier|
+          // keeps refptr of |video_frame| until all copy tasks are done.
           worker_task_runner_->PostTask(
               FROM_HERE,
               base::BindOnce(
                   &CopyRowsToNV12Buffer, row, rows_to_copy, coded_size.width(),
-                  video_frame, static_cast<uint8_t*>(buffer->memory(0)),
-                  buffer->stride(0), static_cast<uint8_t*>(buffer2->memory(0)),
-                  buffer2->stride(0), barrier));
+                  base::Unretained(video_frame.get()),
+                  static_cast<uint8_t*>(buffer->memory(0)), buffer->stride(0),
+                  static_cast<uint8_t*>(buffer2->memory(0)), buffer2->stride(0),
+                  barrier));
           break;
         }
 
         case GpuVideoAcceleratorFactories::OutputFormat::UYVY:
+          // Using base::Unretained(video_frame) here is safe because |barrier|
+          // keeps refptr of |video_frame| until all copy tasks are done.
           worker_task_runner_->PostTask(
               FROM_HERE,
               base::BindOnce(&CopyRowsToUYVYBuffer, row, rows_to_copy,
-                             coded_size.width(), video_frame,
+                             coded_size.width(),
+                             base::Unretained(video_frame.get()),
                              static_cast<uint8_t*>(buffer->memory(0)),
                              buffer->stride(0), barrier));
           break;
@@ -861,10 +870,13 @@
         case GpuVideoAcceleratorFactories::OutputFormat::XB30: {
           const bool is_argb = output_format_ ==
                                GpuVideoAcceleratorFactories::OutputFormat::XR30;
+          // Using base::Unretained(video_frame) here is safe because |barrier|
+          // keeps refptr of |video_frame| until all copy tasks are done.
           worker_task_runner_->PostTask(
               FROM_HERE,
               base::BindOnce(&CopyRowsToRGB10Buffer, is_argb, row, rows_to_copy,
-                             coded_size.width(), video_frame,
+                             coded_size.width(),
+                             base::Unretained(video_frame.get()),
                              static_cast<uint8_t*>(buffer->memory(0)),
                              buffer->stride(0), barrier));
           break;
@@ -874,10 +886,13 @@
         case GpuVideoAcceleratorFactories::OutputFormat::BGRA: {
           const bool is_rgba = output_format_ ==
                                GpuVideoAcceleratorFactories::OutputFormat::RGBA;
+          // Using base::Unretained(video_frame) here is safe because |barrier|
+          // keeps refptr of |video_frame| until all copy tasks are done.
           worker_task_runner_->PostTask(
               FROM_HERE,
               base::BindOnce(&CopyRowsToRGBABuffer, is_rgba, row, rows_to_copy,
-                             coded_size.width(), video_frame,
+                             coded_size.width(),
+                             base::Unretained(video_frame.get()),
                              static_cast<uint8_t*>(buffer->memory(0)),
                              buffer->stride(0), barrier));
           break;
@@ -892,16 +907,16 @@
 
 void GpuMemoryBufferVideoFramePool::PoolImpl::
     BindAndCreateMailboxesHardwareFrameResources(
-        const scoped_refptr<VideoFrame>& video_frame,
+        scoped_refptr<VideoFrame> video_frame,
         FrameResources* frame_resources) {
   gpu::SharedImageInterface* sii = gpu_factories_->SharedImageInterface();
   if (!sii) {
     frame_resources->MarkUnused(tick_clock_->NowTicks());
-    CompleteCopyRequestAndMaybeStartNextCopy(video_frame);
+    CompleteCopyRequestAndMaybeStartNextCopy(std::move(video_frame));
     return;
   }
 
-  const gfx::Size coded_size = CodedSize(video_frame, output_format_);
+  const gfx::Size coded_size = CodedSize(video_frame.get(), output_format_);
   gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
   // Set up the planes creating the mailboxes needed to refer to the textures.
   for (size_t i = 0; i < NumGpuMemoryBuffers(output_format_); i++) {
@@ -945,7 +960,7 @@
   if (!frame) {
     frame_resources->MarkUnused(tick_clock_->NowTicks());
     MailboxHoldersReleased(frame_resources, gpu::SyncToken());
-    CompleteCopyRequestAndMaybeStartNextCopy(video_frame);
+    CompleteCopyRequestAndMaybeStartNextCopy(std::move(video_frame));
     return;
   }
   frame->SetReleaseMailboxCB(
@@ -992,7 +1007,7 @@
   frame->metadata()->SetBoolean(VideoFrameMetadata::READ_LOCK_FENCES_ENABLED,
                                 true);
 
-  CompleteCopyRequestAndMaybeStartNextCopy(frame);
+  CompleteCopyRequestAndMaybeStartNextCopy(std::move(frame));
 }
 
 // Destroy all the resources posting one task per FrameResources
@@ -1072,10 +1087,11 @@
 
 void GpuMemoryBufferVideoFramePool::PoolImpl::
     CompleteCopyRequestAndMaybeStartNextCopy(
-        const scoped_refptr<VideoFrame>& video_frame) {
+        scoped_refptr<VideoFrame> video_frame) {
   DCHECK(!frame_copy_requests_.empty());
 
-  std::move(frame_copy_requests_.front().frame_ready_cb).Run(video_frame);
+  std::move(frame_copy_requests_.front().frame_ready_cb)
+      .Run(std::move(video_frame));
   frame_copy_requests_.pop_front();
   if (!frame_copy_requests_.empty())
     StartCopy();
diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc
index d93e5b9..3393d9a 100644
--- a/net/base/network_change_notifier_win.cc
+++ b/net/base/network_change_notifier_win.cc
@@ -12,10 +12,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -33,18 +30,32 @@
 
 }  // namespace
 
+// Thread on which we can run DnsConfigService, which requires AssertIOAllowed
+// to open registry keys and to handle FilePathWatcher updates.
+class NetworkChangeNotifierWin::DnsConfigServiceThread : public base::Thread {
+ public:
+  DnsConfigServiceThread() : base::Thread("DnsConfigService") {}
+
+  ~DnsConfigServiceThread() override { Stop(); }
+
+  void Init() override {
+    service_ = DnsConfigService::CreateSystemService();
+    service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig));
+  }
+
+  void CleanUp() override { service_.reset(); }
+
+ private:
+  std::unique_ptr<DnsConfigService> service_;
+
+  DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
+};
+
 NetworkChangeNotifierWin::NetworkChangeNotifierWin()
     : NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()),
       is_watching_(false),
       sequential_failures_(0),
-      dns_config_service_runner_(
-          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
-      dns_config_service_(
-          DnsConfigService::CreateSystemService().release(),
-          // Ensure DnsConfigService lives on |dns_config_service_runner_|
-          // to prevent races where NetworkChangeNotifierWin outlives
-          // ScopedTaskEnvironment. https://crbug.com/938126
-          base::OnTaskRunnerDeleter(dns_config_service_runner_)),
+      dns_config_service_thread_(new DnsConfigServiceThread()),
       last_computed_connection_type_(RecomputeCurrentConnectionType()),
       last_announced_offline_(last_computed_connection_type_ ==
                               CONNECTION_NONE),
@@ -54,7 +65,7 @@
 }
 
 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (is_watching_) {
     CancelIPChangeNotify(&addr_overlapped_);
     addr_watcher_.StopWatching();
@@ -193,15 +204,15 @@
                           : NetworkChangeNotifier::CONNECTION_NONE;
 }
 
-void NetworkChangeNotifierWin::RecomputeCurrentConnectionTypeOnDnsSequence(
-    base::OnceCallback<void(ConnectionType)> reply_callback) const {
+void NetworkChangeNotifierWin::RecomputeCurrentConnectionTypeOnDnsThread(
+    base::Callback<void(ConnectionType)> reply_callback) const {
   // Unretained is safe in this call because this object owns the thread and the
   // thread is stopped in this object's destructor.
   base::PostTaskAndReplyWithResult(
-      dns_config_service_runner_.get(), FROM_HERE,
-      base::BindOnce(&NetworkChangeNotifierWin::RecomputeCurrentConnectionType,
-                     base::Unretained(this)),
-      std::move(reply_callback));
+      dns_config_service_thread_->task_runner().get(), FROM_HERE,
+      base::Bind(&NetworkChangeNotifierWin::RecomputeCurrentConnectionType,
+                 base::Unretained(this)),
+      reply_callback);
 }
 
 NetworkChangeNotifier::ConnectionType
@@ -217,19 +228,19 @@
 }
 
 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(is_watching_);
   is_watching_ = false;
 
   // Start watching for the next address change.
   WatchForAddressChange();
 
-  RecomputeCurrentConnectionTypeOnDnsSequence(base::BindOnce(
+  RecomputeCurrentConnectionTypeOnDnsThread(base::Bind(
       &NetworkChangeNotifierWin::NotifyObservers, weak_factory_.GetWeakPtr()));
 }
 
 void NetworkChangeNotifierWin::NotifyObservers(ConnectionType connection_type) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   SetCurrentConnectionType(connection_type);
   NotifyObserversOfIPAddressChange();
 
@@ -246,7 +257,7 @@
 }
 
 void NetworkChangeNotifierWin::WatchForAddressChange() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!is_watching_);
 
   // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown
@@ -276,7 +287,7 @@
   // network change event, since network changes were not being observed in
   // that interval.
   if (sequential_failures_ > 0) {
-    RecomputeCurrentConnectionTypeOnDnsSequence(
+    RecomputeCurrentConnectionTypeOnDnsThread(
         base::Bind(&NetworkChangeNotifierWin::NotifyObservers,
                    weak_factory_.GetWeakPtr()));
   }
@@ -291,13 +302,12 @@
 }
 
 bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  dns_config_service_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&DnsConfigService::WatchConfig,
-                                base::Unretained(dns_config_service_.get()),
-                                base::BindRepeating(
-                                    &NetworkChangeNotifier::SetDnsConfig)));
+  if (!dns_config_service_thread_->IsRunning()) {
+    dns_config_service_thread_->StartWithOptions(
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+  }
 
   ResetEventIfSignaled(addr_overlapped_.hEvent);
   HANDLE handle = nullptr;
@@ -310,7 +320,7 @@
 }
 
 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() {
-  RecomputeCurrentConnectionTypeOnDnsSequence(base::BindOnce(
+  RecomputeCurrentConnectionTypeOnDnsThread(base::Bind(
       &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl,
       weak_factory_.GetWeakPtr()));
 }
diff --git a/net/base/network_change_notifier_win.h b/net/base/network_change_notifier_win.h
index 533c90ac..beef6ba 100644
--- a/net/base/network_change_notifier_win.h
+++ b/net/base/network_change_notifier_win.h
@@ -12,25 +12,17 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
+#include "base/threading/thread_checker.h"
 #include "base/timer/timer.h"
 #include "base/win/object_watcher.h"
 #include "net/base/net_export.h"
 #include "net/base/network_change_notifier.h"
 
-namespace base {
-class SequencedTaskRunner;
-struct OnTaskRunnerDeleter;
-}  // namespace base
-
 namespace net {
 
-class DnsConfigService;
-
-// NetworkChangeNotifierWin uses a SequenceChecker, as all its internal
-// notification code must be called on the sequence it is created and destroyed
+// NetworkChangeNotifierWin uses a ThreadChecker, as all its internal
+// notification code must be called on the thread it is created and destroyed
 // on.  All the NetworkChangeNotifier methods it implements are threadsafe.
 class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
     : public NetworkChangeNotifier,
@@ -40,8 +32,8 @@
 
   // Begins listening for a single subsequent address change.  If it fails to
   // start watching, it retries on a timer.  Must be called only once, on the
-  // sequence |this| was created on.  This cannot be called in the constructor,
-  // as WatchForAddressChangeInternal is mocked out in unit tests.
+  // thread |this| was created on.  This cannot be called in the constructor, as
+  // WatchForAddressChangeInternal is mocked out in unit tests.
   // TODO(mmenke): Consider making this function a part of the
   //               NetworkChangeNotifier interface, so other subclasses can be
   //               unit tested in similar fashion, as needed.
@@ -56,29 +48,30 @@
   int sequential_failures() { return sequential_failures_; }
 
  private:
+  class DnsConfigServiceThread;
   friend class NetworkChangeNotifierWinTest;
 
   // NetworkChangeNotifier methods:
   ConnectionType GetCurrentConnectionType() const override;
 
   // ObjectWatcher::Delegate methods:
-  // Must only be called on the sequence |this| was created on.
+  // Must only be called on the thread |this| was created on.
   void OnObjectSignaled(HANDLE object) override;
 
   // Does the actual work to determine the current connection type.
   // It is not thread safe, see crbug.com/324913.
   virtual ConnectionType RecomputeCurrentConnectionType() const;
 
-  // Calls RecomputeCurrentConnectionTypeImpl on the DNS sequence and runs
-  // |reply_callback| with the type on the calling sequence.
-  virtual void RecomputeCurrentConnectionTypeOnDnsSequence(
-      base::OnceCallback<void(ConnectionType)> reply_callback) const;
+  // Calls RecomputeCurrentConnectionTypeImpl on the DNS thread and runs
+  // |reply_callback| with the type on the calling thread.
+  virtual void RecomputeCurrentConnectionTypeOnDnsThread(
+      base::Callback<void(ConnectionType)> reply_callback) const;
 
   void SetCurrentConnectionType(ConnectionType connection_type);
 
   // Notifies IP address change observers of a change immediately, and notifies
   // network state change observers on a delay.  Must only be called on the
-  // sequence |this| was created on.
+  // thread |this| was created on.
   void NotifyObservers(ConnectionType connection_type);
 
   // Forwards connection type notifications to parent class.
@@ -87,14 +80,14 @@
 
   // Tries to start listening for a single subsequent address change.  Returns
   // false on failure.  The caller is responsible for updating |is_watching_|.
-  // Virtual for unit tests.  Must only be called on the sequence |this| was
+  // Virtual for unit tests.  Must only be called on the thread |this| was
   // created on.
   virtual bool WatchForAddressChangeInternal();
 
   static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsWin();
 
-  // All member variables may only be accessed on the sequence |this| was
-  // created on.
+  // All member variables may only be accessed on the thread |this| was created
+  // on.
 
   // False when not currently watching for network change events.  This only
   // happens on initialization and when WatchForAddressChangeInternal fails and
@@ -109,11 +102,8 @@
   // Number of times WatchForAddressChange has failed in a row.
   int sequential_failures_;
 
-  // |dns_config_service_| will live on this runner.
-  scoped_refptr<base::SequencedTaskRunner> dns_config_service_runner_;
-  // DnsConfigService that lives on |dns_config_service_runner_|.
-  std::unique_ptr<DnsConfigService, base::OnTaskRunnerDeleter>
-      dns_config_service_;
+  // Thread on which we can run DnsConfigService.
+  std::unique_ptr<DnsConfigServiceThread> dns_config_service_thread_;
 
   mutable base::Lock last_computed_connection_type_lock_;
   ConnectionType last_computed_connection_type_;
@@ -124,7 +114,7 @@
   // Number of times polled to check if still offline.
   int offline_polls_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  THREAD_CHECKER(thread_checker_);
 
   // Used for calling WatchForAddressChange again on failure.
   base::WeakPtrFactory<NetworkChangeNotifierWin> weak_factory_;
diff --git a/net/base/network_change_notifier_win_unittest.cc b/net/base/network_change_notifier_win_unittest.cc
index 0d51205..ba695806 100644
--- a/net/base/network_change_notifier_win_unittest.cc
+++ b/net/base/network_change_notifier_win_unittest.cc
@@ -43,10 +43,10 @@
   }
 
   // From NetworkChangeNotifierWin.
-  void RecomputeCurrentConnectionTypeOnDnsSequence(
-      base::OnceCallback<void(ConnectionType)> reply_callback) const override {
+  void RecomputeCurrentConnectionTypeOnDnsThread(
+      base::Callback<void(ConnectionType)> reply_callback) const override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(reply_callback),
+        FROM_HERE, base::BindOnce(reply_callback,
                                   NetworkChangeNotifier::CONNECTION_UNKNOWN));
   }
 
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index 01108c5..ebb5044 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -18,13 +18,13 @@
 #include "base/macros.h"
 #include "base/memory/free_deleter.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/win/registry.h"
@@ -76,11 +76,11 @@
     key_.Open(HKEY_LOCAL_MACHINE, key, KEY_QUERY_VALUE);
   }
 
-  ~RegistryReader() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
+  ~RegistryReader() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
 
   bool ReadString(const base::char16* name,
                   DnsSystemSettings::RegString* out) const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     out->set = false;
     if (!key_.Valid()) {
       // Assume that if the |key_| is invalid then the key is missing.
@@ -96,7 +96,7 @@
 
   bool ReadDword(const base::char16* name,
                  DnsSystemSettings::RegDword* out) const {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     out->set = false;
     if (!key_.Valid()) {
       // Assume that if the |key_| is invalid then the key is missing.
@@ -113,7 +113,7 @@
  private:
   base::win::RegKey key_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  THREAD_CHECKER(thread_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(RegistryReader);
 };
@@ -275,10 +275,10 @@
   typedef base::Callback<void(bool succeeded)> CallbackType;
   RegistryWatcher() {}
 
-  ~RegistryWatcher() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
+  ~RegistryWatcher() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
 
   bool Watch(const base::char16* key, const CallbackType& callback) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     DCHECK(!callback.is_null());
     DCHECK(callback_.is_null());
     callback_ = callback;
@@ -290,7 +290,7 @@
   }
 
   void OnObjectSignaled() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     DCHECK(!callback_.is_null());
     if (key_.StartWatching(base::Bind(&RegistryWatcher::OnObjectSignaled,
                                       base::Unretained(this)))) {
@@ -305,7 +305,7 @@
   CallbackType callback_;
   base::win::RegKey key_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  THREAD_CHECKER(thread_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
 };
@@ -564,7 +564,7 @@
   return result;
 }
 
-// Watches registry and HOSTS file for changes. Must live on a sequence which
+// Watches registry and HOSTS file for changes. Must live on a thread which
 // allows IO.
 class DnsConfigServiceWin::Watcher
     : public NetworkChangeNotifier::IPAddressObserver {
@@ -729,16 +729,13 @@
   DISALLOW_COPY_AND_ASSIGN(HostsReader);
 };
 
-DnsConfigServiceWin::DnsConfigServiceWin() {
-  // Allow constructing on one sequence and living on another.
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-}
+DnsConfigServiceWin::DnsConfigServiceWin()
+    : config_reader_(new ConfigReader(this)),
+      hosts_reader_(new HostsReader(this)) {}
 
 DnsConfigServiceWin::~DnsConfigServiceWin() {
-  if (config_reader_)
-    config_reader_->Cancel();
-  if (hosts_reader_)
-    hosts_reader_->Cancel();
+  config_reader_->Cancel();
+  hosts_reader_->Cancel();
 }
 
 void DnsConfigServiceWin::ReadNow() {
@@ -747,10 +744,6 @@
 }
 
 bool DnsConfigServiceWin::StartWatching() {
-  if (!config_reader_)
-    config_reader_ = base::MakeRefCounted<ConfigReader>(this);
-  if (!hosts_reader_)
-    hosts_reader_ = base::MakeRefCounted<HostsReader>(this);
   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
   watcher_.reset(new Watcher(this));
   UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index 098d476..a1224068 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -124,11 +124,6 @@
     const DnsSystemSettings& settings,
     DnsConfig* dns_config);
 
-// Service for reading and watching Windows system DNS settings. This object is
-// not thread-safe and methods may perform blocking I/O so methods must be
-// called on a sequence that allows blocking (i.e. base::MayBlock). It may be
-// constructed on a different sequence than which it's later called on.
-// WatchConfig() must be called prior to ReadConfig().
 // Use DnsConfigService::CreateSystemService to use it outside of tests.
 class NET_EXPORT_PRIVATE DnsConfigServiceWin : public DnsConfigService {
  public:
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 106c8d9b..f7bd4d9 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -101,7 +101,7 @@
 
 # Internal-facing config for Skia library code.
 config("skia_library_config") {
-  defines = [ "SK_IGNORE_LINEAR_METRICS_FIX" ]
+  defines = []
 
   if (!is_ios && !use_system_freetype) {
     defines += [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) * 0x01000000) | ((FREETYPE_MINOR) * 0x00010000) | ((FREETYPE_PATCH) * 0x00000100))" ]
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 906c552..4e7c83d 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -52,8 +52,10 @@
       {
         "args": [
           "-v",
-          "--browser=android-chrome",
+          "--browser=exact",
           "--upload-results",
+          "--browser-executable=../../out/Release/bin/monochrome_64_32_bundle",
+          "--device=android",
           "--run-ref-build",
           "--test-shard-map-filename=android-pixel2-perf_map.json"
         ],
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9717cc7..ae9abf43 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1076,21 +1076,6 @@
             ]
         }
     ],
-    "BackgroundTaskSchedulerForBackgroundSync": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BackgroundTaskSchedulerForBackgroundSync"
-                    ]
-                }
-            ]
-        }
-    ],
     "BlinkGenPropertyTrees": [
         {
             "platforms": [
@@ -6015,6 +6000,23 @@
             ]
         }
     ],
+    "WebRTC-Pacer-BlockAudio": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "linux",
+                "ios",
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Disabled"
+                }
+            ]
+        }
+    ],
     "WebRTC-ProbingScreenshareBwe": [
         {
             "platforms": [
diff --git a/third_party/android_crazy_linker/BUILD.gn b/third_party/android_crazy_linker/BUILD.gn
index a7c21e02..5747a16 100644
--- a/third_party/android_crazy_linker/BUILD.gn
+++ b/third_party/android_crazy_linker/BUILD.gn
@@ -85,6 +85,7 @@
         "src/src/crazy_linker_library_view.h",
         "src/src/crazy_linker_line_reader.cpp",
         "src/src/crazy_linker_line_reader.h",
+        "src/src/crazy_linker_load_params.h",
         "src/src/crazy_linker_memory_mapping.cpp",
         "src/src/crazy_linker_memory_mapping.h",
         "src/src/crazy_linker_pointer_set.cpp",
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp
index eeda97c..e7b7413 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_api.cpp
@@ -109,9 +109,22 @@
                                   const char* lib_name,
                                   crazy_context_t* context) {
   ScopedLockedGlobals globals;
-  LibraryView* view = globals->libraries()->LoadLibrary(
-      lib_name, context->load_address, globals->search_path_list(),
-      &context->error);
+  crazy::LibraryList* libs = globals->libraries();
+  crazy::LoadParams params;
+  params.wanted_address = context->load_address;
+  crazy::Expected<LibraryView*> found =
+      libs->FindAndCheckLoadedLibrary(lib_name, params, &context->error);
+  if (!found.has_value())
+    return CRAZY_STATUS_FAILURE;
+
+  LibraryView* view = found.value();
+  if (!view) {
+    if (!libs->LocateLibraryFile(lib_name, *globals->search_path_list(),
+                                 &params, &context->error)) {
+      return CRAZY_STATUS_FAILURE;
+    }
+    view = libs->LoadLibraryInternal(params, &context->error);
+  }
 
   if (!view)
     return CRAZY_STATUS_FAILURE;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp
index d219b3c..80e3c92 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.cpp
@@ -27,10 +27,7 @@
  public:
   ~InternalElfLoader();
 
-  bool LoadAt(const char* lib_path,
-              off_t file_offset,
-              uintptr_t wanted_address,
-              Error* error);
+  bool LoadAt(const LoadParams& params, Error* error);
 
   // Only call the following functions after a successful LoadAt() call.
 
@@ -56,7 +53,6 @@
   ELF::Addr phdr_size_ = 0;  // and its size.
 
   off_t file_offset_ = 0;
-  void* wanted_load_address_ = nullptr;
   void* load_start_ = nullptr;  // First page of reserved address space.
   ELF::Addr load_size_ = 0;     // Size in bytes of reserved address space.
   ELF::Addr load_bias_ = 0;     // load_bias, add this value to all "vaddr"
@@ -71,7 +67,7 @@
   // Individual steps used by ::LoadAt()
   bool ReadElfHeader(Error* error);
   bool ReadProgramHeader(Error* error);
-  bool ReserveAddressSpace(Error* error);
+  bool ReserveAddressSpace(const LoadParams& params, Error* error);
   bool LoadSegments(Error* error);
   bool FindPhdr(Error* error);
   bool CheckPhdr(ELF::Addr, Error* error);
@@ -84,25 +80,25 @@
   }
 }
 
-bool InternalElfLoader::LoadAt(const char* lib_path,
-                               off_t file_offset,
-                               uintptr_t wanted_address,
-                               Error* error) {
-  LOG("lib_path='%s', file_offset=%p, load_address=%p", lib_path, file_offset,
-      wanted_address);
+bool InternalElfLoader::LoadAt(const LoadParams& params, Error* error) {
+  const char* lib_path = params.library_path.c_str();
+  LOG("lib_path='%s', file_offset=%p, load_address=%lx", lib_path,
+      params.library_offset, static_cast<unsigned long>(params.wanted_address));
 
   // Check that the load address is properly page-aligned.
+  uintptr_t wanted_address = params.wanted_address;
   if (wanted_address != PAGE_START(wanted_address)) {
     error->Format("Load address is not page aligned (%08x)", wanted_address);
     return false;
   }
-  wanted_load_address_ = reinterpret_cast<void*>(wanted_address);
 
   // Check that the file offset is also properly page-aligned.
   // PAGE_START() can't be used here due to the compiler complaining about
   // comparing signed (off_t) and unsigned (size_t) values.
+  off_t file_offset = params.library_offset;
   if ((file_offset & static_cast<off_t>(PAGE_SIZE - 1)) != 0) {
-    error->Format("File offset is not page aligned (%08x)", file_offset);
+    error->Format("File offset is not page aligned (%08lx)",
+                  static_cast<unsigned long>(file_offset));
     return false;
   }
   file_offset_ = file_offset;
@@ -122,7 +118,7 @@
   path_ = lib_path;
 
   if (!ReadElfHeader(error) || !ReadProgramHeader(error) ||
-      !ReserveAddressSpace(error)) {
+      !ReserveAddressSpace(params, error)) {
     return false;
   }
 
@@ -220,7 +216,8 @@
 // This will use the wanted_load_address_ value. Fails if the requested
 // address range cannot be reserved. Typically this would be because
 // it overlaps an existing, possibly system, mapping.
-bool InternalElfLoader::ReserveAddressSpace(Error* error) {
+bool InternalElfLoader::ReserveAddressSpace(const LoadParams& params,
+                                            Error* error) {
   ELF::Addr min_vaddr;
   load_size_ =
       phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr, NULL);
@@ -229,20 +226,25 @@
     return false;
   }
 
-  uint8_t* addr = NULL;
+  void* addr = nullptr;
   int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
 
   // Support loading at a fixed address.
-  if (wanted_load_address_) {
-    addr = static_cast<uint8_t*>(wanted_load_address_);
+  if (params.wanted_address) {
+    addr = reinterpret_cast<void*>(params.wanted_address);
+    mmap_flags |= MAP_FIXED;
   }
 
   size_t reserved_size = load_size_;
 
-  LOG("address=%p size=%p", addr, reserved_size);
+  LOG("Trying to reserve memory address=%p size=%lu (0x%lx)", addr,
+      static_cast<unsigned long>(load_size_),
+      static_cast<unsigned long>(load_size_));
+
   void* start = mmap(addr, reserved_size, PROT_NONE, mmap_flags, -1, 0);
   if (start == MAP_FAILED) {
-    error->Format("Could not reserve %d bytes of address space", reserved_size);
+    error->Format("Could not reserve %lu bytes of address space",
+                  static_cast<unsigned long>(reserved_size));
     return false;
   }
   if (addr && start != addr) {
@@ -253,12 +255,16 @@
 
   // Take ownership of the mapping here.
   reserved_map_ = MemoryMapping(start, reserved_size);
-  LOG("reserved start=%p", reserved_map_.address());
 
   load_start_ = start;
-  load_bias_ = reinterpret_cast<ELF::Addr>(start) - min_vaddr;
+  load_bias_ = reinterpret_cast<ELF::Addr>(load_start_) - min_vaddr;
 
-  LOG("load start=%p, bias=%p", load_start_, load_bias_);
+  LOG("Reserved memory address=%p, size=%lu (0x%lx), bias=%lu (0x%lx)",
+      load_start_, static_cast<unsigned long>(load_size_),
+      static_cast<unsigned long>(load_size_),
+      static_cast<unsigned long>(load_bias_),
+      static_cast<unsigned long>(load_bias_));
+
   return true;
 }
 
@@ -390,13 +396,10 @@
 }  // namespace
 
 // static
-ElfLoader::Result ElfLoader::LoadAt(const char* lib_path,
-                                    off_t file_offset,
-                                    uintptr_t wanted_address,
-                                    Error* error) {
+ElfLoader::Result ElfLoader::LoadAt(const LoadParams& params, Error* error) {
   InternalElfLoader loader;
   Result result;
-  if (loader.LoadAt(lib_path, file_offset, wanted_address, error)) {
+  if (loader.LoadAt(params, error)) {
     result.load_start = reinterpret_cast<ELF::Addr>(loader.load_start());
     result.load_size = loader.load_size();
     result.load_bias = loader.load_bias();
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h
index bf4016c..03924e0b 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_elf_loader.h
@@ -6,6 +6,7 @@
 #define CRAZY_LINKER_ELF_LOADER_H
 
 #include "crazy_linker_error.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_memory_mapping.h"
 #include "crazy_linker_system.h"  // For ScopedFileDescriptor
 #include "elf_traits.h"
@@ -29,32 +30,13 @@
     const ELF::Phdr* phdr = nullptr;
     size_t phdr_count = 0;
     MemoryMapping reserved_mapping;
-    Error error;  // empty in case of success.
 
     constexpr bool IsValid() const { return this->load_start != 0; }
   };
 
-  // Try to load a library at a given address. On failure, this will
-  // update the linker error message and returns false.
-  //
-  // |lib_path| is the full library path, and |wanted_address| should
-  // be the desired load address, or 0 to enable randomization.
-  //
-  // |file_offset| is an offset in the file where the ELF header will
-  // be looked for.
-  //
-  // |wanted_address| is the wanted load address (of the first loadable
-  // segment), or 0 to enable randomization.
-  //
-  // On success, returns a valid Result instance, where |reserved_mapping| will
-  // map the single range of reserved memory addresses for the ELF object
-  // (including the breakpad guard regions).
-  //
-  // On failure, return an invalid Result instance, and sets |*error|.
-  static Result LoadAt(const char* lib_path,
-                       off_t file_offset,
-                       uintptr_t wanted_address,
-                       Error* error);
+  // Try to load a library at a given address. On failure, return an
+  // invalid Result instance, and sets |*error|.
+  static Result LoadAt(const LoadParams& params, Error* error);
 };
 
 }  // namespace crazy
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp
index 99d0a209..c7565a9 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp
@@ -53,6 +53,29 @@
   }
 };
 
+// Checks that |params| is compatible with a system library load.
+// On success return true. On failure, set |*error| and return false.
+// |lib_name| is either the library name, or nullptr, in which case
+// |params.library_path| will be used for the error message.
+bool CheckSystemLibraryLoadParams(const char* lib_name,
+                                  const LoadParams& params,
+                                  Error* error) {
+  if (!lib_name)
+    lib_name = params.library_path.c_str();
+
+  if (params.library_offset != 0) {
+    error->Format("Cannot load system library from offset 0x%08lx: %s",
+                  static_cast<unsigned long>(params.library_offset), lib_name);
+    return false;
+  }
+  if (params.wanted_address != 0) {
+    error->Format("Cannot load system library at address 0x%08lx: %s",
+                  static_cast<unsigned long>(params.wanted_address), lib_name);
+    return false;
+  }
+  return true;
+}
+
 }  // namespace
 
 LibraryList::LibraryList() {
@@ -295,11 +318,8 @@
                                                       Error* error) {
   // First check whether a library with the same base name was
   // already loaded.
-  LibraryView* view = FindKnownLibrary(lib_name);
-  if (view) {
-    view->AddRef();
-    return view;
-  }
+  ASSERT(!FindKnownLibrary(lib_name),
+         "System library already loaded: ", lib_name);
 
   void* system_lib = SystemLinker::Open(lib_name, dlopen_mode);
   if (!system_lib) {
@@ -309,86 +329,120 @@
   }
 
   // Can't really find the DT_SONAME of this library, assume if is its basename.
-  view = new LibraryView(system_lib, GetBaseNamePtr(lib_name));
+  LibraryView* view = new LibraryView(system_lib, GetBaseNamePtr(lib_name));
   known_libraries_.PushBack(view);
 
   LOG("System library %s loaded at %p", lib_name, view);
   return view;
 }
 
-LibraryView* LibraryList::LoadLibrary(const char* lib_name,
-                                      uintptr_t load_address,
-                                      SearchPathList* search_path_list,
-                                      Error* error) {
-  const char* base_name = GetBaseNamePtr(lib_name);
+Expected<LibraryView*> LibraryList::FindAndCheckLoadedLibrary(
+    const char* lib_name,
+    const LoadParams& params,
+    Error* error) {
+  // First check whether a library with the same base name was already loaded.
+  LibraryView* view = FindKnownLibrary(lib_name);
+  if (!view)
+    return nullptr;
 
-  LOG("lib_name='%s'", lib_name);
-
-  // First check whether a library with the same base name was
-  // already loaded.
-  LibraryView* view = FindKnownLibrary(base_name);
-  if (view) {
-    if (load_address) {
+  if (view->IsSystem()) {
+    if (!CheckSystemLibraryLoadParams(lib_name, params, error))
+      return error;
+  } else {
+    const SharedLibrary* crazy_lib = view->GetCrazy();
+    ASSERT(crazy_lib != nullptr, "Not a crazy library");
+    if (params.wanted_address) {
       // Check that this is a crazy library and that is was loaded at
       // the correct address.
-      if (!view->IsCrazy()) {
-        error->Format("System library can't be loaded at fixed address %08x",
-                      load_address);
-        return nullptr;
-      }
-      uintptr_t actual_address = view->GetCrazy()->load_address();
-      if (actual_address != load_address) {
-        error->Format("Library already loaded at @%08x, can't load it at @%08x",
-                      actual_address,
-                      load_address);
-        return nullptr;
+      uintptr_t actual_address = crazy_lib->load_address();
+      if (actual_address != params.wanted_address) {
+        error->Format(
+            "Library already loaded at address 0x%08lx, can't load it at "
+            "0x%08lx: %s",
+            static_cast<unsigned long>(actual_address),
+            static_cast<unsigned long>(params.wanted_address), lib_name);
+        return error;
       }
     }
-    view->AddRef();
-    return view;
   }
 
-  // Find the full library path.
-  String full_path;
+  view->AddRef();
+  return view;
+}
 
+// static
+bool LibraryList::LocateLibraryFile(const char* lib_name,
+                                    const SearchPathList& search_path_list,
+                                    LoadParams* params,
+                                    Error* error) {
   LOG("Looking through the search path list");
-  SearchPathList::Result probe = search_path_list->FindFile(lib_name);
+  SearchPathList::Result probe = search_path_list.FindFile(lib_name);
   if (!probe.IsValid()) {
     error->Format("Can't find library file %s", lib_name);
-    return nullptr;
+    return false;
   }
   LOG("Found library: path %s @ 0x%x", probe.path.c_str(), probe.offset);
+  params->library_path = std::move(probe.path);
+  params->library_offset = probe.offset;
+  return true;
+}
 
-  if (IsSystemLibraryPath(probe.path.c_str())) {
-    return LoadLibraryWithSystemLinker(probe.path.c_str(), RTLD_NOW, error);
+LibraryView* LibraryList::LoadLibrary(const char* lib_name,
+                                      const LoadParams& params,
+                                      Error* error) {
+  Expected<LibraryView*> found =
+      FindAndCheckLoadedLibrary(lib_name, params, error);
+  if (!found.has_value())
+    return nullptr;
+  if (found.value())
+    return found.value();
+  return LoadLibraryInternal(params, error);
+}
+
+LibraryView* LibraryList::LoadLibraryInternal(const LoadParams& params,
+                                              Error* error) {
+  // Load the library with the system linker if necessary.
+  const char* lib_path = params.library_path.c_str();
+  if (IsSystemLibraryPath(lib_path)) {
+    if (!CheckSystemLibraryLoadParams(lib_path, params, error))
+      return nullptr;
+    return LoadLibraryWithSystemLinker(lib_path, RTLD_NOW, error);
   }
 
   // Load the library with the crazy linker.
   ScopedPtr<SharedLibrary> lib(new SharedLibrary());
-  if (!lib->Load(probe.path.c_str(), load_address, probe.offset, error))
+  if (!lib->Load(params, error))
     return nullptr;
 
   // Load all dependendent libraries.
+  const char* base_name = GetBaseNamePtr(lib_path);
   LOG("Loading dependencies of %s", base_name);
   SharedLibrary::DependencyIterator iter(lib.Get());
   Vector<LibraryView*> dependencies;
   while (iter.GetNext()) {
     Error dep_error;
-    // TODO(digit): Call LoadLibrary recursively instead when properly
-    // detecting system vs Chromium libraries (http://crbug.com/843987).
-    LibraryView* dependency =
-        LoadLibraryWithSystemLinker(iter.GetName(), RTLD_NOW, &dep_error);
+    // TODO(digit): Better library dependency loading that isn't limited
+    // to system libraries. This would allow the linker to load anything
+    // without the caller having to load all dependencies before hand in
+    // reverse topological order.
+    const char* dependency_name = iter.GetName();
+    LibraryView* dependency = FindKnownLibrary(dependency_name);
     if (!dependency) {
-      error->Format("When loading %s: %s", base_name, dep_error.c_str());
-      return nullptr;
+      dependency =
+          LoadLibraryWithSystemLinker(dependency_name, RTLD_NOW, &dep_error);
+      if (!dependency) {
+        error->Format("When loading %s: %s", base_name, dep_error.c_str());
+        // TODO(digit): Unload all dependencies that were loaded so far.
+        return nullptr;
+      }
     }
     dependencies.PushBack(dependency);
   }
   if (CRAZY_DEBUG) {
     LOG("Dependencies loaded for %s", base_name);
     for (const LibraryView* dep : dependencies)
-      LOG("  ... %p %s\n", dep, dep->GetName());
-    LOG("    dependencies @%p\n", &dependencies);
+      LOG("  ... %p %s", dep, dep->GetName());
+    LOG("    dependencies @%p", &dependencies);
   }
 
   // Relocate the library.
@@ -412,7 +466,7 @@
   head_ = lib.Get();
 
   // Then create a new LibraryView for it.
-  view = new LibraryView(lib.Release());
+  LibraryView* view = new LibraryView(lib.Release());
   known_libraries_.PushBack(view);
 
   LOG("Running constructors for %s", base_name);
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h
index 27e6f31..20a6ab6d7f 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.h
@@ -8,6 +8,8 @@
 #include <link.h>
 
 #include "crazy_linker_error.h"
+#include "crazy_linker_expected.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_search_path_list.h"
 #include "elf_traits.h"
 
@@ -62,13 +64,42 @@
   int IteratePhdr(PhdrIterationCallback callback, void* data);
 #endif
 
-  // Try to load a library, possibly at a fixed address.
-  // On failure, returns NULL and sets the |error| message.
-  LibraryView* LoadLibrary(const char* path,
-                           uintptr_t load_address,
-                           SearchPathList* search_path_list,
+  // Find whether a library identified by |name| has already been loaded.
+  // Note that |name| should correspond to the library's unique soname, which
+  // comes from its DT_SONAME entry, and typically, but not necessarily
+  // matches its base name.
+  LibraryView* FindKnownLibrary(const char* name);
+
+  // Check whether |lib_path| matches an already loaded library, compatible
+  // with the content of |load_params| (except its |library_path| field).
+  // On failure, i.e. if the load parameters are incompatible, set |*error|
+  // and return its address. On success, return either nullptr (if the library
+  // was not previously loaded, or a LibraryView* pointer after incrementing
+  // its reference count).
+  Expected<LibraryView*> FindAndCheckLoadedLibrary(
+      const char* lib_path,
+      const LoadParams& load_params,
+      Error* error);
+
+  // Locate library |lib_name| using |search_path_list|. On success, update
+  // |params->library_path| and |params->library_offset| and return true. On
+  // failure, set |*error| and return false.
+  static bool LocateLibraryFile(const char* lib_name,
+                                const SearchPathList& search_path_list,
+                                LoadParams* params,
+                                Error* error);
+
+  // Try to load a library, according to |load_params|. On failure, returns
+  // nullptr and sets the |error| message.
+  LibraryView* LoadLibrary(const char* lib_name,
+                           const LoadParams& load_params,
                            Error* error);
 
+  // Try to load a library, according to |load_params|.
+  // On failure, return nullptr and sets the |error| message.
+  // Note: this will fail if the library is already loaded.
+  LibraryView* LoadLibraryInternal(const LoadParams& load_params, Error* error);
+
   // Unload a given shared library. This really decrements the library's
   // internal reference count. When it reaches zero, the library's
   // destructors are run, its dependencies are unloaded, then the
@@ -103,12 +134,6 @@
   // The list of all known libraries.
   Vector<LibraryView*> known_libraries_;
 
-  // Find whether a library identified by |name| has already been loaded.
-  // Note that |name| should correspond to the library's unique soname, which
-  // comes from its DT_SONAME entry, and typically, but not necessarily
-  // matches its base name.
-  LibraryView* FindKnownLibrary(const char* name);
-
   // The list of all libraries loaded by the crazy linker.
   // This does _not_ include system libraries present in known_libraries_.
   SharedLibrary* head_ = nullptr;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_load_params.h b/third_party/android_crazy_linker/src/src/crazy_linker_load_params.h
new file mode 100644
index 0000000..66cd03c
--- /dev/null
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_load_params.h
@@ -0,0 +1,32 @@
+// 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 CRAZY_LINKER_LOAD_PARAMS_H
+#define CRAZY_LINKER_LOAD_PARAMS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "crazy_linker_util.h"
+
+namespace crazy {
+
+// A structure used to hold parameters related to loading an ELF library
+// into the current process' address space.
+//
+// |library_path| is either the full library path.
+// |library_offset| is the page-aligned offset where the library starts in
+// its input file (typically > 0 when reading from Android APKs).
+// |wanted_address| is either 0, or the address where the library should
+// be loaded.
+struct LoadParams {
+  String library_path;
+  off_t library_offset = 0;
+  uintptr_t wanted_address = 0;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_LOAD_PARAMS_H
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
index de136c4..1f5a711e 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
@@ -15,6 +15,7 @@
 #include "crazy_linker_globals.h"
 #include "crazy_linker_library_list.h"
 #include "crazy_linker_library_view.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_memory_mapping.h"
 #include "crazy_linker_system_linker.h"
 #include "crazy_linker_thread_data.h"
@@ -225,11 +226,9 @@
 
 SharedLibrary::~SharedLibrary() = default;
 
-bool SharedLibrary::Load(const char* full_path,
-                         size_t load_address,
-                         size_t file_offset,
-                         Error* error) {
+bool SharedLibrary::Load(const LoadParams& params, Error* error) {
   // First, record the path.
+  const char* full_path = params.library_path.c_str();
   LOG("full path '%s'", full_path);
 
   size_t full_path_len = strlen(full_path);
@@ -252,8 +251,7 @@
   LOG("Loading ELF segments for %s", base_name_);
 
   {
-    ElfLoader::Result ret =
-        ElfLoader::LoadAt(full_path_, file_offset, load_address, error);
+    ElfLoader::Result ret = ElfLoader::LoadAt(params, error);
     if (!ret.IsValid() ||
         !view_.InitUnmapped(ret.load_start, ret.phdr, ret.phdr_count, error)) {
       return false;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
index b0c7b89..5a32665 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.h
@@ -13,6 +13,7 @@
 #include "crazy_linker_elf_symbols.h"
 #include "crazy_linker_elf_view.h"
 #include "crazy_linker_error.h"
+#include "crazy_linker_load_params.h"
 #include "crazy_linker_memory_mapping.h"
 #include "crazy_linker_rdebug.h"
 #include "crazy_linker_util.h"
@@ -50,17 +51,13 @@
   // Load a library (without its dependents) from an ELF file.
   // Note: This does not apply relocations, nor runs constructors.
   // |full_path| if the file full path.
-  // |load_address| is the page-aligned load address in memory, or 0.
-  // |file_offset| is the page-aligned file offset.
+  // |params| are the load parameters for this operation.
   // On failure, return false and set |error| message.
   //
   // After this, the caller should load all library dependencies,
   // Then call Relocate() and CallConstructors() to complete the
   // operation.
-  bool Load(const char* full_path,
-            size_t load_address,
-            size_t file_offset,
-            Error* error);
+  bool Load(const LoadParams& params, Error* error);
 
   // Relocate this library, assuming all its dependencies are already
   // loaded in |lib_list|. On failure, return false and set |error|
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp
index f25ffaea..44e54af 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_wrappers.cpp
@@ -89,17 +89,26 @@
 
 void* WrapDlopen(const char* path, int mode) {
   ScopedLockedGlobals globals;
+  LibraryList* libs = globals->libraries();
 
   // NOTE: If |path| is NULL, the wrapper should return a handle
   // corresponding to the current executable. This can't be a crazy
   // library, so don't try to handle it with the crazy linker.
   if (path) {
     Error error;
-    LibraryView* wrap = globals->libraries()->LoadLibrary(
-        path, 0U /* load_address */, globals->search_path_list(), &error);
-    if (wrap) {
-      globals->valid_handles()->Add(wrap);
-      return wrap;
+    LibraryView* view = libs->FindKnownLibrary(path);
+    if (!view) {
+      LoadParams params;
+      if (libs->LocateLibraryFile(path, *globals->search_path_list(), &params,
+                                  &error)) {
+        view = libs->LoadLibraryInternal(params, &error);
+        if (!view) {
+          SetLinkerError("%s: %s", "dlopen", error.c_str());
+          return nullptr;
+        }
+        globals->valid_handles()->Add(view);
+        return view;
+      }
     }
   }
 
@@ -110,10 +119,10 @@
     return nullptr;
   }
 
-  auto* wrap_lib = new LibraryView(system_lib, path ? path : "<executable>");
-  globals->libraries()->AddLibrary(wrap_lib);
-  globals->valid_handles()->Add(wrap_lib);
-  return wrap_lib;
+  auto* view = new LibraryView(system_lib, path ? path : "<executable>");
+  libs->AddLibrary(view);
+  globals->valid_handles()->Add(view);
+  return view;
 }
 
 void* WrapDlsym(void* lib_handle, const char* symbol_name) {
diff --git a/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc b/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc
index 070e66c..35d3782 100644
--- a/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc
+++ b/third_party/blink/common/push_messaging/push_messaging_mojom_traits.cc
@@ -49,11 +49,11 @@
 
 // static
 bool StructTraits<blink::mojom::PushSubscriptionOptionsDataView,
-                  blink::PushSubscriptionOptionsParams>::
+                  blink::WebPushSubscriptionOptions>::
     Read(blink::mojom::PushSubscriptionOptionsDataView data,
-         blink::PushSubscriptionOptionsParams* out) {
+         blink::WebPushSubscriptionOptions* out) {
   out->user_visible_only = data.user_visible_only();
-  if (!data.ReadSenderInfo(&out->sender_info)) {
+  if (!data.ReadApplicationServerKey(&out->application_server_key)) {
     return false;
   }
   return true;
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index abde81c..06ddb2cb 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -166,7 +166,6 @@
     "platform/modules/push_messaging/web_push_error.h",
     "platform/modules/push_messaging/web_push_provider.h",
     "platform/modules/push_messaging/web_push_subscription.h",
-    "platform/modules/push_messaging/web_push_subscription_options.h",
     "platform/modules/remoteplayback/web_remote_playback_client.h",
     "platform/modules/service_worker/web_service_worker_clients_info.h",
     "platform/modules/service_worker/web_service_worker_error.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index ac39869..d43833a 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -95,7 +95,7 @@
     "page/launching_process_state.h",
     "prerender/prerender_rel_type.h",
     "privacy_preferences.h",
-    "push_messaging/push_subscription_options_params.h",
+    "push_messaging/web_push_subscription_options.h",
     "scheduler/web_scheduler_tracked_feature.h",
     "screen_orientation/web_screen_orientation_lock_type.h",
     "screen_orientation/web_screen_orientation_type.h",
diff --git a/third_party/blink/public/common/push_messaging/push_messaging.typemap b/third_party/blink/public/common/push_messaging/push_messaging.typemap
index 9f8410c..4f7a0e51 100644
--- a/third_party/blink/public/common/push_messaging/push_messaging.typemap
+++ b/third_party/blink/public/common/push_messaging/push_messaging.typemap
@@ -4,7 +4,7 @@
 
 mojom = "//third_party/blink/public/mojom/push_messaging/push_messaging.mojom"
 public_headers = [
-  "//third_party/blink/public/common/push_messaging/push_subscription_options_params.h",
+  "//third_party/blink/public/common/push_messaging/web_push_subscription_options.h",
   "//third_party/blink/public/platform/modules/push_messaging/web_push_error.h",
 ]
 traits_headers = [ "//third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h" ]
@@ -13,5 +13,5 @@
 ]
 type_mappings = [
   "blink.mojom.PushErrorType=blink::WebPushError::ErrorType",
-  "blink.mojom.PushSubscriptionOptions=blink::PushSubscriptionOptionsParams",
+  "blink.mojom.PushSubscriptionOptions=blink::WebPushSubscriptionOptions",
 ]
diff --git a/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h b/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h
index 2b316d47..22ebe1e 100644
--- a/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h
+++ b/third_party/blink/public/common/push_messaging/push_messaging_mojom_traits.h
@@ -17,16 +17,16 @@
 template <>
 struct BLINK_COMMON_EXPORT
     StructTraits<blink::mojom::PushSubscriptionOptionsDataView,
-                 blink::PushSubscriptionOptionsParams> {
-  static bool user_visible_only(const blink::PushSubscriptionOptionsParams& r) {
+                 blink::WebPushSubscriptionOptions> {
+  static bool user_visible_only(const blink::WebPushSubscriptionOptions& r) {
     return r.user_visible_only;
   }
-  static const std::string& sender_info(
-      const blink::PushSubscriptionOptionsParams& r) {
-    return r.sender_info;
+  static const std::string& application_server_key(
+      const blink::WebPushSubscriptionOptions& r) {
+    return r.application_server_key;
   }
   static bool Read(blink::mojom::PushSubscriptionOptionsDataView data,
-                   blink::PushSubscriptionOptionsParams* out);
+                   blink::WebPushSubscriptionOptions* out);
 };
 
 template <>
diff --git a/third_party/blink/public/common/push_messaging/push_subscription_options_params.h b/third_party/blink/public/common/push_messaging/web_push_subscription_options.h
similarity index 64%
rename from third_party/blink/public/common/push_messaging/push_subscription_options_params.h
rename to third_party/blink/public/common/push_messaging/web_push_subscription_options.h
index a189bd0e..ec72f7d 100644
--- a/third_party/blink/public/common/push_messaging/push_subscription_options_params.h
+++ b/third_party/blink/public/common/push_messaging/web_push_subscription_options.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_PUBLIC_COMMON_PUSH_MESSAGING_PUSH_SUBSCRIPTION_OPTIONS_PARAMS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PUSH_MESSAGING_PUSH_SUBSCRIPTION_OPTIONS_PARAMS_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_OPTIONS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_OPTIONS_H_
 
 #include <string>
 
@@ -13,9 +13,9 @@
 
 // Structure to hold the options provided from the web app developer as
 // part of asking for a new push subscription.
-struct BLINK_COMMON_EXPORT PushSubscriptionOptionsParams {
-  PushSubscriptionOptionsParams() {}
-  ~PushSubscriptionOptionsParams() {}
+struct BLINK_COMMON_EXPORT WebPushSubscriptionOptions {
+  WebPushSubscriptionOptions() {}
+  ~WebPushSubscriptionOptions() {}
 
   // Whether or not the app developer agrees to provide user visible
   // notifications whenever they receive a push message.
@@ -24,9 +24,9 @@
   // The unique identifier of the application service which is used to
   // verify the push message before delivery. This could either be an ID
   // assigned by the developer console or the app server's public key.
-  std::string sender_info;
+  std::string application_server_key;
 };
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PUSH_MESSAGING_PUSH_SUBSCRIPTION_OPTIONS_PARAMS_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_OPTIONS_H_
diff --git a/third_party/blink/public/mojom/push_messaging/push_messaging.mojom b/third_party/blink/public/mojom/push_messaging/push_messaging.mojom
index 2cdc111..80b63925 100644
--- a/third_party/blink/public/mojom/push_messaging/push_messaging.mojom
+++ b/third_party/blink/public/mojom/push_messaging/push_messaging.mojom
@@ -12,7 +12,7 @@
 
 struct PushSubscriptionOptions {
   bool user_visible_only;
-  string sender_info;
+  string application_server_key;
 };
 
 enum PushErrorType {
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index a54bdd0..415b6b3 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2292,6 +2292,7 @@
   kV8RTCRtpReceiver_JitterBufferDelayHint_AttributeGetter = 2886,
   kV8RTCRtpReceiver_JitterBufferDelayHint_AttributeSetter = 2887,
   kMediaCapabilitiesDecodingInfoWithKeySystemConfig = 2888,
+  kRevertInCustomIdent = 2889,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h b/third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h
index 07862583..fccd6ff5 100644
--- a/third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h
+++ b/third_party/blink/public/platform/modules/push_messaging/web_push_subscription.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_H_
 
-#include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_vector.h"
@@ -22,7 +22,7 @@
                       const WebVector<unsigned char>& auth)
       : endpoint(endpoint), p256dh(p256dh), auth(auth) {
     options.user_visible_only = user_visible_only;
-    options.application_server_key = application_server_key;
+    options.application_server_key = application_server_key.Latin1();
   }
 
   WebURL endpoint;
diff --git a/third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h b/third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h
deleted file mode 100644
index 8229fa26..0000000
--- a/third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2015 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_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_OPTIONS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_OPTIONS_H_
-
-#include "third_party/blink/public/platform/web_string.h"
-
-namespace blink {
-
-struct WebPushSubscriptionOptions {
-  WebPushSubscriptionOptions() : user_visible_only(false) {}
-
-  // Indicates that the subscription will only be used for push messages
-  // that result in UI visible to the user.
-  bool user_visible_only;
-
-  // P-256 public key, in uncompressed form, of the app server that can send
-  // push messages to this subscription.
-  // TODO(johnme): Make this a WebVector<uint8_t>.
-  WebString application_server_key;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_PUSH_MESSAGING_WEB_PUSH_SUBSCRIPTION_OPTIONS_H_
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index e6a8887a..d0f6eeb 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -213,10 +213,6 @@
   // Start navigation to the given URL.
   virtual void StartNavigation(const WebURLRequest&) = 0;
 
-  // Returns the document loader that is currently loading.  May be null.
-  // TODO(dgozman): move this to WebNavigationControl.
-  virtual WebDocumentLoader* GetProvisionalDocumentLoader() const = 0;
-
   // View-source rendering mode.  Set this before loading an URL to cause
   // it to be rendered in view-source mode.
   virtual void EnableViewSourceMode(bool) = 0;
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.cc b/third_party/blink/renderer/bindings/core/v8/module_record.cc
index 5ecdb23..b13469ab 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.cc
@@ -131,12 +131,14 @@
   // returning from here.
   v8::TryCatch try_catch(isolate);
 
-  probe::ExecuteScript probe(ExecutionContext::From(script_state), source_url_);
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  probe::ExecuteScript probe(execution_context, source_url_);
 
   // TODO(kouhei): We currently don't have a code-path which use return value of
   // EvaluateModule. Stop ignoring result once we have such path.
   v8::Local<v8::Value> result;
-  if (!V8ScriptRunner::EvaluateModule(isolate, module_->NewLocal(isolate),
+  if (!V8ScriptRunner::EvaluateModule(isolate, execution_context,
+                                      module_->NewLocal(isolate),
                                       script_state->GetContext())
            .ToLocal(&result)) {
     DCHECK(try_catch.HasCaught());
diff --git a/third_party/blink/renderer/bindings/core/v8/script_regexp.cc b/third_party/blink/renderer/bindings/core/v8/script_regexp.cc
index cf5d816..910cafe0 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_regexp.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_regexp.cc
@@ -94,8 +94,9 @@
   v8::Local<v8::Value> argv[] = {
       V8String(isolate, string.Substring(start_from))};
   v8::Local<v8::Value> return_value;
-  if (!V8ScriptRunner::CallInternalFunction(isolate, exec.As<v8::Function>(),
-                                            regex, base::size(argv), argv)
+  if (!V8ScriptRunner::CallInternalFunction(isolate, nullptr,
+                                            exec.As<v8::Function>(), regex,
+                                            base::size(argv), argv)
            .ToLocal(&return_value))
     return -1;
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
index 49ca746..5f649d42 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/qualified_name.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
@@ -61,6 +62,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
 #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -926,4 +928,15 @@
       isolate, property_names, exception_state);
 }
 
+v8::MicrotaskQueue* ToMicrotaskQueue(ExecutionContext* execution_context) {
+  if (!execution_context)
+    return nullptr;
+  Agent* agent = execution_context->GetAgent();
+  return agent ? agent->event_loop()->microtask_queue() : nullptr;
+}
+
+v8::MicrotaskQueue* ToMicrotaskQueue(ScriptState* script_state) {
+  return ToMicrotaskQueue(ExecutionContext::From(script_state));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h
index 75a186d..a0340739 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h
@@ -555,6 +555,9 @@
                                                const v8::Local<v8::Object>&,
                                                ExceptionState&);
 
+v8::MicrotaskQueue* ToMicrotaskQueue(ExecutionContext*);
+v8::MicrotaskQueue* ToMicrotaskQueue(ScriptState*);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_BINDING_FOR_CORE_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index de0a0ca6..0d289cb6 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_code_cache.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_initializer.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -43,6 +44,7 @@
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
@@ -70,14 +72,15 @@
 }
 
 v8::MaybeLocal<v8::Value> ThrowStackOverflowExceptionIfNeeded(
-    v8::Isolate* isolate) {
+    v8::Isolate* isolate,
+    v8::MicrotaskQueue* microtask_queue) {
   if (V8PerIsolateData::From(isolate)->IsHandlingRecursionLevelError()) {
     // If we are already handling a recursion level error, we should
     // not invoke v8::Function::Call.
     return v8::Undefined(isolate);
   }
   v8::MicrotasksScope microtasks_scope(
-      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
+      isolate, microtask_queue, v8::MicrotasksScope::kDoNotRunMicrotasks);
   V8PerIsolateData::From(isolate)->SetIsHandlingRecursionLevelError(true);
 
   ScriptForbiddenScope::AllowUserAgentScript allow_script;
@@ -163,6 +166,13 @@
   return v8::MaybeLocal<v8::Script>();
 }
 
+int GetMicrotasksScopeDepth(v8::Isolate* isolate,
+                            v8::MicrotaskQueue* microtask_queue) {
+  if (microtask_queue)
+    return microtask_queue->GetMicrotasksScopeDepth();
+  return v8::MicrotasksScope::GetCurrentDepth(isolate);
+}
+
 }  // namespace
 
 v8::MaybeLocal<v8::Script> V8ScriptRunner::CompileScript(
@@ -303,8 +313,9 @@
   RuntimeCallStatsScopedTracer rcs_scoped_tracer(isolate);
   RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
 
-  if (v8::MicrotasksScope::GetCurrentDepth(isolate) >= kMaxRecursionDepth)
-    return ThrowStackOverflowExceptionIfNeeded(isolate);
+  v8::MicrotaskQueue* microtask_queue = ToMicrotaskQueue(context);
+  if (GetMicrotasksScopeDepth(isolate, microtask_queue) > kMaxRecursionDepth)
+    return ThrowStackOverflowExceptionIfNeeded(isolate, microtask_queue);
 
   CHECK(!context->IsIteratingOverObservers());
 
@@ -315,8 +326,9 @@
       ThrowScriptForbiddenException(isolate);
       return v8::MaybeLocal<v8::Value>();
     }
+
     v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
-    v8::MicrotasksScope microtasks_scope(isolate,
+    v8::MicrotasksScope microtasks_scope(isolate, microtask_queue,
                                          v8::MicrotasksScope::kRunMicrotasks);
     v8::Local<v8::String> script_url;
     if (!script_name->ToString(isolate->GetCurrentContext())
@@ -363,7 +375,8 @@
   RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
   v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
   v8::MicrotasksScope microtasks_scope(
-      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
+      isolate, ToMicrotaskQueue(script_state),
+      v8::MicrotasksScope::kDoNotRunMicrotasks);
   v8::MaybeLocal<v8::Value> result = script->Run(isolate->GetCurrentContext());
   CHECK(!isolate->IsDead());
   return result;
@@ -378,9 +391,10 @@
   TRACE_EVENT0("v8", "v8.callAsConstructor");
   RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
 
-  int depth = v8::MicrotasksScope::GetCurrentDepth(isolate);
+  v8::MicrotaskQueue* microtask_queue = ToMicrotaskQueue(context);
+  int depth = GetMicrotasksScopeDepth(isolate, microtask_queue);
   if (depth >= kMaxRecursionDepth)
-    return ThrowStackOverflowExceptionIfNeeded(isolate);
+    return ThrowStackOverflowExceptionIfNeeded(isolate, microtask_queue);
 
   CHECK(!context->IsIteratingOverObservers());
 
@@ -397,7 +411,7 @@
   v8::Local<v8::Function> function = constructor.As<v8::Function>();
 
   v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
-  v8::MicrotasksScope microtasks_scope(isolate,
+  v8::MicrotasksScope microtasks_scope(isolate, ToMicrotaskQueue(context),
                                        v8::MicrotasksScope::kRunMicrotasks);
   probe::CallFunction probe(context, function, depth);
 
@@ -430,9 +444,10 @@
   RuntimeCallStatsScopedTracer rcs_scoped_tracer(isolate);
   RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
 
-  int depth = v8::MicrotasksScope::GetCurrentDepth(isolate);
+  v8::MicrotaskQueue* microtask_queue = ToMicrotaskQueue(context);
+  int depth = GetMicrotasksScopeDepth(isolate, microtask_queue);
   if (depth >= kMaxRecursionDepth)
-    return ThrowStackOverflowExceptionIfNeeded(isolate);
+    return ThrowStackOverflowExceptionIfNeeded(isolate, microtask_queue);
 
   CHECK(!context->IsIteratingOverObservers());
 
@@ -445,7 +460,7 @@
                        ToLocalDOMWindow(function->CreationContext()), frame,
                        BindingSecurity::ErrorReportOption::kDoNotReport));
   v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
-  v8::MicrotasksScope microtasks_scope(isolate,
+  v8::MicrotasksScope microtasks_scope(isolate, microtask_queue,
                                        v8::MicrotasksScope::kRunMicrotasks);
   if (!depth) {
     TRACE_EVENT_BEGIN1("devtools.timeline", "FunctionCall", "data",
@@ -465,6 +480,7 @@
 
 v8::MaybeLocal<v8::Value> V8ScriptRunner::CallInternalFunction(
     v8::Isolate* isolate,
+    v8::MicrotaskQueue* microtask_queue,
     v8::Local<v8::Function> function,
     v8::Local<v8::Value> receiver,
     int argc,
@@ -475,7 +491,7 @@
 
   v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
   v8::MicrotasksScope microtasks_scope(
-      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
+      isolate, microtask_queue, v8::MicrotasksScope::kDoNotRunMicrotasks);
   v8::MaybeLocal<v8::Value> result =
       function->Call(isolate->GetCurrentContext(), receiver, argc, args);
   CHECK(!isolate->IsDead());
@@ -484,12 +500,14 @@
 
 v8::MaybeLocal<v8::Value> V8ScriptRunner::EvaluateModule(
     v8::Isolate* isolate,
+    ExecutionContext* execution_context,
     v8::Local<v8::Module> module,
     v8::Local<v8::Context> context) {
   TRACE_EVENT0("v8,devtools.timeline", "v8.evaluateModule");
   RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
   v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
   v8::MicrotasksScope microtasks_scope(isolate,
+                                       ToMicrotaskQueue(execution_context),
                                        v8::MicrotasksScope::kRunMicrotasks);
   return module->Evaluate(context);
 }
@@ -521,7 +539,8 @@
     return v8::MaybeLocal<v8::Value>();
   v8::Local<v8::Function> function = function_value.As<v8::Function>();
   return V8ScriptRunner::CallInternalFunction(
-      isolate, function, v8::Undefined(isolate), num_args, args);
+      isolate, ToMicrotaskQueue(script_state), function, v8::Undefined(isolate),
+      num_args, args);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index 5e3f7d59..0fde1ce 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -81,6 +81,7 @@
       v8::Local<v8::Value> argv[] = nullptr);
   static v8::MaybeLocal<v8::Value> CallInternalFunction(
       v8::Isolate*,
+      v8::MicrotaskQueue*,
       v8::Local<v8::Function>,
       v8::Local<v8::Value> receiver,
       int argc,
@@ -92,6 +93,7 @@
                                                 v8::Local<v8::Value> info[],
                                                 v8::Isolate*);
   static v8::MaybeLocal<v8::Value> EvaluateModule(v8::Isolate*,
+                                                  ExecutionContext*,
                                                   v8::Local<v8::Module>,
                                                   v8::Local<v8::Context>);
 
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index 679754a0..9083897 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -43,6 +43,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_initializer.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
 #include "third_party/blink/renderer/core/events/error_event.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
@@ -55,6 +56,7 @@
 #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -163,10 +165,15 @@
     v8::ExtensionConfiguration extension_configuration =
         ScriptController::ExtensionsFor(global_scope_);
 
+    Agent* agent = global_scope_->GetAgent();
+    DCHECK(agent);
+
     V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
         V8PerIsolateData::From(isolate_));
-    context =
-        v8::Context::New(isolate_, &extension_configuration, global_template);
+    context = v8::Context::New(isolate_, &extension_configuration,
+                               global_template, v8::MaybeLocal<v8::Value>(),
+                               v8::DeserializeInternalFieldsCallback(),
+                               agent->event_loop()->microtask_queue());
   }
   if (context.IsEmpty())
     return false;
diff --git a/third_party/blink/renderer/build/scripts/templates/instrumenting_probes_impl.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/instrumenting_probes_impl.cc.tmpl
index 0d27ca76f..c9c1de31 100644
--- a/third_party/blink/renderer/build/scripts/templates/instrumenting_probes_impl.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/instrumenting_probes_impl.cc.tmpl
@@ -25,19 +25,6 @@
   return mutex;
 }
 
-// HeapLinkedHashSet does not support modification during iteration.
-// This helper copies items first so calees iterate over the copied container.
-template <typename T>
-HeapVector<Member<T>> CopyLinkedHashSetToVector(
-    const HeapLinkedHashSet<Member<T>>& linked_hash_set) {
-  unsigned capacity = linked_hash_set.size();
-  HeapVector<Member<T>> result;
-  result.ReserveInitialCapacity(capacity);
-  for (T* agent : linked_hash_set)
-      result.push_back(agent);
-  return result;
-}
-
 }  // namespace
 
 // static
@@ -114,8 +101,7 @@
 {% for agent in probe.agents %}
 {% set class_name = agent | agent_name_to_class %}
   if (probe_sink->Has{{agent}}s()) {
-    auto copy = CopyLinkedHashSetToVector(probe_sink->{{class_name}}s());
-    for ({{class_name}}* agent : copy)
+    for ({{class_name}}* agent : probe_sink->{{class_name}}s())
       agent->{{agent_probe_name}}({{caller()}});
   }
 {% endfor %}
diff --git a/third_party/blink/renderer/build/scripts/templates/probe_sink.h.tmpl b/third_party/blink/renderer/build/scripts/templates/probe_sink.h.tmpl
index 25ed17e5..19a4466 100644
--- a/third_party/blink/renderer/build/scripts/templates/probe_sink.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/probe_sink.h.tmpl
@@ -43,7 +43,7 @@
 {% set class_name = agent | agent_name_to_class %}
 {% set getter_name = agent | to_snake_case %}
   bool Has{{agent}}s() const { return !{{getter_name}}s_.IsEmpty(); }
-  const HeapLinkedHashSet<Member<{{class_name}}>>& {{class_name}}s() const { return {{getter_name}}s_; }
+  const HeapListHashSet<Member<{{class_name}}>>& {{class_name}}s() const { return {{getter_name}}s_; }
   void Add{{agent}}({{class_name}}* agent);
   void Remove{{agent}}({{class_name}}* agent);
 
@@ -57,7 +57,7 @@
 {% for agent in agents %}
 {% set class_name = agent | agent_name_to_class %}
 {% set getter_name = agent | to_snake_case %}
-  HeapLinkedHashSet<Member<{{class_name}}>> {{getter_name}}s_;
+  HeapListHashSet<Member<{{class_name}}>> {{getter_name}}s_;
 {% endfor %}
 
   // Number of sinks with an enabled agent of each type, used to keep
diff --git a/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc b/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
index 4595f4c..a463dfa 100644
--- a/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
+++ b/third_party/blink/renderer/core/accessibility/apply_dark_mode_test.cc
@@ -75,14 +75,14 @@
                                               GetLayoutView()));
 }
 
-TEST_F(ApplyDarkModeCheckTest, SupportedColorSchemesDark) {
-  RuntimeEnabledFeatures::SetMetaSupportedColorSchemesEnabled(true);
+TEST_F(ApplyDarkModeCheckTest, MetaColorSchemeDark) {
+  RuntimeEnabledFeatures::SetMetaColorSchemeEnabled(true);
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
   GetDocument().GetSettings()->SetPreferredColorScheme(
       PreferredColorScheme::kDark);
   ColorSchemeSet schemes;
   schemes.Set(ColorScheme::kDark);
-  GetDocument().GetStyleEngine().SetSupportedColorSchemes(schemes);
+  GetDocument().GetStyleEngine().SetMetaColorScheme(schemes);
   UpdateAllLifecyclePhasesForTest();
 
   // Opting out of forced darkening when dark is among the supported color
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index 96c020f..44b51d10 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -589,10 +589,12 @@
 
   auto style = GetDocument().EnsureStyleResolver().StyleForElement(element_);
   EXPECT_TRUE(style->NonInheritedVariables());
-  EXPECT_TRUE(
-      style->NonInheritedVariables()->GetVariable(AtomicString("--foo")));
-  EXPECT_TRUE(
-      style->NonInheritedVariables()->GetVariable(AtomicString("--bar")));
+  EXPECT_TRUE(style->NonInheritedVariables()
+                  ->GetData(AtomicString("--foo"))
+                  .value_or(nullptr));
+  EXPECT_TRUE(style->NonInheritedVariables()
+                  ->GetData(AtomicString("--bar"))
+                  .value_or(nullptr));
 
   StringKeyframe* keyframe = CreateReplaceOpKeyframe("--foo", "10");
   EXPECT_TRUE(DuplicateSingleKeyframeAndTestIsCandidateOnResult(keyframe));
diff --git a/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc b/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc
index b9ad1f5..306e8a5 100644
--- a/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc
+++ b/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc
@@ -67,7 +67,7 @@
         return nullptr;
       }
       const AtomicString& property_name = property.CustomPropertyName();
-      const CSSValue* value = style.GetRegisteredVariable(property_name);
+      const CSSValue* value = style.GetVariableValue(property_name);
       const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value);
       if (!primitive_value || !primitive_value->IsNumber())
         return nullptr;
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
index 2908159..1643a75 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
@@ -69,8 +69,7 @@
   bool IsValid(const StyleResolverState& state,
                const InterpolationValue&) const final {
     const CSSValue* inherited_value =
-        state.ParentStyle()->GetRegisteredVariable(name_,
-                                                   is_inherited_property_);
+        state.ParentStyle()->GetVariableValue(name_, is_inherited_property_);
     if (!inherited_value) {
       inherited_value = initial_value_.Get();
     }
@@ -192,8 +191,8 @@
     if (declaration.IsInitial(is_inherited_property)) {
       value = Registration().Initial();
     } else {
-      value = state.ParentStyle()->GetRegisteredVariable(name,
-                                                         is_inherited_property);
+      value =
+          state.ParentStyle()->GetVariableValue(name, is_inherited_property);
       if (!value) {
         value = Registration().Initial();
       }
@@ -242,7 +241,7 @@
   const PropertyHandle property = GetProperty();
   const AtomicString& name = property.CustomPropertyName();
   const CSSValue* underlying_value =
-      style.GetRegisteredVariable(name, Registration().Inherits());
+      style.GetVariableValue(name, Registration().Inherits());
   if (!underlying_value)
     return nullptr;
   // TODO(alancutter): Remove the need for passing in conversion checkers.
@@ -288,8 +287,8 @@
   const PropertyHandle property = GetProperty();
   const AtomicString& property_name = property.CustomPropertyName();
   bool inherits = Registration().Inherits();
-  style.SetVariable(property_name, std::move(variable_data), inherits);
-  style.SetRegisteredVariable(property_name, css_value, inherits);
+  style.SetVariableData(property_name, std::move(variable_data), inherits);
+  style.SetVariableValue(property_name, css_value, inherits);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
index da36c0dd..1173a1b 100644
--- a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
@@ -107,8 +107,8 @@
     const InterpolationEnvironment& environment) const {
   const ComputedStyle& style =
       ToCSSInterpolationEnvironment(environment).Style();
-  DCHECK(!style.GetVariable(GetProperty().CustomPropertyName()) ||
-         !style.GetVariable(GetProperty().CustomPropertyName())
+  DCHECK(!style.GetVariableData(GetProperty().CustomPropertyName()) ||
+         !style.GetVariableData(GetProperty().CustomPropertyName())
               ->NeedsVariableResolution());
   return nullptr;
 }
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index 5ee1a10..8205c12 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -65,8 +65,7 @@
                                           const ComputedStyle& b) {
   if (property.IsCSSCustomProperty()) {
     const AtomicString& name = property.CustomPropertyName();
-    return DataEquivalent(a.GetRegisteredVariable(name),
-                          b.GetRegisteredVariable(name));
+    return DataEquivalent(a.GetVariableValue(name), b.GetVariableValue(name));
   }
   switch (property.GetCSSProperty().PropertyID()) {
     case CSSPropertyID::kBackgroundColor:
diff --git a/third_party/blink/renderer/core/css/css_style_sheet.cc b/third_party/blink/renderer/core/css/css_style_sheet.cc
index 77be14b..cc732061 100644
--- a/third_party/blink/renderer/core/css/css_style_sheet.cc
+++ b/third_party/blink/renderer/core/css/css_style_sheet.cc
@@ -226,6 +226,8 @@
       resolver->InvalidateMatchedPropertiesCache();
   } else if (!adopted_tree_scopes_.IsEmpty()) {
     for (auto tree_scope : adopted_tree_scopes_) {
+      if (!tree_scope->RootNode().isConnected())
+        continue;
       tree_scope->GetDocument().GetStyleEngine().SetNeedsActiveStyleUpdate(
           *tree_scope);
       if (StyleResolver* resolver =
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.cc b/third_party/blink/renderer/core/css/css_test_helpers.cc
index 9b1fac6..75d46d95 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.cc
+++ b/third_party/blink/renderer/core/css/css_test_helpers.cc
@@ -5,8 +5,11 @@
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
 #include "third_party/blink/renderer/core/css/css_rule_list.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
+#include "third_party/blink/renderer/core/css/css_variable_data.h"
+#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/css/property_descriptor.h"
 #include "third_party/blink/renderer/core/css/property_registration.h"
 #include "third_party/blink/renderer/core/css/property_registry.h"
@@ -69,5 +72,19 @@
   ASSERT_FALSE(exception_state.HadException());
 }
 
+scoped_refptr<CSSVariableData> CreateVariableData(String s) {
+  auto tokens = CSSTokenizer(s).TokenizeToEOF();
+  CSSParserTokenRange range(tokens);
+  bool is_animation_tainted = false;
+  bool needs_variable_resolution = false;
+  return CSSVariableData::Create(range, is_animation_tainted,
+                                 needs_variable_resolution, KURL(),
+                                 WTF::TextEncoding());
+}
+
+const CSSValue* CreateCustomIdent(AtomicString s) {
+  return MakeGarbageCollected<CSSCustomIdentValue>(s);
+}
+
 }  // namespace css_test_helpers
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.h b/third_party/blink/renderer/core/css/css_test_helpers.h
index 3cf1af00..cdb0e4a 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.h
+++ b/third_party/blink/renderer/core/css/css_test_helpers.h
@@ -13,6 +13,8 @@
 
 class Document;
 class CSSStyleSheet;
+class CSSVariableData;
+class CSSValue;
 
 namespace css_test_helpers {
 
@@ -46,6 +48,9 @@
                       const String& initial_value,
                       bool is_inherited);
 
+scoped_refptr<CSSVariableData> CreateVariableData(String);
+const CSSValue* CreateCustomIdent(AtomicString);
+
 }  // namespace css_test_helpers
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index ee2107f2..a728cc75 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -588,6 +588,8 @@
 
   if (EqualIgnoringASCIICase(token.Value(), "default"))
     context.Count(WebFeature::kDefaultInCustomIdent);
+  if (EqualIgnoringASCIICase(token.Value(), "revert"))
+    context.Count(WebFeature::kRevertInCustomIdent);
 
   return MakeGarbageCollected<CSSCustomIdentValue>(
       token.Value().ToAtomicString());
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
index 2c309c0..2b050ac 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_test.cc
@@ -530,16 +530,34 @@
   EXPECT_FALSE(IsCounted(feature));
 }
 
-TEST_F(CSSPropertyUseCounterTest, CSSPropertyAnimationNameCustomIdentUseCount) {
+TEST_F(CSSPropertyUseCounterTest, CSSPropertyDefaultAnimationNameUseCount) {
   WebFeature feature = WebFeature::kDefaultInCustomIdent;
   EXPECT_FALSE(IsCounted(feature));
+
   ParseProperty(CSSPropertyID::kAnimationName, "initial");
-  // css-wide keywords in custom ident other than default should not register.
   EXPECT_FALSE(IsCounted(feature));
+
+  ParseProperty(CSSPropertyID::kAnimationName, "test");
+  EXPECT_FALSE(IsCounted(feature));
+
   ParseProperty(CSSPropertyID::kAnimationName, "default");
   EXPECT_TRUE(IsCounted(feature));
 }
 
+TEST_F(CSSPropertyUseCounterTest, CSSPropertyRevertAnimationNameUseCount) {
+  WebFeature feature = WebFeature::kRevertInCustomIdent;
+  EXPECT_FALSE(IsCounted(feature));
+
+  ParseProperty(CSSPropertyID::kAnimationName, "initial");
+  EXPECT_FALSE(IsCounted(feature));
+
+  ParseProperty(CSSPropertyID::kAnimationName, "test");
+  EXPECT_FALSE(IsCounted(feature));
+
+  ParseProperty(CSSPropertyID::kAnimationName, "revert");
+  EXPECT_TRUE(IsCounted(feature));
+}
+
 TEST_F(CSSPropertyUseCounterTest, CSSPropertyContainStyleUseCount) {
   WebFeature feature = WebFeature::kCSSValueContainStyle;
   EXPECT_FALSE(IsCounted(feature));
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc b/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
index e2195da..c24e5e8 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
@@ -39,29 +39,27 @@
   bool is_inherited_property = IsInherited();
 
   if (!registration_) {
-    state.Style()->SetVariable(name_, nullptr, is_inherited_property);
+    state.Style()->SetVariableData(name_, nullptr, is_inherited_property);
     return;
   }
 
-  state.Style()->SetVariable(name_, registration_->InitialVariableData(),
-                             is_inherited_property);
-  state.Style()->SetRegisteredVariable(name_, registration_->Initial(),
-                                       is_inherited_property);
+  state.Style()->SetVariableData(name_, registration_->InitialVariableData(),
+                                 is_inherited_property);
+  state.Style()->SetVariableValue(name_, registration_->Initial(),
+                                  is_inherited_property);
 }
 
 void CustomProperty::ApplyInherit(StyleResolverState& state) const {
   bool is_inherited_property = IsInherited();
 
-  CSSVariableData* parent_value =
-      state.ParentStyle()->GetVariable(name_, is_inherited_property);
+  CSSVariableData* parent_data =
+      state.ParentStyle()->GetVariableData(name_, is_inherited_property);
 
-  state.Style()->SetVariable(name_, parent_value, is_inherited_property);
+  state.Style()->SetVariableData(name_, parent_data, is_inherited_property);
 
   if (registration_) {
-    const CSSValue* parent_value =
-        state.ParentStyle()->GetRegisteredVariable(name_);
-    state.Style()->SetRegisteredVariable(name_, parent_value,
-                                         is_inherited_property);
+    const CSSValue* parent_value = state.ParentStyle()->GetVariableValue(name_);
+    state.Style()->SetVariableValue(name_, parent_value, is_inherited_property);
   }
 }
 
@@ -81,11 +79,10 @@
   } else if (inherit) {
     ApplyInherit(state);
   } else {
-    state.Style()->SetVariable(name_, declaration.Value(),
-                               is_inherited_property);
+    state.Style()->SetVariableData(name_, declaration.Value(),
+                                   is_inherited_property);
     if (registration_) {
-      state.Style()->SetRegisteredVariable(name_, nullptr,
-                                           is_inherited_property);
+      state.Style()->SetVariableValue(name_, nullptr, is_inherited_property);
     }
   }
 }
@@ -115,7 +112,7 @@
     Node* styled_node,
     bool allow_visited_style) const {
   if (registration_) {
-    const CSSValue* value = style.GetRegisteredVariable(name_, IsInherited());
+    const CSSValue* value = style.GetVariableValue(name_, IsInherited());
     if (value)
       return value;
     // If we don't have CSSValue for this registered property, it means that
@@ -123,7 +120,7 @@
     // hence we proceed with unregistered behavior.
   }
 
-  CSSVariableData* data = style.GetVariable(name_, IsInherited());
+  CSSVariableData* data = style.GetVariableData(name_, IsInherited());
 
   if (!data)
     return nullptr;
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_data_test.cc b/third_party/blink/renderer/core/css/resolver/css_variable_data_test.cc
index f437d9cd..dd1f932a 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_data_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_data_test.cc
@@ -4,19 +4,12 @@
 
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 
 namespace blink {
 
-namespace {
-
-scoped_refptr<CSSVariableData> CreateVariableData(const String& string) {
-  auto tokens = CSSTokenizer(string).TokenizeToEOF();
-  return CSSVariableData::Create(tokens, false, false, KURL(),
-                                 WTF::TextEncoding());
-}
-
-}  // namespace
+using namespace css_test_helpers;
 
 TEST(CSSVariableDataTest, FontUnitsDetected) {
   EXPECT_FALSE(CreateVariableData("100px")->HasFontUnits());
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
index f237d54..a99c4f0 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
@@ -71,7 +71,7 @@
   const PropertyRegistration* registration =
       registry_ ? registry_->Registration(name) : nullptr;
 
-  CSSVariableData* variable_data = GetVariable(name, registration);
+  CSSVariableData* variable_data = GetVariableData(name, registration);
 
   if (!variable_data)
     return nullptr;
@@ -93,11 +93,11 @@
 
   if (!registration) {
     if (resolved_data != variable_data && options.absolutize)
-      SetVariable(name, registration, resolved_data);
+      SetVariableData(name, registration, resolved_data);
     return resolved_data;
   }
 
-  const CSSValue* value = GetRegisteredVariable(name, *registration);
+  const CSSValue* value = GetVariableValue(name, *registration);
   const CSSValue* resolved_value = value;
 
   // The computed value of a registered property must be stored as a CSSValue
@@ -116,8 +116,8 @@
   // If either parsing or resolution failed, fall back on "unset".
   if (!resolved_data) {
     if (registration->Inherits()) {
-      resolved_data = state_.ParentStyle()->GetVariable(name, true);
-      resolved_value = state_.ParentStyle()->GetRegisteredVariable(name, true);
+      resolved_data = state_.ParentStyle()->GetVariableData(name, true);
+      resolved_value = state_.ParentStyle()->GetVariableValue(name, true);
     } else {
       resolved_data = registration->InitialVariableData();
       resolved_value = registration->Initial();
@@ -141,14 +141,14 @@
   // token sequence to retain any var()-references. This makes it possible to
   // resolve the var()-reference again, using a different (e.g. animated) value.
   if (options.absolutize && resolved_data != variable_data)
-    SetVariable(name, registration, resolved_data);
+    SetVariableData(name, registration, resolved_data);
 
   // The options.absolutize flag does not apply to the computed value, only
   // to the tokens used for substitution. Hence, store the computed value on
   // ComputedStyle, regardless of the flag. This is needed to correctly
   // calculate animations.
   if (value != resolved_value)
-    SetRegisteredVariable(name, *registration, resolved_value);
+    SetVariableValue(name, *registration, resolved_value);
 
   return resolved_data;
 }
@@ -210,51 +210,51 @@
           variable_data.HasRootFontUnits());
 }
 
-CSSVariableData* CSSVariableResolver::GetVariable(
+CSSVariableData* CSSVariableResolver::GetVariableData(
     const AtomicString& name,
     const PropertyRegistration* registration) {
-  return state_.Style()->GetVariable(name,
-                                     !registration || registration->Inherits());
+  return state_.Style()->GetVariableData(
+      name, !registration || registration->Inherits());
 }
 
-const CSSValue* CSSVariableResolver::GetRegisteredVariable(
+const CSSValue* CSSVariableResolver::GetVariableValue(
     const AtomicString& name,
     const PropertyRegistration& registration) {
-  return state_.Style()->GetRegisteredVariable(name, registration.Inherits());
+  return state_.Style()->GetVariableValue(name, registration.Inherits());
 }
 
-void CSSVariableResolver::SetVariable(
+void CSSVariableResolver::SetVariableData(
     const AtomicString& name,
     const PropertyRegistration* registration,
     scoped_refptr<CSSVariableData> variable_data) {
   if (!registration || registration->Inherits()) {
     DCHECK(inherited_variables_);
-    inherited_variables_->SetVariable(name, std::move(variable_data));
+    inherited_variables_->SetData(name, std::move(variable_data));
   } else {
     DCHECK(non_inherited_variables_);
-    non_inherited_variables_->SetVariable(name, std::move(variable_data));
+    non_inherited_variables_->SetData(name, std::move(variable_data));
   }
 }
 
-void CSSVariableResolver::SetRegisteredVariable(
+void CSSVariableResolver::SetVariableValue(
     const AtomicString& name,
     const PropertyRegistration& registration,
     const CSSValue* value) {
   if (registration.Inherits()) {
     DCHECK(inherited_variables_);
-    inherited_variables_->SetRegisteredVariable(name, value);
+    inherited_variables_->SetValue(name, value);
   } else {
     DCHECK(non_inherited_variables_);
-    non_inherited_variables_->SetRegisteredVariable(name, value);
+    non_inherited_variables_->SetValue(name, value);
   }
 }
 
 void CSSVariableResolver::SetInvalidVariable(
     const AtomicString& name,
     const PropertyRegistration* registration) {
-  SetVariable(name, registration, nullptr);
+  SetVariableData(name, registration, nullptr);
   if (registration)
-    SetRegisteredVariable(name, *registration, nullptr);
+    SetVariableValue(name, *registration, nullptr);
 }
 
 bool CSSVariableResolver::ResolveVariableReference(CSSParserTokenRange range,
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
index b47f9484..4b61055 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
@@ -195,16 +195,16 @@
   // The following utilities get/set variables on either StyleInheritedVariables
   // or StyleNonInheritedVariables, according to their PropertyRegistration.
 
-  CSSVariableData* GetVariable(const AtomicString& name,
-                               const PropertyRegistration*);
-  const CSSValue* GetRegisteredVariable(const AtomicString& name,
-                                        const PropertyRegistration&);
-  void SetVariable(const AtomicString& name,
-                   const PropertyRegistration*,
-                   scoped_refptr<CSSVariableData>);
-  void SetRegisteredVariable(const AtomicString& name,
-                             const PropertyRegistration&,
-                             const CSSValue*);
+  CSSVariableData* GetVariableData(const AtomicString& name,
+                                   const PropertyRegistration*);
+  const CSSValue* GetVariableValue(const AtomicString& name,
+                                   const PropertyRegistration&);
+  void SetVariableData(const AtomicString& name,
+                       const PropertyRegistration*,
+                       scoped_refptr<CSSVariableData>);
+  void SetVariableValue(const AtomicString& name,
+                        const PropertyRegistration&,
+                        const CSSValue*);
   void SetInvalidVariable(const AtomicString& name,
                           const PropertyRegistration*);
   const StyleResolverState& state_;
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
index 0a8869a4..1c60d40 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver_test.cc
@@ -206,8 +206,8 @@
 
   const auto* prop = CreateCustomProperty("#fefefe");
 
-  inherited_variables->SetVariable("--prop", prop->Value());
-  non_inherited_variables->SetVariable("--prop", prop->Value());
+  inherited_variables->SetData("--prop", prop->Value());
+  non_inherited_variables->SetData("--prop", prop->Value());
 
   EXPECT_FALSE(inherited_variables->NeedsResolution());
   EXPECT_FALSE(non_inherited_variables->NeedsResolution());
@@ -224,16 +224,16 @@
   const auto* prop1 = CreateCustomProperty("var(--prop2)");
   const auto* prop2 = CreateCustomProperty("#fefefe");
 
-  inherited_variables->SetVariable("--prop1", prop1->Value());
-  non_inherited_variables->SetVariable("--prop1", prop1->Value());
+  inherited_variables->SetData("--prop1", prop1->Value());
+  non_inherited_variables->SetData("--prop1", prop1->Value());
 
   EXPECT_TRUE(inherited_variables->NeedsResolution());
   EXPECT_TRUE(non_inherited_variables->NeedsResolution());
 
   // While NeedsResolution() == true, add some properties without
   // var()-references.
-  inherited_variables->SetVariable("--prop2", prop2->Value());
-  non_inherited_variables->SetVariable("--prop2", prop2->Value());
+  inherited_variables->SetData("--prop2", prop2->Value());
+  non_inherited_variables->SetData("--prop2", prop2->Value());
 
   // We should still need resolution even after adding properties that don't
   // have var-references.
@@ -254,8 +254,8 @@
 
   const auto* prop = CreateCustomProperty("var(--x)");
 
-  inherited_variables->SetVariable("--prop", prop->Value());
-  non_inherited_variables->SetVariable("--prop", prop->Value());
+  inherited_variables->SetData("--prop", prop->Value());
+  non_inherited_variables->SetData("--prop", prop->Value());
 
   EXPECT_TRUE(inherited_variables->NeedsResolution());
   EXPECT_TRUE(non_inherited_variables->NeedsResolution());
@@ -319,15 +319,15 @@
   scoped_refptr<StyleInheritedVariables> inherited_variables =
       StyleInheritedVariables::Create();
   AtomicString name("--test");
-  inherited_variables->SetVariable(name, nullptr);
-  inherited_variables->SetRegisteredVariable(name, nullptr);
+  inherited_variables->SetData(name, nullptr);
+  inherited_variables->SetValue(name, nullptr);
 }
 
 TEST_F(CSSVariableResolverTest, DontCrashWhenSettingNonInheritedNullVariable) {
   auto inherited_variables = std::make_unique<StyleNonInheritedVariables>();
   AtomicString name("--test");
-  inherited_variables->SetVariable(name, nullptr);
-  inherited_variables->SetRegisteredVariable(name, nullptr);
+  inherited_variables->SetData(name, nullptr);
+  inherited_variables->SetValue(name, nullptr);
 }
 
 TEST_F(CSSVariableResolverTest, TokenCountAboveLimitIsInValidForSubstitution) {
@@ -343,12 +343,12 @@
 
   // A custom property with more than MaxSubstitutionTokens() is valid ...
   const CSSVariableData* referenced =
-      target->ComputedStyleRef().GetVariable("--referenced");
+      target->ComputedStyleRef().GetVariableData("--referenced");
   ASSERT_TRUE(referenced);
   EXPECT_EQ(MaxSubstitutionTokens() + 1, referenced->Tokens().size());
 
   // ... it is not valid for substitution, however.
-  EXPECT_FALSE(target->ComputedStyleRef().GetVariable("--x"));
+  EXPECT_FALSE(target->ComputedStyleRef().GetVariableData("--x"));
 }
 
 TEST_F(CSSVariableResolverTest, TokenCountAtLimitIsValidForSubstitution) {
@@ -362,11 +362,11 @@
   ASSERT_TRUE(target);
 
   const CSSVariableData* referenced =
-      target->ComputedStyleRef().GetVariable("--referenced");
+      target->ComputedStyleRef().GetVariableData("--referenced");
   ASSERT_TRUE(referenced);
   EXPECT_EQ(MaxSubstitutionTokens(), referenced->Tokens().size());
 
-  const CSSVariableData* x = target->ComputedStyleRef().GetVariable("--x");
+  const CSSVariableData* x = target->ComputedStyleRef().GetVariableData("--x");
   ASSERT_TRUE(x);
   EXPECT_EQ(MaxSubstitutionTokens(), x->Tokens().size());
 
@@ -425,13 +425,13 @@
   // The last --x2^N variable is over the limit. Any reference to that
   // should be invalid.
   const CSSVariableData* ref_last =
-      target->ComputedStyleRef().GetVariable("--ref-last");
+      target->ComputedStyleRef().GetVariableData("--ref-last");
   EXPECT_FALSE(ref_last);
 
   // The next-to-last (--x2^(N-1)) variable is not over the limit. A reference
   // to that is still valid.
   const CSSVariableData* ref_next_to_last =
-      target->ComputedStyleRef().GetVariable("--ref-next-to-last");
+      target->ComputedStyleRef().GetVariableData("--ref-next-to-last");
   ASSERT_TRUE(ref_next_to_last);
   EXPECT_EQ(tokens / 2, ref_next_to_last->Tokens().size());
 
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index c6ee6900..af1ce20 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -992,12 +992,8 @@
     case CSSSelector::kPseudoRequired:
       return element.IsRequiredFormControl();
     case CSSSelector::kPseudoValid:
-      if (mode_ == kResolvingStyle)
-        element.GetDocument().SetContainsValidityStyleRules();
       return element.MatchesValidityPseudoClasses() && element.IsValidElement();
     case CSSSelector::kPseudoInvalid:
-      if (mode_ == kResolvingStyle)
-        element.GetDocument().SetContainsValidityStyleRules();
       return element.MatchesValidityPseudoClasses() &&
              !element.IsValidElement();
     case CSSSelector::kPseudoChecked: {
@@ -1049,12 +1045,8 @@
       DCHECK(is_ua_rule_);
       return element.ContainsPersistentVideo();
     case CSSSelector::kPseudoInRange:
-      if (mode_ == kResolvingStyle)
-        element.GetDocument().SetContainsValidityStyleRules();
       return element.IsInRange();
     case CSSSelector::kPseudoOutOfRange:
-      if (mode_ == kResolvingStyle)
-        element.GetDocument().SetContainsValidityStyleRules();
       return element.IsOutOfRange();
     case CSSSelector::kPseudoFutureCue: {
       auto* vtt_element = DynamicTo<VTTElement>(element);
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 178ee7f59..a5bc0d2 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -1767,20 +1767,30 @@
 void StyleEngine::UpdateStyleInvalidationRoot(ContainerNode* ancestor,
                                               Node* dirty_node) {
   DCHECK(IsMaster());
-  if (GetDocument().IsActive())
+  if (GetDocument().IsActive()) {
+    if (in_dom_removal_) {
+      ancestor = nullptr;
+      dirty_node = document_;
+    }
     style_invalidation_root_.Update(ancestor, dirty_node);
+  }
 }
 
 void StyleEngine::UpdateStyleRecalcRoot(ContainerNode* ancestor,
                                         Node* dirty_node) {
   if (GetDocument().IsActive()) {
     DCHECK(!in_layout_tree_rebuild_);
+    if (in_dom_removal_) {
+      ancestor = nullptr;
+      dirty_node = document_;
+    }
     style_recalc_root_.Update(ancestor, dirty_node);
   }
 }
 
 void StyleEngine::UpdateLayoutTreeRebuildRoot(ContainerNode* ancestor,
                                               Node* dirty_node) {
+  DCHECK(!in_dom_removal_);
   if (GetDocument().IsActive())
     layout_tree_rebuild_root_.Update(ancestor, dirty_node);
 }
@@ -1795,7 +1805,7 @@
   color_scheme_ = ColorScheme::kLight;
 
   if (preferred_color_scheme_ == PreferredColorScheme::kDark) {
-    if (supported_color_schemes_.Contains(ColorScheme::kDark)) {
+    if (meta_color_scheme_.Contains(ColorScheme::kDark)) {
       color_scheme_ = ColorScheme::kDark;
     } else if (settings->ForceDarkModeEnabled()) {
       // Make sure we don't match (prefers-color-scheme: dark) when forced
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 5ba9862b..fc478a8 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -342,12 +342,12 @@
   void RebuildLayoutTree();
   bool InRebuildLayoutTree() const { return in_layout_tree_rebuild_; }
 
-  void SetSupportedColorSchemes(const ColorSchemeSet& supported_color_schemes) {
-    supported_color_schemes_ = supported_color_schemes;
+  void SetMetaColorScheme(const ColorSchemeSet& meta_color_scheme) {
+    meta_color_scheme_ = meta_color_scheme;
     UpdateColorScheme();
   }
-  const ColorSchemeSet& GetSupportedColorSchemes() const {
-    return supported_color_schemes_;
+  const ColorSchemeSet& GetMetaColorScheme() const {
+    return meta_color_scheme_;
   }
   PreferredColorScheme GetPreferredColorScheme() const {
     return preferred_color_scheme_;
@@ -357,8 +357,6 @@
   void Trace(blink::Visitor*) override;
   const char* NameInHeapSnapshot() const override { return "StyleEngine"; }
 
-  bool InDomRemoval() const { return in_dom_removal_; }
-
  private:
   // FontSelectorClient implementation.
   void FontsNeedUpdate(FontSelector*) override;
@@ -534,9 +532,9 @@
   HashMap<AtomicString, FontDisplay> default_font_display_map_;
 
   // Color schemes explicitly supported by the author through the viewport meta
-  // tag. E.g. <meta name="supported-color-schemes" content="light dark">. The
-  // supported schemes are used to opt-out of forced darkening.
-  ColorSchemeSet supported_color_schemes_;
+  // tag. E.g. <meta name="color-scheme" content="light dark">. A dark color-
+  // scheme is used to opt-out of forced darkening.
+  ColorSchemeSet meta_color_scheme_;
 
   // The preferred color scheme is set in settings, but may be overridden by the
   // ForceDarkMode setting where the preferred_color_scheme_ will be set no
@@ -544,7 +542,7 @@
   PreferredColorScheme preferred_color_scheme_ =
       PreferredColorScheme::kNoPreference;
 
-  // The resolved color scheme to use based on the supported color schemes, the
+  // The resolved color scheme to use based on the meta color-scheme, the
   // preferred color scheme, and the ForceDarkMode setting.
   ColorScheme color_scheme_ = ColorScheme::kLight;
 
diff --git a/third_party/blink/renderer/core/css/style_traversal_root.cc b/third_party/blink/renderer/core/css/style_traversal_root.cc
index 0347c82..901b140 100644
--- a/third_party/blink/renderer/core/css/style_traversal_root.cc
+++ b/third_party/blink/renderer/core/css/style_traversal_root.cc
@@ -23,19 +23,7 @@
     return;
   }
 
-  if (!root_node_) {
-    // When removing elements in a <form> subtree, we can reach this
-    // point without a root node, because we can synchronously mark
-    // nodes for recalc in a partially disconnected tree via
-    // HTMLFormElement::InvalidateDefaultButtonStyle. These DCHECKs
-    // are in place to prevent this unfortunate situation in other
-    // circumstances.
-#if DCHECK_IS_ON()
-    DCHECK(!IsConnectedToDocument(*dirty_node));
-    DCHECK(dirty_node->GetDocument().GetStyleEngine().InDomRemoval());
-#endif  // DCHECK_IS_ON()
-    return;
-  }
+  DCHECK(root_node_);
 #if DCHECK_IS_ON()
   DCHECK(Parent(*dirty_node));
   DCHECK(!IsDirty(*Parent(*dirty_node)));
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 07da90f..3ca0fa7 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -609,7 +609,7 @@
                    DocumentClassFlags document_classes)
     : ContainerNode(nullptr, kCreateDocument),
       TreeScope(*this),
-      ExecutionContext(V8PerIsolateData::MainThreadIsolate()),
+      ExecutionContext(V8PerIsolateData::MainThreadIsolate(), nullptr),
       evaluate_media_queries_on_style_recalc_(false),
       pending_sheet_layout_(kNoLayoutWithPendingSheets),
       frame_(initializer.GetFrame()),
@@ -639,7 +639,6 @@
       visually_ordered_(false),
       ready_state_(kComplete),
       parsing_state_(kFinishedParsing),
-      contains_validity_style_rules_(false),
       contains_plugins_(false),
       ignore_destructive_write_count_(0),
       throw_on_dynamic_markup_insertion_count_(0),
@@ -744,6 +743,9 @@
   if (frame_)
     frame_->Client()->DidSetFramePolicyHeaders(GetSandboxFlags(), {});
 
+  // TODO(tzik): Set up Agent for the current SecurityOrigin, and store it
+  // with SetAgent().
+
   InitDNSPrefetch();
 
   InstanceCounters::IncrementCounter(InstanceCounters::kDocumentCounter);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 145f8f73..2f75c1d 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1136,13 +1136,6 @@
   bool IsContextThread() const final;
   bool IsJSExecutionForbidden() const final { return false; }
 
-  bool ContainsValidityStyleRules() const {
-    return contains_validity_style_rules_;
-  }
-  void SetContainsValidityStyleRules() {
-    contains_validity_style_rules_ = true;
-  }
-
   void EnqueueResizeEvent();
   void EnqueueScrollEventForNode(Node*);
   void EnqueueScrollEndEventForNode(Node*);
@@ -1814,7 +1807,6 @@
 
   bool is_dns_prefetch_enabled_;
   bool have_explicitly_disabled_dns_prefetch_;
-  bool contains_validity_style_rules_;
   bool contains_plugins_;
 
   // https://html.spec.whatwg.org/C/dynamic-markup-insertion.html#ignore-destructive-writes-counter
diff --git a/third_party/blink/renderer/core/execution_context/BUILD.gn b/third_party/blink/renderer/core/execution_context/BUILD.gn
index 1edcdfd0..d588e2ca 100644
--- a/third_party/blink/renderer/core/execution_context/BUILD.gn
+++ b/third_party/blink/renderer/core/execution_context/BUILD.gn
@@ -6,6 +6,8 @@
 
 blink_core_sources("execution_context") {
   sources = [
+    "agent.cc",
+    "agent.h",
     "context_lifecycle_notifier.cc",
     "context_lifecycle_notifier.h",
     "context_lifecycle_observer.cc",
@@ -18,5 +20,9 @@
     "remote_security_context.h",
     "security_context.cc",
     "security_context.h",
+    "window_agent.cc",
+    "window_agent.h",
+    "window_agent_factory.cc",
+    "window_agent_factory.h",
   ]
 }
diff --git a/third_party/blink/renderer/core/execution_context/agent.cc b/third_party/blink/renderer/core/execution_context/agent.cc
new file mode 100644
index 0000000..7d2c9e0
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/agent.cc
@@ -0,0 +1,19 @@
+// 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/core/execution_context/agent.h"
+
+#include "third_party/blink/renderer/core/dom/mutation_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
+
+namespace blink {
+
+Agent::Agent(v8::Isolate* isolate)
+    : event_loop_(base::AdoptRef(new scheduler::EventLoop(isolate))) {}
+
+Agent::~Agent() = default;
+
+void Agent::Trace(Visitor* visitor) {}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/agent.h b/third_party/blink/renderer/core/execution_context/agent.h
new file mode 100644
index 0000000..2f3fff0
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/agent.h
@@ -0,0 +1,55 @@
+// 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_CORE_EXECUTION_CONTEXT_AGENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_AGENT_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace v8 {
+class Isolate;
+}
+
+namespace blink {
+
+namespace scheduler {
+class EventLoop;
+}
+
+// Corresponding spec concept is:
+// https://html.spec.whatwg.org/C#integration-with-the-javascript-agent-formalism
+//
+// Agent is a group of browsing contexts that can access each other
+// synchronously. E.g. same-site iframes share the same agent, Workers and
+// Worklets have their own agent.
+// While an WindowAgentFactory is shared across a group of reachable frames,
+// Agent is shared across a group of reachable and same-site frames.
+class Agent : public GarbageCollectedFinalized<Agent> {
+ public:
+  static Agent* CreateForWorkerOrWorklet(v8::Isolate* isolate) {
+    return MakeGarbageCollected<Agent>(isolate);
+  }
+
+  // Do not create the instance directly.
+  // Use Agent::CreateForWorkerOrWorklet() or
+  // WindowAgentFactory::GetAgentForOrigin().
+  explicit Agent(v8::Isolate* isolate);
+  virtual ~Agent();
+
+  const scoped_refptr<scheduler::EventLoop>& event_loop() const {
+    return event_loop_;
+  }
+
+  virtual void Trace(blink::Visitor*);
+
+ private:
+  scoped_refptr<scheduler::EventLoop> event_loop_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_AGENT_H_
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index 923eb54..dab87bc 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/events/error_event.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_state_observer.h"
 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
 #include "third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.h"
@@ -49,12 +50,13 @@
 
 namespace blink {
 
-ExecutionContext::ExecutionContext(v8::Isolate* isolate)
+ExecutionContext::ExecutionContext(v8::Isolate* isolate, Agent* agent)
     : isolate_(isolate),
       circular_sequential_id_(0),
       in_dispatch_error_event_(false),
       is_context_destroyed_(false),
       csp_delegate_(MakeGarbageCollected<ExecutionContextCSPDelegate>(*this)),
+      agent_(agent),
       window_interaction_tokens_(0),
       referrer_policy_(network::mojom::ReferrerPolicy::kDefault),
       invalidator_(std::make_unique<InterfaceInvalidator>()) {}
@@ -261,6 +263,7 @@
   visitor->Trace(public_url_manager_);
   visitor->Trace(pending_exceptions_);
   visitor->Trace(csp_delegate_);
+  visitor->Trace(agent_);
   ContextLifecycleNotifier::Trace(visitor);
   ConsoleLogger::Trace(visitor);
   Supplementable<ExecutionContext>::Trace(visitor);
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index b7c60cf..5e8eed4 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -68,6 +68,7 @@
 }  // namespace blink
 }  // namespace mojom
 
+class Agent;
 class ConsoleMessage;
 class ContentSecurityPolicy;
 class ContentSecurityPolicyDelegate;
@@ -293,13 +294,19 @@
   InterfaceInvalidator* GetInterfaceInvalidator() { return invalidator_.get(); }
 
   v8::Isolate* GetIsolate() const { return isolate_; }
+  Agent* GetAgent() const { return agent_; }
 
   virtual TrustedTypePolicyFactory* GetTrustedTypes() const { return nullptr; }
 
  protected:
-  explicit ExecutionContext(v8::Isolate* isolate);
+  explicit ExecutionContext(v8::Isolate* isolate, Agent* agent);
   ~ExecutionContext() override;
 
+  void SetAgent(Agent* agent) {
+    DCHECK(agent);
+    agent_ = agent;
+  }
+
  private:
   v8::Isolate* const isolate_;
 
@@ -318,6 +325,8 @@
 
   const Member<ContentSecurityPolicyDelegate> csp_delegate_;
 
+  Member<Agent> agent_;
+
   // Counter that keeps track of how many window interaction calls are allowed
   // for this ExecutionContext. Callers are expected to call
   // |allowWindowInteraction()| and |consumeWindowInteraction()| in order to
diff --git a/third_party/blink/renderer/core/execution_context/window_agent.cc b/third_party/blink/renderer/core/execution_context/window_agent.cc
new file mode 100644
index 0000000..2d9ac181
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/window_agent.cc
@@ -0,0 +1,20 @@
+// 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/core/execution_context/window_agent.h"
+#include "third_party/blink/renderer/core/dom/mutation_observer.h"
+#include "third_party/blink/renderer/core/html/custom/custom_element_reaction_stack.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
+
+namespace blink {
+
+WindowAgent::WindowAgent(v8::Isolate* isolate) : Agent(isolate) {}
+
+WindowAgent::~WindowAgent() = default;
+
+void WindowAgent::Trace(Visitor* visitor) {
+  Agent::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/window_agent.h b/third_party/blink/renderer/core/execution_context/window_agent.h
new file mode 100644
index 0000000..3775250d
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/window_agent.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_H_
+
+#include "third_party/blink/renderer/core/execution_context/agent.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+
+namespace v8 {
+class Isolate;
+}
+
+namespace blink {
+
+// This corresponds to similar-origin window agent, that is shared by a group
+// of Documents that are mutually reachable and have the same-site origins.
+// https://html.spec.whatwg.org/C#similar-origin-window-agent
+//
+// The instance holds per-agent data in addition to the base Agent, that is also
+// shared by associated Documents.
+class WindowAgent final : public Agent {
+ public:
+  // Do not create the instance directly. Use
+  // WindowAgentFactory::GetAgentForOrigin() instead.
+  explicit WindowAgent(v8::Isolate* isolate);
+  ~WindowAgent() override;
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  // TODO(tzik): Move per-agent data here with the correct granularity.
+  // E.g. ActiveMutationObservers and CustomElementReactionStack should move
+  // here.
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_H_
diff --git a/third_party/blink/renderer/core/execution_context/window_agent_factory.cc b/third_party/blink/renderer/core/execution_context/window_agent_factory.cc
new file mode 100644
index 0000000..169e36b
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/window_agent_factory.cc
@@ -0,0 +1,93 @@
+// 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/core/execution_context/window_agent_factory.h"
+#include "third_party/blink/renderer/core/execution_context/window_agent.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin_hash.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+WindowAgentFactory::WindowAgentFactory() = default;
+
+WindowAgent* WindowAgentFactory::GetAgentForOrigin(
+    bool has_potential_universal_access_privilege,
+    v8::Isolate* isolate,
+    const SecurityOrigin* origin) {
+  if (has_potential_universal_access_privilege) {
+    if (!universal_access_agent_) {
+      universal_access_agent_ = MakeGarbageCollected<WindowAgent>(isolate);
+    }
+    return universal_access_agent_;
+  }
+
+  // For `file:` scheme origins.
+  if (origin->IsLocal()) {
+    if (!file_url_agent_)
+      file_url_agent_ = MakeGarbageCollected<WindowAgent>(isolate);
+    return file_url_agent_;
+  }
+
+  // For opaque origins.
+  if (origin->IsOpaque()) {
+    auto inserted = opaque_origin_agents_.insert(origin, nullptr);
+    if (inserted.is_new_entry)
+      inserted.stored_value->value = MakeGarbageCollected<WindowAgent>(isolate);
+    return inserted.stored_value->value;
+  }
+
+  // For tuple origins.
+  String registrable_domain = origin->RegistrableDomain();
+  if (registrable_domain.IsNull())
+    registrable_domain = origin->Host();
+
+  SchemeAndRegistrableDomain key(origin->Protocol(), registrable_domain);
+  auto inserted = tuple_origin_agents_.insert(key, nullptr);
+  if (inserted.is_new_entry)
+    inserted.stored_value->value = MakeGarbageCollected<WindowAgent>(isolate);
+  return inserted.stored_value->value;
+}
+
+void WindowAgentFactory::Trace(blink::Visitor* visitor) {
+  visitor->Trace(universal_access_agent_);
+  visitor->Trace(file_url_agent_);
+  visitor->Trace(opaque_origin_agents_);
+  visitor->Trace(tuple_origin_agents_);
+}
+
+// static
+unsigned WindowAgentFactory::SchemeAndRegistrableDomainHash::GetHash(
+    const SchemeAndRegistrableDomain& value) {
+  return WTF::HashInts(StringHash::GetHash(value.scheme),
+                       StringHash::GetHash(value.registrable_domain));
+}
+
+// static
+bool WindowAgentFactory::SchemeAndRegistrableDomainHash::Equal(
+    const SchemeAndRegistrableDomain& x,
+    const SchemeAndRegistrableDomain& y) {
+  return x.scheme == y.scheme && x.registrable_domain == y.registrable_domain;
+}
+
+// static
+bool WindowAgentFactory::SchemeAndRegistrableDomainTraits::IsEmptyValue(
+    const SchemeAndRegistrableDomain& value) {
+  return HashTraits<String>::IsEmptyValue(value.scheme);
+}
+
+// static
+bool WindowAgentFactory::SchemeAndRegistrableDomainTraits::IsDeletedValue(
+    const SchemeAndRegistrableDomain& value) {
+  return HashTraits<String>::IsDeletedValue(value.scheme);
+}
+
+// static
+void WindowAgentFactory::SchemeAndRegistrableDomainTraits::
+    ConstructDeletedValue(SchemeAndRegistrableDomain& slot, bool zero_value) {
+  HashTraits<String>::ConstructDeletedValue(slot.scheme, zero_value);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/window_agent_factory.h b/third_party/blink/renderer/core/execution_context/window_agent_factory.h
new file mode 100644
index 0000000..092b1d8
--- /dev/null
+++ b/third_party/blink/renderer/core/execution_context/window_agent_factory.h
@@ -0,0 +1,111 @@
+// 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_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_FACTORY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_FACTORY_H_
+
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace v8 {
+class Isolate;
+}
+
+namespace blink {
+
+class SecurityOrigin;
+class WindowAgent;
+
+// This is a helper class to assign WindowAgent to Document.
+// The instance should be created for each group of Documents that are mutually
+// reachable via `window.opener`, `window.frames` or others. These Documents
+// may have different origins.
+//
+// The instance is intended to have the same granularity to the group of
+// browsing context, that contains auxiliary browsing contexts.
+// https://html.spec.whatwg.org/C#tlbc-group
+// https://html.spec.whatwg.org/C#auxiliary-browsing-context
+class WindowAgentFactory final : public GarbageCollected<WindowAgentFactory> {
+ public:
+  WindowAgentFactory();
+
+  // Returns an instance of WindowAgent for |origin|.
+  // This returns the same instance for origin A and origin B if either:
+  //  - |has_potential_universal_access_privilege| is true,
+  //  - both A and B have `file:` scheme,
+  //  - or, they have the same scheme and the same registrable origin.
+  //
+  // Set |has_potential_universal_access_privilege| if an agent may be able to
+  // access all other agents synchronously.
+  // I.e. pass true to if either:
+  //   * --disable-web-security is set,
+  //   * --run-web-tests is set,
+  //   * or, the Blink instance is running for Android WebView.
+  WindowAgent* GetAgentForOrigin(bool has_potential_universal_access_privilege,
+                                 v8::Isolate* isolate,
+                                 const SecurityOrigin* origin);
+
+  void Trace(blink::Visitor*);
+
+ private:
+  struct SchemeAndRegistrableDomain {
+    String scheme;
+    String registrable_domain;
+
+    SchemeAndRegistrableDomain(const String& scheme,
+                               const String& registrable_domain)
+        : scheme(scheme), registrable_domain(registrable_domain) {}
+  };
+
+  struct SchemeAndRegistrableDomainHash {
+    STATIC_ONLY(SchemeAndRegistrableDomainHash);
+    static const bool safe_to_compare_to_empty_or_deleted = false;
+
+    static unsigned GetHash(const SchemeAndRegistrableDomain&);
+    static bool Equal(const SchemeAndRegistrableDomain&,
+                      const SchemeAndRegistrableDomain&);
+  };
+
+  struct SchemeAndRegistrableDomainTraits
+      : SimpleClassHashTraits<SchemeAndRegistrableDomain> {
+    STATIC_ONLY(SchemeAndRegistrableDomainTraits);
+    static const bool kHasIsEmptyValueFunction = true;
+
+    static bool IsEmptyValue(const SchemeAndRegistrableDomain&);
+    static bool IsDeletedValue(const SchemeAndRegistrableDomain& value);
+    static void ConstructDeletedValue(SchemeAndRegistrableDomain& slot,
+                                      bool zero_value);
+  };
+
+  // Use a shared instance of Agent for all frames if a frame may have the
+  // universal access privilege.
+  WeakMember<WindowAgent> universal_access_agent_;
+
+  // `file:` scheme URLs are hard for tracking the equality. Use a shared
+  // Agent for them.
+  WeakMember<WindowAgent> file_url_agent_;
+
+  // Use the SecurityOrigin itself as the key for opaque origins.
+  HeapHashMap<scoped_refptr<const SecurityOrigin>,
+              WeakMember<WindowAgent>,
+              SecurityOriginHash>
+      opaque_origin_agents_;
+
+  // Use registerable domain as the key for general tuple origins.
+  HeapHashMap<SchemeAndRegistrableDomain,
+              WeakMember<WindowAgent>,
+              SchemeAndRegistrableDomainHash,
+              SchemeAndRegistrableDomainTraits>
+      tuple_origin_agents_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_FACTORY_H_
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index d00aa05..f944aca 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -8751,28 +8751,40 @@
                                     &client);
   EXPECT_TRUE(client.DidNotify());
   WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
-  EXPECT_EQ(0xff0000ff, frame->GetDocument().ThemeColor());
+  EXPECT_EQ(Color(0, 0, 255), frame->GetDocument().ThemeColor());
   // Change color by rgb.
   client.Reset();
   frame->ExecuteScript(
       WebScriptSource("document.getElementById('tc1').setAttribute('content', "
                       "'rgb(0, 0, 0)');"));
   EXPECT_TRUE(client.DidNotify());
-  EXPECT_EQ(0xff000000, frame->GetDocument().ThemeColor());
+  EXPECT_EQ(Color::kBlack, frame->GetDocument().ThemeColor());
   // Change color by hsl.
   client.Reset();
   frame->ExecuteScript(
       WebScriptSource("document.getElementById('tc1').setAttribute('content', "
                       "'hsl(240,100%, 50%)');"));
   EXPECT_TRUE(client.DidNotify());
-  EXPECT_EQ(0xff0000ff, frame->GetDocument().ThemeColor());
+  EXPECT_EQ(Color(0, 0, 255), frame->GetDocument().ThemeColor());
   // Change of second theme-color meta tag will not change frame's theme
   // color.
   client.Reset();
   frame->ExecuteScript(WebScriptSource(
       "document.getElementById('tc2').setAttribute('content', '#00FF00');"));
   EXPECT_TRUE(client.DidNotify());
-  EXPECT_EQ(0xff0000ff, frame->GetDocument().ThemeColor());
+  EXPECT_EQ(Color(0, 0, 255), frame->GetDocument().ThemeColor());
+  // Remove the first theme-color meta tag to apply the second.
+  client.Reset();
+  frame->ExecuteScript(
+      WebScriptSource("document.getElementById('tc1').remove();"));
+  EXPECT_TRUE(client.DidNotify());
+  EXPECT_EQ(Color(0, 255, 0), frame->GetDocument().ThemeColor());
+  // Remove the name attribute of the remaining meta.
+  client.Reset();
+  frame->ExecuteScript(WebScriptSource(
+      "document.getElementById('tc2').removeAttribute('name');"));
+  EXPECT_TRUE(client.DidNotify());
+  EXPECT_EQ(base::nullopt, frame->GetDocument().ThemeColor());
 }
 
 // Make sure that an embedder-triggered detach with a remote frame parent
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index beb848a..33883eb 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -122,17 +122,15 @@
 // Public methods --------------------------------------------------------------
 
 void WebPluginContainerImpl::AttachToLayout() {
-  DCHECK(!is_attached_);
-  is_attached_ = true;
+  DCHECK(!IsAttached());
+  SetAttached(true);
   SetParentVisible(true);
-  // No need to track visibility for plugins.
-  element_->StopVisibilityObserver();
 }
 
 void WebPluginContainerImpl::DetachFromLayout() {
-  DCHECK(is_attached_);
+  DCHECK(IsAttached());
   SetParentVisible(false);
-  is_attached_ = false;
+  SetAttached(false);
 }
 
 void WebPluginContainerImpl::UpdateAllLifecyclePhases() {
@@ -143,30 +141,6 @@
       WebWidget::LifecycleUpdateReason::kOther);
 }
 
-void WebPluginContainerImpl::SetFrameRect(const IntRect& frame_rect) {
-  frame_rect_ = frame_rect;
-  ReportGeometry();
-}
-
-IntRect WebPluginContainerImpl::FrameRect() const {
-  IntPoint location(frame_rect_.Location());
-
-  // As an optimization, we don't include the root layer's scroll offset in the
-  // frame rect.  As a result, we don't need to recalculate the frame rect every
-  // time the root layer scrolls, but we need to add it in here.
-  LayoutEmbeddedContent* owner = element_->GetLayoutEmbeddedContent();
-  if (owner) {
-    LayoutView* owner_layout_view = owner->View();
-    DCHECK(owner_layout_view);
-    if (owner_layout_view->HasOverflowClip()) {
-      IntSize scroll_offset(owner_layout_view->ScrolledContentOffset());
-      location.SaturatedMove(-scroll_offset.Width(), -scroll_offset.Height());
-    }
-  }
-
-  return IntRect(location, frame_rect_.Size());
-}
-
 void WebPluginContainerImpl::Paint(GraphicsContext& context,
                                    const GlobalPaintFlags,
                                    const CullRect& cull_rect,
@@ -176,50 +150,50 @@
     return;
 
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && layer_) {
-    layer_->SetBounds(gfx::Size(frame_rect_.Size()));
+    layer_->SetBounds(gfx::Size(Size()));
     layer_->SetIsDrawable(true);
     layer_->SetHitTestable(true);
     // When compositing is after paint, composited plugins should have their
     // layers inserted rather than invoking WebPlugin::paint.
     RecordForeignLayer(context, DisplayItem::kForeignLayerPlugin, layer_,
-                       FloatPoint(frame_rect_.Location()));
+                       FloatPoint(DocumentLocation()));
     return;
   }
 
   if (DrawingRecorder::UseCachedDrawingIfPossible(
-          context, *element_->GetLayoutObject(), DisplayItem::kWebPlugin))
+          context, *GetLayoutEmbeddedContent(), DisplayItem::kWebPlugin))
     return;
 
-  DrawingRecorder recorder(context, *element_->GetLayoutObject(),
+  DrawingRecorder recorder(context, *GetLayoutEmbeddedContent(),
                            DisplayItem::kWebPlugin);
   context.Save();
 
   // The plugin is positioned in the root frame's coordinates, so it needs to
   // be painted in them too.
-  FloatPoint origin(ParentFrameView().ConvertToRootFrame(IntPoint()));
+  FloatPoint origin(ParentFrameView()->ConvertToRootFrame(IntPoint()));
   origin.Move(-paint_offset);
   context.Translate(-origin.X(), -origin.Y());
 
   cc::PaintCanvas* canvas = context.Canvas();
 
-  IntRect window_rect = ParentFrameView().ConvertToRootFrame(cull_rect.Rect());
+  IntRect window_rect = ParentFrameView()->ConvertToRootFrame(cull_rect.Rect());
   web_plugin_->Paint(canvas, window_rect);
 
   context.Restore();
 }
 
 void WebPluginContainerImpl::UpdateGeometry() {
-  if (LayoutEmbeddedContent* layout = element_->GetLayoutEmbeddedContent())
+  if (LayoutEmbeddedContent* layout = GetLayoutEmbeddedContent())
     layout->UpdateGeometry(*this);
 }
 
 void WebPluginContainerImpl::InvalidateRect(const IntRect& rect) {
   // InvalidateRect can be called from Dispose when this plugin is no longer
   // attached.  In this case, we return immediately.
-  if (!is_attached_)
+  if (!IsAttached())
     return;
 
-  LayoutBox* layout_object = ToLayoutBox(element_->GetLayoutObject());
+  LayoutBox* layout_object = GetLayoutEmbeddedContent();
   if (!layout_object)
     return;
 
@@ -242,12 +216,12 @@
 }
 
 void WebPluginContainerImpl::Show() {
-  self_visible_ = true;
+  SetSelfVisible(true);
   web_plugin_->UpdateVisibility(true);
 }
 
 void WebPluginContainerImpl::Hide() {
-  self_visible_ = false;
+  SetSelfVisible(false);
   web_plugin_->UpdateVisibility(false);
 }
 
@@ -276,32 +250,19 @@
     element_->Node::DefaultEventHandler(event);
 }
 
-void WebPluginContainerImpl::FrameRectsChanged() {
-  ReportGeometry();
-}
-
 void WebPluginContainerImpl::EventListenersRemoved() {
   // We're no longer registered to receive touch events, so don't try to remove
   // the touch event handlers in our destructor.
   touch_event_request_type_ = kTouchEventRequestTypeNone;
 }
 
-void WebPluginContainerImpl::SetParentVisible(bool parent_visible) {
+void WebPluginContainerImpl::ParentVisibleChanged() {
   // We override this function to make sure that geometry updates are sent
   // over to the plugin. For e.g. when a plugin is instantiated it does not
-  // have a valid parent. As a result the first geometry update from webkit
-  // is ignored. This function is called when the plugin eventually gets a
-  // parent.
-
-  if (parent_visible_ == parent_visible)
-    return;  // No change.
-
-  parent_visible_ = parent_visible;
-  if (!self_visible_)
-    return;  // This widget has explicitely been marked as not visible.
-
-  if (web_plugin_)
-    web_plugin_->UpdateVisibility(parent_visible_ && self_visible_);
+  // have a valid parent. As a result the first geometry update is ignored. This
+  // function is called when the plugin eventually gets a parent.
+  if (web_plugin_ && IsSelfVisible())
+    web_plugin_->UpdateVisibility(IsVisible());
 }
 
 void WebPluginContainerImpl::SetPlugin(WebPlugin* plugin) {
@@ -410,11 +371,11 @@
 
 void WebPluginContainerImpl::PrintPage(int page_number, GraphicsContext& gc) {
   if (DrawingRecorder::UseCachedDrawingIfPossible(
-          gc, *element_->GetLayoutObject(), DisplayItem::kWebPlugin))
+          gc, *GetLayoutEmbeddedContent(), DisplayItem::kWebPlugin))
     return;
 
   // TODO(wkorman): Do we still need print_rect at all?
-  DrawingRecorder recorder(gc, *element_->GetLayoutObject(),
+  DrawingRecorder recorder(gc, *GetLayoutEmbeddedContent(),
                            DisplayItem::kWebPlugin);
   gc.Save();
 
@@ -490,7 +451,7 @@
 }
 
 void WebPluginContainerImpl::Invalidate() {
-  InvalidateRect(IntRect(0, 0, frame_rect_.Width(), frame_rect_.Height()));
+  InvalidateRect(IntRect(0, 0, Size().Width(), Size().Height()));
 }
 
 void WebPluginContainerImpl::InvalidateRect(const WebRect& rect) {
@@ -509,13 +470,13 @@
 void WebPluginContainerImpl::ReportGeometry() {
   // Ignore when SetFrameRect/ReportGeometry is called from
   // UpdateOnEmbeddedContentViewChange before plugin is attached.
-  if (!is_attached_)
+  if (!IsAttached())
     return;
 
   IntRect window_rect, clip_rect, unobscured_rect;
   CalculateGeometry(window_rect, clip_rect, unobscured_rect);
   web_plugin_->UpdateGeometry(window_rect, clip_rect, unobscured_rect,
-                              self_visible_);
+                              IsSelfVisible());
 }
 
 v8::Local<v8::Object> WebPluginContainerImpl::V8ObjectForElement() {
@@ -598,14 +559,14 @@
   // we might be being disposed because the frame has already be deleted and
   // then something else dropped the
   // last reference to the this object.
-  if (!is_attached_ || !element_)
+  if (!IsAttached() || !element_)
     return false;
 
   LocalFrame* frame = element_->GetDocument().GetFrame();
   if (!frame)
     return false;
 
-  IntPoint frame_location = FrameRect().Location();
+  IntPoint frame_location = Location();
   HitTestLocation location(LayoutRect(frame_location.X() + rect.x,
                                       frame_location.Y() + rect.y, rect.width,
                                       rect.height));
@@ -676,7 +637,7 @@
             page->GetScrollingCoordinator()) {
       // Only call scrolling_coordinator if attached.  SetWantsWheelEvents can
       // be called from Plugin Initialization when it is not yet attached.
-      if (is_attached_) {
+      if (IsAttached()) {
         LocalFrameView* frame_view = element_->GetDocument().GetFrame()->View();
         scrolling_coordinator->NotifyGeometryChanged(frame_view);
       }
@@ -687,24 +648,24 @@
 WebPoint WebPluginContainerImpl::RootFrameToLocalPoint(
     const WebPoint& point_in_root_frame) {
   WebPoint point_in_content =
-      ParentFrameView().ConvertFromRootFrame(point_in_root_frame);
-  return RoundedIntPoint(element_->GetLayoutObject()->AbsoluteToLocal(
+      ParentFrameView()->ConvertFromRootFrame(point_in_root_frame);
+  return RoundedIntPoint(GetLayoutEmbeddedContent()->AbsoluteToLocal(
       FloatPoint(point_in_content), kUseTransforms));
 }
 
 WebPoint WebPluginContainerImpl::LocalToRootFramePoint(
     const WebPoint& point_in_local) {
   IntPoint absolute_point =
-      RoundedIntPoint(element_->GetLayoutObject()->LocalToAbsolute(
+      RoundedIntPoint(GetLayoutEmbeddedContent()->LocalToAbsolute(
           FloatPoint(point_in_local), kUseTransforms));
-  return ParentFrameView().ConvertToRootFrame(absolute_point);
+  return ParentFrameView()->ConvertToRootFrame(absolute_point);
 }
 
 void WebPluginContainerImpl::DidReceiveResponse(
     const ResourceResponse& response) {
   // Make sure that the plugin receives window geometry before data, or else
   // plugins misbehave.
-  FrameRectsChanged();
+  ReportGeometry();
 
   WrappedResourceResponse url_response(response);
   web_plugin_->DidReceiveResponse(url_response);
@@ -770,29 +731,32 @@
 
 WebPluginContainerImpl::WebPluginContainerImpl(HTMLPlugInElement& element,
                                                WebPlugin* web_plugin)
-    : ContextClient(element.GetDocument().GetFrame()),
+    : EmbeddedContentView(IntRect()),
+      ContextClient(element.GetDocument().GetFrame()),
       element_(element),
       web_plugin_(web_plugin),
       layer_(nullptr),
       touch_event_request_type_(kTouchEventRequestTypeNone),
       prevent_contents_opaque_changes_(false),
-      wants_wheel_events_(false),
-      self_visible_(false),
-      parent_visible_(false),
-      is_attached_(false) {}
+      wants_wheel_events_(false) {}
 
 WebPluginContainerImpl::~WebPluginContainerImpl() {
   // The plugin container must have been disposed of by now.
   DCHECK(!web_plugin_);
 }
 
-LocalFrameView& WebPluginContainerImpl::ParentFrameView() const {
-  DCHECK(is_attached_);
-  return *element_->GetDocument().GetFrame()->View();
+LocalFrameView* WebPluginContainerImpl::ParentFrameView() const {
+  DCHECK(IsAttached());
+  return element_->GetDocument().GetFrame()->View();
+}
+
+LayoutEmbeddedContent* WebPluginContainerImpl::GetLayoutEmbeddedContent()
+    const {
+  return element_->GetLayoutEmbeddedContent();
 }
 
 void WebPluginContainerImpl::Dispose() {
-  is_attached_ = false;
+  SetAttached(false);
 
   RequestTouchEventType(kTouchEventRequestTypeNone);
   SetWantsWheelEvents(false);
@@ -815,6 +779,15 @@
   }
 }
 
+void WebPluginContainerImpl::SetFrameRect(const IntRect& rect) {
+  IntRect old_rect(FrameRect());
+  EmbeddedContentView::SetFrameRect(rect);
+  // We need to report every time SetFrameRect is called, even if there is no
+  // change (if there is a change, FrameRectsChanged will do the reporting).
+  if (old_rect == FrameRect())
+    PropagateFrameRects();
+}
+
 void WebPluginContainerImpl::Trace(blink::Visitor* visitor) {
   visitor->Trace(element_);
   ContextClient::Trace(visitor);
@@ -823,11 +796,11 @@
 void WebPluginContainerImpl::HandleMouseEvent(MouseEvent& event) {
   // We cache the parent LocalFrameView here as the plugin widget could be
   // deleted in the call to HandleEvent. See http://b/issue?id=1362948
-  LocalFrameView& parent = ParentFrameView();
+  LocalFrameView* parent = ParentFrameView();
 
   // TODO(dtapuska): Move WebMouseEventBuilder into the anonymous namespace
   // in this class.
-  WebMouseEventBuilder transformed_event(&parent, element_->GetLayoutObject(),
+  WebMouseEventBuilder transformed_event(parent, GetLayoutEmbeddedContent(),
                                          event);
   if (transformed_event.GetType() == WebInputEvent::kUndefined)
     return;
@@ -844,11 +817,11 @@
   // A windowless plugin can change the cursor in response to a mouse move
   // event.  We need to reflect the changed cursor in the frame view as the
   // mouse is moved in the boundaries of the windowless plugin.
-  Page* page = parent.GetFrame().GetPage();
+  Page* page = parent->GetFrame().GetPage();
   if (!page)
     return;
   page->GetChromeClient().SetCursorForPlugin(
-      cursor_info, &parent.GetFrame().LocalFrameRoot());
+      cursor_info, &parent->GetFrame().LocalFrameRoot());
 }
 
 void WebPluginContainerImpl::HandleDragEvent(MouseEvent& event) {
@@ -872,7 +845,7 @@
   WebDragOperationsMask drag_operation_mask =
       static_cast<WebDragOperationsMask>(data_transfer->SourceOperation());
   WebFloatPoint drag_screen_location(event.screenX(), event.screenY());
-  IntPoint location(FrameRect().Location());
+  IntPoint location(Location());
   WebFloatPoint drag_location(event.AbsoluteLocation().X() - location.X(),
                               event.AbsoluteLocation().Y() - location.Y());
 
@@ -885,9 +858,10 @@
   WebFloatPoint absolute_location = event.NativeEvent().PositionInRootFrame();
 
   // Translate the root frame position to content coordinates.
-  absolute_location = ParentFrameView().ConvertFromRootFrame(absolute_location);
+  absolute_location =
+      ParentFrameView()->ConvertFromRootFrame(absolute_location);
 
-  FloatPoint local_point = element_->GetLayoutObject()->AbsoluteToLocal(
+  FloatPoint local_point = GetLayoutEmbeddedContent()->AbsoluteToLocal(
       absolute_location, kUseTransforms);
   WebMouseWheelEvent translated_event = event.NativeEvent().FlattenTransform();
   translated_event.SetPositionInWidget(local_point.X(), local_point.Y());
@@ -974,15 +948,15 @@
   const WebTouchEvent* touch_event = static_cast<const WebTouchEvent*>(&event);
   WebTouchEvent transformed_event = touch_event->FlattenTransform();
 
-  LocalFrameView& parent = ParentFrameView();
+  LocalFrameView* parent = ParentFrameView();
   for (unsigned i = 0; i < transformed_event.touches_length; ++i) {
     WebFloatPoint absolute_location =
         transformed_event.touches[i].PositionInWidget();
 
     // Translate the root frame position to content coordinates.
-    absolute_location = parent.ConvertFromRootFrame(absolute_location);
+    absolute_location = parent->ConvertFromRootFrame(absolute_location);
 
-    FloatPoint local_point = element_->GetLayoutObject()->AbsoluteToLocal(
+    FloatPoint local_point = GetLayoutEmbeddedContent()->AbsoluteToLocal(
         absolute_location, kUseTransforms);
     transformed_event.touches[i].SetPositionInWidget(local_point);
   }
@@ -1044,7 +1018,7 @@
   WebGestureEvent translated_event = event.NativeEvent();
   WebFloatPoint absolute_root_frame_location =
       event.NativeEvent().PositionInRootFrame();
-  FloatPoint local_point = element_->GetLayoutObject()->AbsoluteToLocal(
+  FloatPoint local_point = GetLayoutEmbeddedContent()->AbsoluteToLocal(
       absolute_root_frame_location, kUseTransforms);
   translated_event.FlattenTransform();
   translated_event.SetPositionInWidget(local_point);
@@ -1061,8 +1035,8 @@
 }
 
 void WebPluginContainerImpl::SynthesizeMouseEventIfPossible(TouchEvent& event) {
-  WebMouseEventBuilder web_event(&ParentFrameView(),
-                                 element_->GetLayoutObject(), event);
+  WebMouseEventBuilder web_event(ParentFrameView(), GetLayoutEmbeddedContent(),
+                                 event);
   if (web_event.GetType() == WebInputEvent::kUndefined)
     return;
 
@@ -1075,7 +1049,7 @@
 
 void WebPluginContainerImpl::FocusPlugin() {
   LocalFrame* frame = element_->GetDocument().GetFrame();
-  DCHECK(is_attached_ && frame && frame->GetPage());
+  DCHECK(IsAttached() && frame && frame->GetPage());
   frame->GetPage()->GetFocusController().SetFocusedElement(element_, frame);
 }
 
@@ -1146,7 +1120,7 @@
   // GetDocument().LayoutView() can be null when we receive messages from the
   // plugins while we are destroying a frame.
   // TODO: Can we just check element_->GetDocument().IsActive() ?
-  if (element_->GetLayoutObject()->GetDocument().GetLayoutView()) {
+  if (GetLayoutEmbeddedContent()->GetDocument().GetLayoutView()) {
     // Take our element and get the clip rect from the enclosing layer and
     // frame view.
     ComputeClipRectsForPlugin(element_, window_rect, clip_rect,
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
index df3a710..db04705 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
@@ -83,13 +83,10 @@
 
   // EmbeddedContentView methods
   bool IsPluginView() const override { return true; }
+  LocalFrameView* ParentFrameView() const override;
+  LayoutEmbeddedContent* GetLayoutEmbeddedContent() const override;
   void AttachToLayout() override;
   void DetachFromLayout() override;
-  bool IsAttached() const override { return is_attached_; }
-  void SetParentVisible(bool) override;
-  void FrameRectsChanged() override;
-  void SetFrameRect(const IntRect&) override;
-  IntRect FrameRect() const override;
   // |paint_offset| is used to to paint the contents at the correct location.
   // It should be issued as a transform operation before painting the contents.
   void Paint(GraphicsContext&,
@@ -194,9 +191,13 @@
   // method. Here we call Dispose() which does the correct virtual dispatch.
   void PreFinalize() { Dispose(); }
   void Dispose() override;
+  void SetFrameRect(const IntRect&) override;
+  void PropagateFrameRects() override { ReportGeometry(); }
+
+ protected:
+  void ParentVisibleChanged() override;
 
  private:
-  LocalFrameView& ParentFrameView() const;
   // Sets |windowRect| to the content rect of the plugin in screen space.
   // Sets |clippedAbsoluteRect| to the visible rect for the plugin, clipped to
   // the visible screen of the root frame, in local space of the plugin.
@@ -233,13 +234,9 @@
   Member<HTMLPlugInElement> element_;
   WebPlugin* web_plugin_;
   cc::Layer* layer_;
-  IntRect frame_rect_;
   TouchEventRequestType touch_event_request_type_;
   bool prevent_contents_opaque_changes_;
   bool wants_wheel_events_;
-  bool self_visible_;
-  bool parent_visible_;
-  bool is_attached_;
 };
 
 DEFINE_TYPE_CASTS(WebPluginContainerImpl,
diff --git a/third_party/blink/renderer/core/frame/BUILD.gn b/third_party/blink/renderer/core/frame/BUILD.gn
index da228b7..df6ab32 100644
--- a/third_party/blink/renderer/core/frame/BUILD.gn
+++ b/third_party/blink/renderer/core/frame/BUILD.gn
@@ -47,6 +47,7 @@
     "dom_visual_viewport.h",
     "dom_window.cc",
     "dom_window.h",
+    "embedded_content_view.cc",
     "embedded_content_view.h",
     "event_handler_registry.cc",
     "event_handler_registry.h",
@@ -67,6 +68,7 @@
     "frame_serializer.cc",
     "frame_serializer.h",
     "frame_types.h",
+    "frame_view.cc",
     "frame_view.h",
     "frame_view_auto_size_info.cc",
     "frame_view_auto_size_info.h",
diff --git a/third_party/blink/renderer/core/frame/embedded_content_view.cc b/third_party/blink/renderer/core/frame/embedded_content_view.cc
new file mode 100644
index 0000000..7c33975
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/embedded_content_view.cc
@@ -0,0 +1,53 @@
+// 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/core/frame/embedded_content_view.h"
+
+#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
+#include "third_party/blink/renderer/core/layout/layout_view.h"
+
+namespace blink {
+
+void EmbeddedContentView::SetFrameRect(const IntRect& unsaturated_frame_rect) {
+  IntRect frame_rect(SaturatedRect(unsaturated_frame_rect));
+  if (frame_rect == frame_rect_)
+    return;
+  IntRect old_rect = frame_rect_;
+  frame_rect_ = frame_rect;
+  FrameRectsChanged(old_rect);
+}
+
+IntPoint EmbeddedContentView::Location() const {
+  IntPoint location(frame_rect_.Location());
+
+  // As an optimization, we don't include the root layer's scroll offset in the
+  // frame rect.  As a result, we don't need to recalculate the frame rect every
+  // time the root layer scrolls, but we need to add it in here.
+  LayoutEmbeddedContent* owner = GetLayoutEmbeddedContent();
+  if (owner) {
+    LayoutView* owner_layout_view = owner->View();
+    DCHECK(owner_layout_view);
+    if (owner_layout_view->HasOverflowClip()) {
+      IntSize scroll_offset(owner_layout_view->ScrolledContentOffset());
+      location.SaturatedMove(-scroll_offset.Width(), -scroll_offset.Height());
+    }
+  }
+  return location;
+}
+
+void EmbeddedContentView::SetSelfVisible(bool visible) {
+  bool was_visible = self_visible_;
+  self_visible_ = visible;
+  if (was_visible != visible)
+    SelfVisibleChanged();
+}
+
+void EmbeddedContentView::SetParentVisible(bool visible) {
+  bool was_visible = parent_visible_;
+  parent_visible_ = visible;
+  if (was_visible != visible)
+    ParentVisibleChanged();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/embedded_content_view.h b/third_party/blink/renderer/core/frame/embedded_content_view.h
index bc3c1c2..ca0dac6 100644
--- a/third_party/blink/renderer/core/frame/embedded_content_view.h
+++ b/third_party/blink/renderer/core/frame/embedded_content_view.h
@@ -7,33 +7,31 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/paint/paint_phase.h"
-#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
 
 class CullRect;
+class LayoutEmbeddedContent;
+class LocalFrameView;
 class GraphicsContext;
-class IntRect;
-class IntSize;
 
 // EmbeddedContentView is a pure virtual class which is implemented by
 // LocalFrameView, RemoteFrameView, and WebPluginContainerImpl.
 class CORE_EXPORT EmbeddedContentView : public GarbageCollectedMixin {
  public:
+  EmbeddedContentView(const IntRect& frame_rect) : frame_rect_(frame_rect) {}
   virtual ~EmbeddedContentView() = default;
 
   virtual bool IsFrameView() const { return false; }
   virtual bool IsLocalFrameView() const { return false; }
   virtual bool IsPluginView() const { return false; }
 
+  virtual LocalFrameView* ParentFrameView() const = 0;
+  virtual LayoutEmbeddedContent* GetLayoutEmbeddedContent() const = 0;
   virtual void AttachToLayout() = 0;
   virtual void DetachFromLayout() = 0;
-  virtual bool IsAttached() const = 0;
-  virtual void SetParentVisible(bool) = 0;
-  virtual void SetFrameRect(const IntRect&) = 0;
-  virtual void FrameRectsChanged() = 0;
-  virtual IntRect FrameRect() const = 0;
   virtual void Paint(GraphicsContext&,
                      const GlobalPaintFlags,
                      const CullRect&,
@@ -45,6 +43,53 @@
   virtual void Show() = 0;
   virtual void Hide() = 0;
   virtual void Dispose() = 0;
+
+  virtual void SetFrameRect(const IntRect&);
+
+  // This method pushes information about our frame rect to consumers.
+  // Typically, it will be invoked by FrameRectsChanged; but it can also be
+  // called directly to push frame rect information without changing it.
+  virtual void PropagateFrameRects() = 0;
+
+  IntRect FrameRect() const { return IntRect(Location(), Size()); }
+  IntPoint Location() const;
+  int X() const { return Location().X(); }
+  int Y() const { return Location().Y(); }
+  int Width() const { return Size().Width(); }
+  int Height() const { return Size().Height(); }
+  IntSize Size() const { return frame_rect_.Size(); }
+  void Resize(int width, int height) { Resize(IntSize(width, height)); }
+  void Resize(const IntSize& size) {
+    SetFrameRect(IntRect(frame_rect_.Location(), size));
+  }
+  bool IsAttached() const { return is_attached_; }
+  // The visibility flags are set for iframes based on style properties of the
+  // HTMLFrameOwnerElement in the embedding document.
+  bool IsSelfVisible() const { return self_visible_; }
+  void SetSelfVisible(bool);
+  bool IsParentVisible() const { return parent_visible_; }
+  void SetParentVisible(bool);
+  bool IsVisible() const { return self_visible_ && parent_visible_; }
+
+ protected:
+  // Called when our frame rect changes (or the rect/scroll offset of an
+  // ancestor changes).
+  virtual void FrameRectsChanged(const IntRect&) { PropagateFrameRects(); }
+  virtual void SelfVisibleChanged() {}
+  virtual void ParentVisibleChanged() {}
+  void SetAttached(bool attached) { is_attached_ = attached; }
+  // Location() is in frame coordinates, DocumentLocation() is in document
+  // coordinates. For more information:
+  // http://www.chromium.org/developers/design-documents/blink-coordinate-spaces
+  IntPoint DocumentLocation() const { return frame_rect_.Location(); }
+
+ private:
+  // Note that frame_rect_ is actually in document coordinates, but the
+  // FrameRect() and Location() methods convert to frame coordinates.
+  IntRect frame_rect_;
+  bool self_visible_;
+  bool parent_visible_;
+  bool is_attached_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc
index f3a50ae..eb001b33 100644
--- a/third_party/blink/renderer/core/frame/frame.cc
+++ b/third_party/blink/renderer/core/frame/frame.cc
@@ -130,6 +130,13 @@
   return !Tree().Parent();
 }
 
+bool Frame::IsCrossOriginSubframe() const {
+  const SecurityOrigin* security_origin =
+      GetSecurityContext()->GetSecurityOrigin();
+  return !security_origin->CanAccess(
+      Tree().Top().GetSecurityContext()->GetSecurityOrigin());
+}
+
 HTMLFrameOwnerElement* Frame::DeprecatedLocalOwner() const {
   return DynamicTo<HTMLFrameOwnerElement>(owner_.Get());
 }
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index 6010b13..58fe978 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -104,6 +104,14 @@
   // reach out to site-isolation-dev@chromium.org.
   bool IsMainFrame() const;
 
+  // Note that the result of this function should not be cached: a frame is
+  // not necessarily detached when it is navigated, so the return value can
+  // change.
+  // In addition, this function will always return true for a detached frame.
+  // TODO(dcheng): Move this to LocalDOMWindow and figure out the right
+  // behavior for detached windows.
+  bool IsCrossOriginSubframe() const;
+
   FrameOwner* Owner() const;
   void SetOwner(FrameOwner*);
   HTMLFrameOwnerElement* DeprecatedLocalOwner() const;
diff --git a/third_party/blink/renderer/core/frame/frame_view.cc b/third_party/blink/renderer/core/frame/frame_view.cc
new file mode 100644
index 0000000..0f9a083
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/frame_view.cc
@@ -0,0 +1,156 @@
+// 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/core/frame/frame_view.h"
+
+#include "third_party/blink/renderer/core/frame/frame_client.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/remote_frame.h"
+#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_geometry.h"
+#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
+#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
+
+namespace blink {
+
+Frame& FrameView::GetFrame() const {
+  if (const LocalFrameView* lfv = DynamicTo<LocalFrameView>(this))
+    return lfv->GetFrame();
+  return DynamicTo<RemoteFrameView>(this)->GetFrame();
+}
+
+bool FrameView::CanThrottleRenderingForPropagation() const {
+  if (CanThrottleRendering())
+    return true;
+  if (!RuntimeEnabledFeatures::RenderingPipelineThrottlingEnabled())
+    return false;
+  LocalFrame* parent_frame = DynamicTo<LocalFrame>(GetFrame().Tree().Parent());
+  if (!parent_frame)
+    return false;
+  Frame& frame = GetFrame();
+  LayoutEmbeddedContent* owner = frame.OwnerLayoutObject();
+  return !owner && frame.IsCrossOriginSubframe();
+}
+
+void FrameView::UpdateViewportIntersection(unsigned flags,
+                                           bool needs_occlusion_tracking) {
+  if (!(flags & IntersectionObservation::kImplicitRootObserversNeedUpdate))
+    return;
+  // This should only run in child frames.
+  Frame& frame = GetFrame();
+  HTMLFrameOwnerElement* owner_element = frame.DeprecatedLocalOwner();
+  if (!owner_element)
+    return;
+  Document& owner_document = owner_element->GetDocument();
+  IntRect viewport_intersection;
+  DocumentLifecycle::LifecycleState parent_lifecycle_state =
+      owner_document.Lifecycle().GetState();
+  FrameOcclusionState occlusion_state =
+      owner_document.GetFrame()->GetOcclusionState();
+  bool should_compute_occlusion =
+      needs_occlusion_tracking &&
+      occlusion_state == FrameOcclusionState::kGuaranteedNotOccluded &&
+      parent_lifecycle_state >= DocumentLifecycle::kPrePaintClean &&
+      RuntimeEnabledFeatures::IntersectionObserverV2Enabled();
+
+  LayoutEmbeddedContent* owner_layout_object =
+      owner_element->GetLayoutEmbeddedContent();
+  if (!owner_layout_object || owner_layout_object->ContentSize().IsEmpty()) {
+    // The frame is detached from layout, not visible, or zero size; leave
+    // viewport_intersection empty, and signal the frame as occluded if
+    // necessary.
+    occlusion_state = FrameOcclusionState::kPossiblyOccluded;
+  } else if (parent_lifecycle_state >= DocumentLifecycle::kLayoutClean) {
+    unsigned geometry_flags =
+        IntersectionGeometry::kShouldUseReplacedContentRect;
+    if (should_compute_occlusion)
+      geometry_flags |= IntersectionGeometry::kShouldComputeVisibility;
+
+    IntersectionGeometry geometry(nullptr, *owner_element, {},
+                                  {IntersectionObserver::kMinimumThreshold},
+                                  geometry_flags);
+    // geometry.IntersectionRect() is in absolute coordinates of the owning
+    // document. Map it down to absolute coordinates in the child document.
+    LayoutRect intersection_rect = LayoutRect(
+        owner_layout_object
+            ->AncestorToLocalQuad(
+                nullptr, FloatQuad(FloatRect(geometry.IntersectionRect())),
+                kUseTransforms)
+            .BoundingBox());
+    // Map from the box coordinates of the owner to the inner frame.
+    intersection_rect.Move(-owner_layout_object->PhysicalContentBoxOffset());
+    // Don't let EnclosingIntRect turn an empty rect into a non-empty one.
+    if (intersection_rect.IsEmpty()) {
+      viewport_intersection =
+          IntRect(FlooredIntPoint(intersection_rect.Location()), IntSize());
+    } else {
+      viewport_intersection = EnclosingIntRect(intersection_rect);
+    }
+    if (should_compute_occlusion && !geometry.IsVisible())
+      occlusion_state = FrameOcclusionState::kPossiblyOccluded;
+  } else if (occlusion_state == FrameOcclusionState::kGuaranteedNotOccluded) {
+    // If the parent LocalFrameView is throttled and out-of-date, then we can't
+    // get any useful information.
+    occlusion_state = FrameOcclusionState::kUnknown;
+  }
+
+  SetViewportIntersection(viewport_intersection, occlusion_state);
+
+  UpdateFrameVisibility(!viewport_intersection.IsEmpty());
+
+  // We don't throttle 0x0 or display:none iframes, because in practice they are
+  // sometimes used to drive UI logic.
+  bool hidden_for_throttling = viewport_intersection.IsEmpty() &&
+                               !FrameRect().IsEmpty() && owner_layout_object;
+  bool subtree_throttled = false;
+  Frame* parent_frame = GetFrame().Tree().Parent();
+  if (parent_frame && parent_frame->View()) {
+    subtree_throttled =
+        parent_frame->View()->CanThrottleRenderingForPropagation();
+  }
+  UpdateRenderThrottlingStatus(hidden_for_throttling, subtree_throttled);
+}
+
+void FrameView::UpdateFrameVisibility(bool intersects_viewport) {
+  blink::mojom::FrameVisibility frame_visibility;
+  if (LifecycleUpdatesThrottled())
+    return;
+  if (IsVisible()) {
+    frame_visibility =
+        intersects_viewport
+            ? blink::mojom::FrameVisibility::kRenderedInViewport
+            : blink::mojom::FrameVisibility::kRenderedOutOfViewport;
+  } else {
+    frame_visibility = blink::mojom::FrameVisibility::kNotRendered;
+  }
+  if (frame_visibility != frame_visibility_) {
+    frame_visibility_ = frame_visibility;
+    if (FrameClient* client = GetFrame().Client())
+      client->VisibilityChanged(frame_visibility);
+  }
+}
+
+void FrameView::UpdateRenderThrottlingStatus(bool hidden_for_throttling,
+                                             bool subtree_throttled,
+                                             bool recurse) {
+  bool was_throttled = CanThrottleRendering();
+  hidden_for_throttling_ = hidden_for_throttling;
+  subtree_throttled_ = subtree_throttled;
+  bool throttling_did_change = (was_throttled != CanThrottleRendering());
+  if (throttling_did_change)
+    RenderThrottlingStatusChanged();
+  if (recurse) {
+    for (Frame* child = GetFrame().Tree().FirstChild(); child;
+         child = child->Tree().NextSibling()) {
+      if (FrameView* child_view = child->View()) {
+        child_view->UpdateRenderThrottlingStatus(
+            child_view->IsHiddenForThrottling(),
+            child_view->IsAttached() && CanThrottleRenderingForPropagation(),
+            true);
+      }
+    }
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/frame_view.h b/third_party/blink/renderer/core/frame/frame_view.h
index 18d4cf9e..2de8435 100644
--- a/third_party/blink/renderer/core/frame/frame_view.h
+++ b/third_party/blink/renderer/core/frame/frame_view.h
@@ -5,15 +5,19 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_VIEW_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_VIEW_H_
 
+#include "third_party/blink/public/common/frame/occlusion_state.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h"
 #include "third_party/blink/renderer/core/frame/embedded_content_view.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
+class Frame;
 struct IntrinsicSizingInfo;
 
 class CORE_EXPORT FrameView : public EmbeddedContentView {
  public:
+  FrameView(const IntRect& frame_rect) : EmbeddedContentView(frame_rect) {}
   ~FrameView() override = default;
 
   // parent_flags is the result of calling GetIntersectionObservationFlags on
@@ -24,7 +28,45 @@
   virtual bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const = 0;
   virtual bool HasIntrinsicSizingInfo() const = 0;
 
+  // Returns true if this frame could potentially skip rendering and avoid
+  // scheduling visual updates.
+  virtual bool CanThrottleRendering() const = 0;
+
+  // A display:none iframe cannot be throttled, but its child frames can be
+  // throttled. This method will return 'true' for the the display:none iframe.
+  // It is used to set the subtree_throttled_ flag on child frames.
+  bool CanThrottleRenderingForPropagation() const;
+
   bool IsFrameView() const override { return true; }
+
+  Frame& GetFrame() const;
+  blink::mojom::FrameVisibility GetFrameVisibility() const {
+    return frame_visibility_;
+  }
+
+  // This is used to control render throttling, which determines whether
+  // lifecycle updates in the child frame will skip rendering work.
+  bool IsHiddenForThrottling() const { return hidden_for_throttling_; }
+  bool IsSubtreeThrottled() const { return subtree_throttled_; }
+  void UpdateRenderThrottlingStatus(bool hidden_for_throttling,
+                                    bool subtree_throttled,
+                                    bool recurse = false);
+
+ protected:
+  virtual void SetViewportIntersection(const IntRect& viewport_intersection,
+                                       FrameOcclusionState occlusion_state) = 0;
+  virtual void RenderThrottlingStatusChanged() = 0;
+  virtual bool LifecycleUpdatesThrottled() const { return false; }
+  void UpdateViewportIntersection(unsigned, bool);
+  // FrameVisibility is tracked by the browser process, which may suppress
+  // lifecycle updates for a frame outside the viewport.
+  void UpdateFrameVisibility(bool);
+
+ private:
+  blink::mojom::FrameVisibility frame_visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
+  bool hidden_for_throttling_;
+  bool subtree_throttled_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index c1bd6b3..29a339c 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -655,13 +655,6 @@
   return const_cast<LocalFrame&>(*cur_frame);
 }
 
-bool LocalFrame::IsCrossOriginSubframe() const {
-  const SecurityOrigin* security_origin =
-      GetSecurityContext()->GetSecurityOrigin();
-  return !security_origin->CanAccess(
-      Tree().Top().GetSecurityContext()->GetSecurityOrigin());
-}
-
 scoped_refptr<InspectorTaskRunner> LocalFrame::GetInspectorTaskRunner() {
   return inspector_task_runner_;
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index a99145ea..3929b639 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -188,14 +188,6 @@
   bool IsLocalRoot() const;
   LocalFrame& LocalFrameRoot() const;
 
-  // Note that the result of this function should not be cached: a frame is
-  // not necessarily detached when it is navigated, so the return value can
-  // change.
-  // In addition, this function will always return true for a detached frame.
-  // TODO(dcheng): Move this to LocalDOMWindow and figure out the right
-  // behavior for detached windows.
-  bool IsCrossOriginSubframe() const;
-
   CoreProbeSink* GetProbeSink() { return probe_sink_.Get(); }
   scoped_refptr<InspectorTaskRunner> GetInspectorTaskRunner();
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 3cbe0a33..aa60dff 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -222,9 +222,8 @@
 static bool g_initial_track_all_paint_invalidations = false;
 
 LocalFrameView::LocalFrameView(LocalFrame& frame, IntRect frame_rect)
-    : frame_(frame),
-      frame_rect_(frame_rect),
-      is_attached_(false),
+    : FrameView(frame_rect),
+      frame_(frame),
       display_mode_(kWebDisplayModeBrowser),
       can_have_scrollbars_(true),
       has_pending_layout_(false),
@@ -248,8 +247,6 @@
       needs_update_geometries_(false),
       root_layer_did_scroll_(false),
       frame_timing_requests_dirty_(true),
-      hidden_for_throttling_(false),
-      subtree_throttled_(false),
       // The compositor throttles the main frame using deferred commits, we
       // can't throttle it here or it seems the root compositor doesn't get
       // setup properly.
@@ -372,17 +369,6 @@
   }
 }
 
-void LocalFrameView::SetupRenderThrottling() {
-  // We observe the frame owner element instead of the document element, because
-  // if the document has no content we can falsely think the frame is invisible.
-  // Note that this means we cannot throttle top-level frames or (currently)
-  // frames whose owner element is remote.
-  HTMLFrameOwnerElement* target_element = GetFrame().DeprecatedLocalOwner();
-  if (!target_element)
-    return;
-  target_element->StartVisibilityObserver();
-}
-
 void LocalFrameView::Dispose() {
   CHECK(!IsInPerformLayout());
 
@@ -448,7 +434,7 @@
 }
 
 void LocalFrameView::InvalidateRect(const IntRect& rect) {
-  auto* layout_object = frame_->OwnerLayoutObject();
+  auto* layout_object = GetLayoutEmbeddedContent();
   if (!layout_object)
     return;
 
@@ -459,47 +445,25 @@
   layout_object->InvalidatePaintRectangle(LayoutRect(paint_invalidation_rect));
 }
 
-void LocalFrameView::SetFrameRect(const IntRect& unclamped_frame_rect) {
-  IntRect frame_rect(SaturatedRect(unclamped_frame_rect));
-  if (frame_rect == frame_rect_)
-    return;
-  const bool width_changed = frame_rect_.Width() != frame_rect.Width();
-  const bool height_changed = frame_rect_.Height() != frame_rect.Height();
-  frame_rect_ = frame_rect;
+void LocalFrameView::FrameRectsChanged(const IntRect& old_rect) {
+  const bool width_changed = Size().Width() != old_rect.Width();
+  const bool height_changed = Size().Height() != old_rect.Height();
 
-  FrameRectsChanged();
+  PropagateFrameRects();
 
-  if (auto* layout_view = GetLayoutView())
-    layout_view->SetShouldCheckForPaintInvalidation();
+  if (FrameRect() != old_rect) {
+    if (auto* layout_view = GetLayoutView())
+      layout_view->SetShouldCheckForPaintInvalidation();
+  }
 
   if (width_changed || height_changed) {
     ViewportSizeChanged(width_changed, height_changed);
-
     if (frame_->IsMainFrame())
       frame_->GetPage()->GetVisualViewport().MainFrameDidChangeSize();
-
     GetFrame().Loader().RestoreScrollPositionAndViewState();
   }
 }
 
-IntPoint LocalFrameView::Location() const {
-  IntPoint location(frame_rect_.Location());
-
-  // As an optimization, we don't include the root layer's scroll offset in the
-  // frame rect.  As a result, we don't need to recalculate the frame rect every
-  // time the root layer scrolls, but we need to add it in here.
-  LayoutEmbeddedContent* owner = frame_->OwnerLayoutObject();
-  if (owner) {
-    LayoutView* owner_layout_view = owner->View();
-    DCHECK(owner_layout_view);
-    if (owner_layout_view->HasOverflowClip()) {
-      IntSize scroll_offset(owner_layout_view->ScrolledContentOffset());
-      location.SaturatedMove(-scroll_offset.Width(), -scroll_offset.Height());
-    }
-  }
-  return location;
-}
-
 Page* LocalFrameView::GetPage() const {
   return GetFrame().GetPage();
 }
@@ -1035,7 +999,6 @@
 
 void LocalFrameView::RunPostLifecycleSteps() {
   RunIntersectionObserverSteps();
-  UpdateThrottlingStatusForSubtree();
 }
 
 void LocalFrameView::RunIntersectionObserverSteps() {
@@ -1099,7 +1062,7 @@
 }
 
 void LocalFrameView::UpdateGeometry() {
-  LayoutEmbeddedContent* layout = frame_->OwnerLayoutObject();
+  LayoutEmbeddedContent* layout = GetLayoutEmbeddedContent();
   if (!layout)
     return;
 
@@ -1320,12 +1283,6 @@
          page->GetFocusController().IsActive();
 }
 
-void LocalFrameView::NotifyFrameRectsChangedIfNeededRecursive() {
-  ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
-    frame_view.NotifyFrameRectsChangedIfNeeded();
-  });
-}
-
 void LocalFrameView::InvalidateBackgroundAttachmentFixedDescendantsOnScroll(
     const LayoutObject& scrolled_object) {
   for (auto* const layout_object : background_attachment_fixed_objects_) {
@@ -1974,7 +1931,7 @@
 }
 
 LocalFrameView* LocalFrameView::ParentFrameView() const {
-  if (!is_attached_)
+  if (!IsAttached())
     return nullptr;
 
   Frame* parent_frame = frame_->Tree().Parent();
@@ -1984,6 +1941,10 @@
   return nullptr;
 }
 
+LayoutEmbeddedContent* LocalFrameView::GetLayoutEmbeddedContent() const {
+  return frame_->OwnerLayoutObject();
+}
+
 void LocalFrameView::VisualViewportScrollbarsChanged() {
   if (LayoutView* layout_view = GetLayoutView())
     layout_view->Layer()->ClearClipRects();
@@ -2044,7 +2005,7 @@
       DocumentLifecycle::LifecycleUpdateReason::kOther);
 
   auto* detached_frame_view = this;
-  while (detached_frame_view->is_attached_ &&
+  while (detached_frame_view->IsAttached() &&
          detached_frame_view != local_frame_view_root) {
     detached_frame_view = detached_frame_view->ParentFrameView();
     CHECK(detached_frame_view);
@@ -2052,7 +2013,7 @@
 
   if (detached_frame_view == local_frame_view_root)
     return;
-  DCHECK(!detached_frame_view->is_attached_);
+  DCHECK(!detached_frame_view->IsAttached());
 
   // We are printing a detached frame or a descendant of a detached frame which
   // was not reached in some phases during during |local_frame_view_root->
@@ -2176,7 +2137,7 @@
   // This must be called from the root frame, or a detached frame for printing,
   // since it recurses down, not up. Otherwise the lifecycles of the frames
   // might be out of sync.
-  DCHECK(frame_->IsLocalRoot() || !is_attached_);
+  DCHECK(frame_->IsLocalRoot() || !IsAttached());
 
   // Only the following target states are supported.
   DCHECK(target_state == DocumentLifecycle::kLayoutClean ||
@@ -2334,7 +2295,9 @@
     ForAllNonThrottledLocalFrameViews(
         [](LocalFrameView& frame_view) { frame_view.NotifyResizeObservers(); });
 
-    NotifyFrameRectsChangedIfNeededRecursive();
+    ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
+      frame_view.NotifyFrameRectsChangedIfNeeded();
+    });
   }
   // If we exceed the number of re-layouts during ResizeObserver notifications,
   // then we shouldn't continue with the lifecycle updates. At that time, we
@@ -2393,7 +2356,7 @@
       // We may record more foreign layers under the frame.
       if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled())
         frame_view.SetPaintArtifactCompositorNeedsUpdate();
-      if (auto* owner = frame_view.GetFrame().OwnerLayoutObject())
+      if (auto* owner = frame_view.GetLayoutEmbeddedContent())
         owner->SetShouldCheckForPaintInvalidation();
     }
     if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
@@ -3079,7 +3042,7 @@
 IntPoint LocalFrameView::ConvertSelfToChild(const EmbeddedContentView& child,
                                             const IntPoint& point) const {
   IntPoint new_point(point);
-  new_point.MoveBy(-child.FrameRect().Location());
+  new_point.MoveBy(-child.Location());
   return new_point;
 }
 
@@ -3173,7 +3136,7 @@
 IntRect LocalFrameView::ConvertToContainingEmbeddedContentView(
     const IntRect& local_rect) const {
   if (LocalFrameView* parent = ParentFrameView()) {
-    auto* layout_object = frame_->OwnerLayoutObject();
+    auto* layout_object = GetLayoutEmbeddedContent();
     if (!layout_object)
       return local_rect;
 
@@ -3203,7 +3166,7 @@
 LayoutPoint LocalFrameView::ConvertToContainingEmbeddedContentView(
     const LayoutPoint& local_point) const {
   if (LocalFrameView* parent = ParentFrameView()) {
-    auto* layout_object = frame_->OwnerLayoutObject();
+    auto* layout_object = GetLayoutEmbeddedContent();
     if (!layout_object)
       return local_point;
 
@@ -3221,7 +3184,7 @@
 FloatPoint LocalFrameView::ConvertToContainingEmbeddedContentView(
     const FloatPoint& local_point) const {
   if (ParentFrameView()) {
-    auto* layout_object = frame_->OwnerLayoutObject();
+    auto* layout_object = GetLayoutEmbeddedContent();
     if (!layout_object)
       return local_point;
 
@@ -3253,7 +3216,7 @@
     const DoublePoint& parent_point) const {
   if (LocalFrameView* parent = ParentFrameView()) {
     // Get our layoutObject in the parent view
-    auto* layout_object = frame_->OwnerLayoutObject();
+    auto* layout_object = GetLayoutEmbeddedContent();
     if (!layout_object)
       return parent_point;
 
@@ -3409,16 +3372,16 @@
 }
 
 void LocalFrameView::AttachToLayout() {
-  CHECK(!is_attached_);
+  CHECK(!IsAttached());
   if (frame_->GetDocument())
     CHECK_NE(Lifecycle().GetState(), DocumentLifecycle::kStopping);
-  is_attached_ = true;
+  SetAttached(true);
   LocalFrameView* parent_view = ParentFrameView();
   CHECK(parent_view);
   if (parent_view->IsVisible())
     SetParentVisible(true);
-  SetupRenderThrottling();
-  subtree_throttled_ = parent_view->CanThrottleRendering();
+  UpdateRenderThrottlingStatus(IsHiddenForThrottling(),
+                               parent_view->CanThrottleRendering());
 
   // We may have updated paint properties in detached frame subtree for
   // printing (see UpdateLifecyclePhasesForPrinting()). The paint properties
@@ -3430,9 +3393,9 @@
 }
 
 void LocalFrameView::DetachFromLayout() {
-  CHECK(is_attached_);
+  CHECK(IsAttached());
   SetParentVisible(false);
-  is_attached_ = false;
+  SetAttached(false);
 
   // We may need update paint properties in detached frame subtree for printing.
   // See UpdateLifecyclePhasesForPrinting().
@@ -3485,15 +3448,16 @@
   page->GetChromeClient().SetCursor(cursor, frame_);
 }
 
-void LocalFrameView::FrameRectsChanged() {
-  TRACE_EVENT0("blink", "LocalFrameView::frameRectsChanged");
+void LocalFrameView::PropagateFrameRects() {
+  TRACE_EVENT0("blink", "LocalFrameView::PropagateFrameRects");
   if (LayoutSizeFixedToFrameSize())
     SetLayoutSizeInternal(Size());
 
-  ForAllChildViewsAndPlugins([](EmbeddedContentView& embedded_content_view) {
-    auto* local_frame_view = DynamicTo<LocalFrameView>(embedded_content_view);
-    if (!local_frame_view || !local_frame_view->ShouldThrottleRendering())
-      embedded_content_view.FrameRectsChanged();
+  ForAllChildViewsAndPlugins([](EmbeddedContentView& view) {
+    auto* local_frame_view = DynamicTo<LocalFrameView>(view);
+    if (!local_frame_view || !local_frame_view->ShouldThrottleRendering()) {
+      view.PropagateFrameRects();
+    }
   });
 
   GetFrame().Client()->FrameRectsChanged(FrameRect());
@@ -3587,7 +3551,7 @@
 void LocalFrameView::NotifyFrameRectsChangedIfNeeded() {
   if (root_layer_did_scroll_) {
     root_layer_did_scroll_ = false;
-    FrameRectsChanged();
+    PropagateFrameRects();
   }
 }
 
@@ -3808,33 +3772,26 @@
   return point_in_root_frame;
 }
 
-void LocalFrameView::SetParentVisible(bool visible) {
-  if (IsParentVisible() == visible)
-    return;
-
+void LocalFrameView::ParentVisibleChanged() {
   // As parent visibility changes, we may need to recomposite this frame view
   // and potentially child frame views.
   SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
 
-  parent_visible_ = visible;
-
   if (!IsSelfVisible())
     return;
 
+  bool visible = IsParentVisible();
   ForAllChildViewsAndPlugins(
       [visible](EmbeddedContentView& embedded_content_view) {
         embedded_content_view.SetParentVisible(visible);
       });
 }
 
-void LocalFrameView::SetSelfVisible(bool visible) {
-  if (visible != self_visible_) {
-    // Frame view visibility affects PLC::CanBeComposited, which in turn
-    // affects compositing inputs.
-    if (LayoutView* view = GetLayoutView())
-      view->Layer()->SetNeedsCompositingInputsUpdate();
-  }
-  self_visible_ = visible;
+void LocalFrameView::SelfVisibleChanged() {
+  // FrameView visibility affects PLC::CanBeComposited, which in turn affects
+  // compositing inputs.
+  if (LayoutView* view = GetLayoutView())
+    view->Layer()->SetNeedsCompositingInputsUpdate();
 }
 
 void LocalFrameView::Show() {
@@ -3925,6 +3882,8 @@
     intersection_observation_state_ = kNotNeeded;
   }
 
+  UpdateViewportIntersection(flags, needs_occlusion_tracking);
+
   for (Frame* child = frame_->Tree().FirstChild(); child;
        child = child->Tree().NextSibling()) {
     needs_occlusion_tracking |=
@@ -3954,70 +3913,27 @@
   });
 }
 
-void LocalFrameView::UpdateThrottlingStatusForSubtree() {
-  if (!GetFrame().GetDocument()->IsActive())
-    return;
-
-  // Don't throttle display:none frames (see updateRenderThrottlingStatus).
-  HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
-  if (IsHiddenForThrottling() && owner_element &&
-      !owner_element->GetLayoutObject()) {
-    // No need to notify children because descendants of display:none frames
-    // should remain throttled.
-    UpdateRenderThrottlingStatus(hidden_for_throttling_, subtree_throttled_,
-                                 kDontForceThrottlingInvalidation,
-                                 kDontNotifyChildren);
-  }
-
-  ForAllChildLocalFrameViews([](LocalFrameView& child_view) {
-    child_view.UpdateThrottlingStatusForSubtree();
-  });
-}
-
 void LocalFrameView::CrossOriginStatusChanged() {
-  // Cross-domain status is not stored as a dirty bit within LocalFrameView,
-  // so force-invalidate throttling status when it changes regardless of
-  // previous or new value.
-  UpdateRenderThrottlingStatus(hidden_for_throttling_, subtree_throttled_,
-                               kForceThrottlingInvalidation);
+  // If any of these conditions hold, then a change in cross-origin status does
+  // not affect throttling.
+  if (lifecycle_updates_throttled_ ||
+      !RuntimeEnabledFeatures::RenderingPipelineThrottlingEnabled() ||
+      IsSubtreeThrottled() || !IsHiddenForThrottling()) {
+    return;
+  }
+  RenderThrottlingStatusChanged();
+  // Immediately propagate changes to children.
+  UpdateRenderThrottlingStatus(IsHiddenForThrottling(), IsSubtreeThrottled(),
+                               true);
 }
 
-void LocalFrameView::UpdateRenderThrottlingStatus(
-    bool hidden,
-    bool subtree_throttled,
-    ForceThrottlingInvalidationBehavior force_throttling_invalidation_behavior,
-    NotifyChildrenBehavior notify_children_behavior) {
-  TRACE_EVENT0("blink", "LocalFrameView::updateRenderThrottlingStatus");
+void LocalFrameView::RenderThrottlingStatusChanged() {
+  TRACE_EVENT0("blink", "LocalFrameView::RenderThrottlingStatusChanged");
   DCHECK(!IsInPerformLayout());
   DCHECK(!frame_->GetDocument() || !frame_->GetDocument()->InStyleRecalc());
-  bool was_throttled = CanThrottleRendering();
-
-  // Note that we disallow throttling of 0x0 and display:none frames because
-  // some sites use them to drive UI logic.
-  hidden_for_throttling_ = hidden;
-  subtree_throttled_ = subtree_throttled;
-
-  bool is_throttled = CanThrottleRendering();
-  bool became_unthrottled = was_throttled && !is_throttled;
-
-  // If this LocalFrameView became unthrottled or throttled, we must make sure
-  // all its children are notified synchronously. Otherwise we 1) might attempt
-  // to paint one of the children with an out-of-date layout before
-  // |updateRenderThrottlingStatus| has made it throttled or 2) fail to
-  // unthrottle a child whose parent is unthrottled by a later notification.
-  if (notify_children_behavior == kNotifyChildren &&
-      (was_throttled != is_throttled ||
-       force_throttling_invalidation_behavior ==
-           kForceThrottlingInvalidation)) {
-    ForAllChildLocalFrameViews([is_throttled](LocalFrameView& frame_view) {
-      frame_view.UpdateRenderThrottlingStatus(frame_view.hidden_for_throttling_,
-                                              is_throttled);
-    });
-  }
 
   ScrollingCoordinator* scrolling_coordinator = this->GetScrollingCoordinator();
-  if (became_unthrottled ||
-      force_throttling_invalidation_behavior == kForceThrottlingInvalidation) {
+  if (!CanThrottleRendering()) {
     // ScrollingCoordinator needs to update according to the new throttling
     // status.
     if (scrolling_coordinator)
@@ -4028,7 +3944,7 @@
     // Force a full repaint of this frame to ensure we are not left with a
     // partially painted version of this frame's contents if we skipped
     // painting them while the frame was throttled.
-    auto* layout_view = GetLayoutView();
+    LayoutView* layout_view = GetLayoutView();
     if (layout_view) {
       layout_view->InvalidatePaintForViewAndCompositedLayers();
       // Also need to update all paint properties that might be skipped while
@@ -4041,17 +3957,18 @@
       SetPaintArtifactCompositorNeedsUpdate();
   }
 
-  EventHandlerRegistry& registry = frame_->GetEventHandlerRegistry();
-  bool has_handlers =
-      (registry.HasEventHandlers(EventHandlerRegistry::kTouchAction) ||
-       registry.HasEventHandlers(
-           EventHandlerRegistry::kTouchStartOrMoveEventBlocking) ||
-       registry.HasEventHandlers(
-           EventHandlerRegistry::kTouchStartOrMoveEventBlockingLowLatency));
-  if (was_throttled != CanThrottleRendering() && scrolling_coordinator &&
-      has_handlers) {
-    scrolling_coordinator->TouchEventTargetRectsDidChange(
-        &GetFrame().LocalFrameRoot());
+  if (scrolling_coordinator) {
+    EventHandlerRegistry& registry = frame_->GetEventHandlerRegistry();
+    bool has_handlers =
+        (registry.HasEventHandlers(EventHandlerRegistry::kTouchAction) ||
+         registry.HasEventHandlers(
+             EventHandlerRegistry::kTouchStartOrMoveEventBlocking) ||
+         registry.HasEventHandlers(
+             EventHandlerRegistry::kTouchStartOrMoveEventBlockingLowLatency));
+    if (has_handlers) {
+      scrolling_coordinator->TouchEventTargetRectsDidChange(
+          &GetFrame().LocalFrameRoot());
+    }
   }
 
   if (FrameScheduler* frame_scheduler = frame_->GetFrameScheduler()) {
@@ -4144,7 +4061,7 @@
     return true;
   if (!RuntimeEnabledFeatures::RenderingPipelineThrottlingEnabled())
     return false;
-  if (subtree_throttled_)
+  if (IsSubtreeThrottled())
     return true;
   // We only throttle hidden cross-origin frames. This is to avoid a situation
   // where an ancestor frame directly depends on the pipeline timing of a
@@ -4155,27 +4072,12 @@
   return IsHiddenForThrottling() && frame_->IsCrossOriginSubframe();
 }
 
-bool LocalFrameView::IsHiddenForThrottling() const {
-  // TODO(szager): hidden_for_throttling_ has a different meaning depending on
-  // whether this frame has a remote parent (i.e, if it's OOPIF). In the OOPIF
-  // case, it means "non-zero sized AND not display:none AND scrolled out of the
-  // parent document's viewport." For the non-OOPIF case, it just means
-  // "scrolled out of the parent document's viewport." Fixing this will
-  // require some moderately complex refactoring of render throttling.
-  if (!hidden_for_throttling_)
-    return false;
-  HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
-  if (owner_element)
-    return !Size().IsEmpty() && !!owner_element->GetLayoutObject();
-  return true;
-}
-
 void LocalFrameView::BeginLifecycleUpdates() {
   // Avoid pumping frames for the initially empty document.
   if (!GetFrame().Loader().StateMachine()->CommittedFirstRealDocumentLoad())
     return;
   lifecycle_updates_throttled_ = false;
-  if (auto* owner = GetFrame().OwnerLayoutObject())
+  if (auto* owner = GetLayoutEmbeddedContent())
     owner->SetShouldCheckForPaintInvalidation();
 
   LayoutView* layout_view = GetLayoutView();
@@ -4186,7 +4088,8 @@
                                 kMarkOnlyThis);
   }
 
-  SetupRenderThrottling();
+  ScheduleAnimation();
+  SetIntersectionObservationState(kRequired);
 
   // The compositor will "defer commits" for the main frame until we
   // explicitly request them.
@@ -4422,10 +4325,6 @@
   lifecycle_observers_.erase(observer);
 }
 
-void LocalFrameView::UpdateVisibility(bool is_visible) {
-  UpdateRenderThrottlingStatus(!is_visible, subtree_throttled_);
-}
-
 #if DCHECK_IS_ON()
 LocalFrameView::DisallowLayoutInvalidationScope::
     DisallowLayoutInvalidationScope(LocalFrameView* view)
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index e759a23..08f8ef8a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -127,22 +127,6 @@
 
   void Invalidate() { InvalidateRect(IntRect(0, 0, Width(), Height())); }
   void InvalidateRect(const IntRect&);
-  void SetFrameRect(const IntRect&) override;
-  IntRect FrameRect() const override { return IntRect(Location(), Size()); }
-  IntPoint Location() const;
-  int X() const { return Location().X(); }
-  int Y() const { return Location().Y(); }
-  int Width() const { return Size().Width(); }
-  int Height() const { return Size().Height(); }
-  IntSize Size() const { return frame_rect_.Size(); }
-  void Resize(int width, int height) { Resize(IntSize(width, height)); }
-  void Resize(const IntSize& size) {
-    SetFrameRect(IntRect(frame_rect_.Location(), size));
-  }
-
-  // Called when our frame rect changes (or the rect/scroll offset of an
-  // ancestor changes).
-  void FrameRectsChanged() override;
 
   LocalFrame& GetFrame() const {
     DCHECK(frame_);
@@ -252,6 +236,7 @@
   void UpdateCountersAfterStyleChange();
 
   void Dispose() override;
+  void PropagateFrameRects() override;
   void InvalidateAllCustomScrollbarsOnActiveChanged();
 
   // True if the LocalFrameView's base background color is completely opaque.
@@ -475,27 +460,10 @@
   // coordinate space.
   ChromeClient* GetChromeClient() const;
 
-  // This is called when the intersection between the FrameView with its
-  // embedding view changes.
-  void UpdateVisibility(bool is_visible);
-
-  // Functions for child manipulation and inspection.
-  // The visibility flags are set for iframes based on style properties of the
-  // HTMLFrameOwnerElement in the embedding document.
-  bool IsSelfVisible() const {
-    return self_visible_;
-  }  // Whether or not we have been explicitly marked as visible or not.
-  bool IsParentVisible() const {
-    return parent_visible_;
-  }  // Whether or not our parent is visible.
-  bool IsVisible() const {
-    return self_visible_ && parent_visible_;
-  }  // Whether or not we are actually visible.
-  void SetParentVisible(bool) override;
-  void SetSelfVisible(bool);
+  LocalFrameView* ParentFrameView() const override;
+  LayoutEmbeddedContent* GetLayoutEmbeddedContent() const override;
   void AttachToLayout() override;
   void DetachFromLayout() override;
-  bool IsAttached() const override { return is_attached_; }
   using PluginSet = HeapHashSet<Member<WebPluginContainerImpl>>;
   const PluginSet& Plugins() const { return plugins_; }
   void AddPlugin(WebPluginContainerImpl*);
@@ -609,11 +577,7 @@
   // Returns true if this frame should not render or schedule visual updates.
   bool ShouldThrottleRendering() const;
 
-  // Returns true if this frame could potentially skip rendering and avoid
-  // scheduling visual updates.
-  bool CanThrottleRendering() const;
-  bool IsHiddenForThrottling() const;
-  void SetupRenderThrottling();
+  bool CanThrottleRendering() const override;
 
   void BeginLifecycleUpdates();
 
@@ -690,17 +654,6 @@
 
   const cc::Layer* RootCcLayer() const;
 
-  enum ForceThrottlingInvalidationBehavior {
-    kDontForceThrottlingInvalidation,
-    kForceThrottlingInvalidation
-  };
-  enum NotifyChildrenBehavior { kDontNotifyChildren, kNotifyChildren };
-  void UpdateRenderThrottlingStatus(
-      bool hidden,
-      bool subtree_throttled,
-      ForceThrottlingInvalidationBehavior = kDontForceThrottlingInvalidation,
-      NotifyChildrenBehavior = kNotifyChildren);
-
   // Keeps track of whether the scrollable state for the LocalRoot has changed
   // since ScrollingCoordinator last checked. Only ScrollingCoordinator should
   // ever call the clearing function.
@@ -736,7 +689,16 @@
   void UnregisterFromLifecycleNotifications(LifecycleNotificationObserver*);
 
  protected:
+  void FrameRectsChanged(const IntRect&) override;
+  void SelfVisibleChanged() override;
+  void ParentVisibleChanged() override;
   void NotifyFrameRectsChangedIfNeeded();
+  void SetViewportIntersection(const IntRect& viewport_intersection,
+                               FrameOcclusionState occlusion_state) override {}
+  void RenderThrottlingStatusChanged() override;
+  bool LifecycleUpdatesThrottled() const override {
+    return lifecycle_updates_throttled_;
+  }
 
  private:
 #if DCHECK_IS_ON()
@@ -762,7 +724,6 @@
                      const GlobalPaintFlags,
                      const CullRect&) const;
 
-  LocalFrameView* ParentFrameView() const;
   LayoutSVGRoot* EmbeddedReplacedContent() const;
 
   void DispatchEventsForPrintingOnAllFrames();
@@ -792,7 +753,6 @@
       DocumentLifecycle::LifecycleState target_state);
   void RunPaintLifecyclePhase();
 
-  void NotifyFrameRectsChangedIfNeededRecursive();
   void PrePaint();
   void PaintTree();
   void UpdateStyleAndLayoutIfNeededRecursive();
@@ -824,7 +784,6 @@
       const DoublePoint&) const;
 
   void UpdateGeometriesIfNeeded();
-
   bool WasViewportResized();
   void SendResizeEventIfNeeded();
 
@@ -859,8 +818,6 @@
   bool UpdateViewportIntersectionsForSubtree(unsigned parent_flags) override;
   void DeliverSynchronousIntersectionObservations();
 
-  void UpdateThrottlingStatusForSubtree();
-
   void NotifyResizeObservers();
 
   bool CheckLayoutInvalidationIsAllowed() const;
@@ -876,11 +833,6 @@
 
   Member<LocalFrame> frame_;
 
-  IntRect frame_rect_;
-  bool is_attached_;
-  bool self_visible_;
-  bool parent_visible_;
-
   WebDisplayMode display_mode_;
 
   DisplayShape display_shape_;
@@ -953,11 +905,8 @@
   // main frame.
   Member<RootFrameViewport> viewport_scrollable_area_;
 
-  // The following members control rendering pipeline throttling for this
-  // frame. They are only updated in response to intersection observer
-  // notifications, i.e., not in the middle of the lifecycle.
-  bool hidden_for_throttling_;
-  bool subtree_throttled_;
+  // Non-top-level frames a throttled until they are ready to run lifecycle
+  // updates (after render-blocking resources have loaded).
   bool lifecycle_updates_throttled_;
 
   // This is set on the local root frame view only.
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index 4d2390b..0044dae 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -11,8 +11,6 @@
 #include "third_party/blink/renderer/core/frame/remote_frame.h"
 #include "third_party/blink/renderer/core/frame/remote_frame_client.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
-#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
-#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_entry.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/page.h"
@@ -24,14 +22,14 @@
 namespace blink {
 
 RemoteFrameView::RemoteFrameView(RemoteFrame* remote_frame)
-    : remote_frame_(remote_frame), is_attached_(false) {
+    : FrameView(IntRect()), remote_frame_(remote_frame) {
   DCHECK(remote_frame);
 }
 
 RemoteFrameView::~RemoteFrameView() = default;
 
 LocalFrameView* RemoteFrameView::ParentFrameView() const {
-  if (!is_attached_)
+  if (!IsAttached())
     return nullptr;
 
   HTMLFrameOwnerElement* owner = remote_frame_->DeprecatedLocalOwner();
@@ -43,8 +41,12 @@
   return To<LocalFrame>(remote_frame_->Tree().Parent())->View();
 }
 
+LayoutEmbeddedContent* RemoteFrameView::GetLayoutEmbeddedContent() const {
+  return remote_frame_->OwnerLayoutObject();
+}
+
 LocalFrameView* RemoteFrameView::ParentLocalRootFrameView() const {
-  if (!is_attached_)
+  if (!IsAttached())
     return nullptr;
 
   HTMLFrameOwnerElement* owner = remote_frame_->DeprecatedLocalOwner();
@@ -59,28 +61,21 @@
 }
 
 void RemoteFrameView::AttachToLayout() {
-  DCHECK(!is_attached_);
-  is_attached_ = true;
+  DCHECK(!IsAttached());
+  SetAttached(true);
   if (ParentFrameView()->IsVisible())
     SetParentVisible(true);
-  UpdateVisibility(true);
-
-  // For RemoteFrame's, the work of the visibility observer is done in
-  // RemoteFrameView::UpdateViewportIntersectionsForSubtree. Disable the
-  // visibility observer to prevent redundant work.
-  HTMLFrameOwnerElement* owner = remote_frame_->DeprecatedLocalOwner();
-  if (owner)
-    owner->StopVisibilityObserver();
-
-  subtree_throttled_ = ParentFrameView()->CanThrottleRendering();
-
-  FrameRectsChanged();
+  UpdateFrameVisibility(true);
+  UpdateRenderThrottlingStatus(
+      IsHiddenForThrottling(),
+      ParentFrameView()->CanThrottleRenderingForPropagation());
+  FrameRectsChanged(FrameRect());
 }
 
 void RemoteFrameView::DetachFromLayout() {
-  DCHECK(is_attached_);
+  DCHECK(IsAttached());
   SetParentVisible(false);
-  is_attached_ = false;
+  SetAttached(false);
 }
 
 RemoteFrameView* RemoteFrameView::Create(RemoteFrame* remote_frame) {
@@ -91,65 +86,13 @@
 
 bool RemoteFrameView::UpdateViewportIntersectionsForSubtree(
     unsigned parent_flags) {
-  if (!(parent_flags &
-        IntersectionObservation::kImplicitRootObserversNeedUpdate)) {
-    return needs_occlusion_tracking_;
-  }
+  UpdateViewportIntersection(parent_flags, needs_occlusion_tracking_);
+  return needs_occlusion_tracking_;
+}
 
-  // This should only run in child frames.
-  HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner();
-  DCHECK(owner_element);
-  LayoutEmbeddedContent* owner = owner_element->GetLayoutEmbeddedContent();
-  IntRect viewport_intersection;
-  DocumentLifecycle::LifecycleState parent_lifecycle_state =
-      owner_element->GetDocument().Lifecycle().GetState();
-  FrameOcclusionState occlusion_state =
-      owner_element->GetDocument().GetFrame()->GetOcclusionState();
-  bool should_compute_occlusion =
-      needs_occlusion_tracking_ &&
-      occlusion_state == FrameOcclusionState::kGuaranteedNotOccluded &&
-      parent_lifecycle_state >= DocumentLifecycle::kPrePaintClean &&
-      RuntimeEnabledFeatures::IntersectionObserverV2Enabled();
-
-  if (!owner || !self_visible_) {
-    // The frame is detached from layout or not visible; leave
-    // viewport_intersection empty, and signal the frame as occluded if
-    // necessary.
-    occlusion_state = FrameOcclusionState::kPossiblyOccluded;
-  } else if (parent_lifecycle_state >= DocumentLifecycle::kLayoutClean) {
-    unsigned geometry_flags =
-        IntersectionGeometry::kShouldUseReplacedContentRect;
-    if (should_compute_occlusion)
-      geometry_flags |= IntersectionGeometry::kShouldComputeVisibility;
-
-    IntersectionGeometry geometry(nullptr, *owner_element, {},
-                                  {IntersectionObserver::kMinimumThreshold},
-                                  geometry_flags);
-    // geometry.IntersectionRect() is in absolute coordinates of the owning
-    // document. Map it down to absolute coordinates in the child document.
-    LayoutRect intersection_rect = LayoutRect(
-        owner
-            ->AncestorToLocalQuad(
-                nullptr, FloatQuad(FloatRect(geometry.IntersectionRect())),
-                kUseTransforms)
-            .BoundingBox());
-    // Map from the box coordinates of the owner to the inner frame.
-    intersection_rect.Move(-owner->PhysicalContentBoxOffset());
-    // Don't let EnclosingIntRect turn an empty rect into a non-empty one.
-    if (intersection_rect.IsEmpty()) {
-      viewport_intersection =
-          IntRect(FlooredIntPoint(intersection_rect.Location()), IntSize());
-    } else {
-      viewport_intersection = EnclosingIntRect(intersection_rect);
-    }
-    if (should_compute_occlusion && !geometry.IsVisible())
-      occlusion_state = FrameOcclusionState::kPossiblyOccluded;
-  } else if (occlusion_state == FrameOcclusionState::kGuaranteedNotOccluded) {
-    // If the parent LocalFrameView is throttled and out-of-date, then we can't
-    // get any useful information.
-    occlusion_state = FrameOcclusionState::kUnknown;
-  }
-
+void RemoteFrameView::SetViewportIntersection(
+    const IntRect& viewport_intersection,
+    FrameOcclusionState occlusion_state) {
   if (viewport_intersection != last_viewport_intersection_ ||
       occlusion_state != last_occlusion_state_) {
     last_viewport_intersection_ = viewport_intersection;
@@ -157,22 +100,6 @@
     remote_frame_->Client()->UpdateRemoteViewportIntersection(
         viewport_intersection, occlusion_state);
   }
-
-  // TODO(szager): There is redundant functionality here; clean it up.
-  // UpdateVisibility controls RenderFrameHostImpl::visibility_ for the iframe,
-  // and RenderWidget::is_hidden_ in the iframe process. When an OOPIF is marked
-  // "hidden", it stops running lifecycle updates altogether.
-  // UpdateRenderThrottlingStatus sets the hidden_for_throttling_ and
-  // subtree_throttled_ flags on LocalFrameView in the iframe process. They
-  // control whether the iframe skips doing rendering work during lifecycle
-  // updates.
-  bool is_visible_for_throttling = !viewport_intersection.IsEmpty();
-  UpdateVisibility(is_visible_for_throttling);
-  UpdateRenderThrottlingStatus(
-      !is_visible_for_throttling,
-      is_attached_ && ParentFrameView()->CanThrottleRendering());
-
-  return needs_occlusion_tracking_;
 }
 
 void RemoteFrameView::SetNeedsOcclusionTracking(bool needs_tracking) {
@@ -194,7 +121,7 @@
   // If the local frame root is an OOPIF itself, then we use the root's
   // intersection rect. This represents a conservative maximum for the area
   // that needs to be rastered by the OOPIF compositor.
-  IntSize viewport_size = local_root_view->FrameRect().Size();
+  IntSize viewport_size = local_root_view->Size();
   if (local_root_view->GetPage()->MainFrame() != local_root_view->GetFrame()) {
     viewport_size =
         local_root_view->GetFrame().RemoteViewportIntersection().Size();
@@ -210,7 +137,7 @@
   IntSize converted_viewport_size =
       EnclosingIntRect(viewport_quad.BoundingBox()).Size();
 
-  IntSize frame_size = FrameRect().Size();
+  IntSize frame_size = Size();
 
   // Iframes that fit within the window viewport get fully rastered. For
   // iframes that are larger than the window viewport, add a 30% buffer to the
@@ -256,34 +183,7 @@
   object->InvalidatePaintRectangle(repaint_rect);
 }
 
-void RemoteFrameView::SetFrameRect(const IntRect& frame_rect) {
-  if (frame_rect == frame_rect_)
-    return;
-
-  frame_rect_ = frame_rect;
-  FrameRectsChanged();
-}
-
-IntRect RemoteFrameView::FrameRect() const {
-  IntPoint location(frame_rect_.Location());
-
-  // As an optimization, we don't include the root layer's scroll offset in the
-  // frame rect.  As a result, we don't need to recalculate the frame rect every
-  // time the root layer scrolls, but we need to add it in here.
-  LayoutEmbeddedContent* owner = remote_frame_->OwnerLayoutObject();
-  if (owner) {
-    LayoutView* owner_layout_view = owner->View();
-    DCHECK(owner_layout_view);
-    if (owner_layout_view->HasOverflowClip()) {
-      IntSize scroll_offset(owner_layout_view->ScrolledContentOffset());
-      location.SaturatedMove(-scroll_offset.Width(), -scroll_offset.Height());
-    }
-  }
-
-  return IntRect(location, frame_rect_.Size());
-}
-
-void RemoteFrameView::FrameRectsChanged() {
+void RemoteFrameView::PropagateFrameRects() {
   // Update the rect to reflect the position of the frame relative to the
   // containing local frame root. The position of the local root within
   // any remote frames, if any, is accounted for by the embedder.
@@ -322,75 +222,37 @@
 }
 
 void RemoteFrameView::UpdateGeometry() {
-  if (LayoutEmbeddedContent* layout = remote_frame_->OwnerLayoutObject())
+  if (LayoutEmbeddedContent* layout = GetLayoutEmbeddedContent())
     layout->UpdateGeometry(*this);
 }
 
 void RemoteFrameView::Hide() {
-  self_visible_ = false;
-  UpdateVisibility(scroll_visible_);
+  SetSelfVisible(false);
+  UpdateFrameVisibility(!last_viewport_intersection_.IsEmpty());
 }
 
 void RemoteFrameView::Show() {
-  self_visible_ = true;
-  UpdateVisibility(scroll_visible_);
+  SetSelfVisible(true);
+  UpdateFrameVisibility(!last_viewport_intersection_.IsEmpty());
 }
 
-void RemoteFrameView::SetParentVisible(bool visible) {
-  if (parent_visible_ == visible)
-    return;
-
-  parent_visible_ = visible;
-  if (!self_visible_)
-    return;
-  UpdateVisibility(scroll_visible_);
+void RemoteFrameView::ParentVisibleChanged() {
+  if (IsSelfVisible())
+    UpdateFrameVisibility(!last_viewport_intersection_.IsEmpty());
 }
 
-void RemoteFrameView::UpdateVisibility(bool scroll_visible) {
-  blink::mojom::FrameVisibility visibility;
-  scroll_visible_ = scroll_visible;
-  if (self_visible_ && parent_visible_) {
-    visibility = scroll_visible
-                     ? blink::mojom::FrameVisibility::kRenderedInViewport
-                     : blink::mojom::FrameVisibility::kRenderedOutOfViewport;
-  } else {
-    visibility = blink::mojom::FrameVisibility::kNotRendered;
-  }
-
-  if (visibility == visibility_)
-    return;
-  visibility_ = visibility;
-  remote_frame_->Client()->VisibilityChanged(visibility);
-}
-
-void RemoteFrameView::UpdateRenderThrottlingStatus(bool hidden,
-                                                   bool subtree_throttled) {
-  TRACE_EVENT0("blink", "RemoteFrameView::UpdateRenderThrottlingStatus");
+void RemoteFrameView::RenderThrottlingStatusChanged() {
+  TRACE_EVENT0("blink", "RemoteFrameView::RenderThrottlingStatusChanged");
   if (!remote_frame_->Client())
     return;
-
-  bool was_throttled = CanThrottleRendering();
-
-  // Note that we disallow throttling of 0x0 and display:none frames because
-  // some sites use them to drive UI logic.
-  HTMLFrameOwnerElement* owner_element = remote_frame_->DeprecatedLocalOwner();
-  hidden_for_throttling_ = hidden && !frame_rect_.IsEmpty() &&
-                           (owner_element && owner_element->GetLayoutObject());
-  subtree_throttled_ = subtree_throttled;
-
-  bool is_throttled = CanThrottleRendering();
-  if (was_throttled != is_throttled) {
-    remote_frame_->Client()->UpdateRenderThrottlingStatus(is_throttled,
-                                                          subtree_throttled_);
-  }
+  remote_frame_->Client()->UpdateRenderThrottlingStatus(IsHiddenForThrottling(),
+                                                        IsSubtreeThrottled());
 }
 
 bool RemoteFrameView::CanThrottleRendering() const {
   if (!RuntimeEnabledFeatures::RenderingPipelineThrottlingEnabled())
     return false;
-  if (subtree_throttled_)
-    return true;
-  return hidden_for_throttling_;
+  return IsSubtreeThrottled() || IsHiddenForThrottling();
 }
 
 void RemoteFrameView::SetIntrinsicSizeInfo(
@@ -419,7 +281,6 @@
 
 void RemoteFrameView::Trace(blink::Visitor* visitor) {
   visitor->Trace(remote_frame_);
-  visitor->Trace(visibility_observer_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index 93721f7..04c1ccb 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -21,8 +21,6 @@
 namespace blink {
 class CullRect;
 class GraphicsContext;
-class IntersectionObserver;
-class IntersectionObserverEntry;
 class LocalFrameView;
 class RemoteFrame;
 
@@ -38,19 +36,18 @@
 
   void AttachToLayout() override;
   void DetachFromLayout() override;
-  bool IsAttached() const override { return is_attached_; }
 
+  LocalFrameView* ParentFrameView() const override;
+  LayoutEmbeddedContent* GetLayoutEmbeddedContent() const override;
   RemoteFrame& GetFrame() const {
     DCHECK(remote_frame_);
     return *remote_frame_;
   }
 
   void Dispose() override;
+  void PropagateFrameRects() override;
   // Override to notify remote frame that its viewport size has changed.
-  void FrameRectsChanged() override;
   void InvalidateRect(const IntRect&);
-  void SetFrameRect(const IntRect&) override;
-  IntRect FrameRect() const override;
   void Paint(GraphicsContext&,
              const GlobalPaintFlags,
              const CullRect&,
@@ -58,7 +55,6 @@
   void UpdateGeometry() override;
   void Hide() override;
   void Show() override;
-  void SetParentVisible(bool) override;
 
   bool UpdateViewportIntersectionsForSubtree(unsigned parent_flags) override;
   void SetNeedsOcclusionTracking(bool);
@@ -69,6 +65,9 @@
   void SetIntrinsicSizeInfo(const IntrinsicSizingInfo& size_info);
   bool HasIntrinsicSizingInfo() const override;
 
+  bool CanThrottleRendering() const override;
+  void RenderThrottlingStatusChanged() override;
+
   // Compute the interest rect of this frame in its unscrolled space. This may
   // be used by the OOPIF's compositor to limit the amount of rastered tiles,
   // and reduce the number of paint-ops generated.
@@ -78,44 +77,40 @@
 
   void Trace(blink::Visitor*) override;
 
- private:
-  LocalFrameView* ParentFrameView() const;
+ protected:
+  // This is used to service IntersectionObservers in an OOPIF child document.
+  void SetViewportIntersection(const IntRect& viewport_intersection,
+                               FrameOcclusionState occlusion_state) override;
+  void ParentVisibleChanged() override;
 
+ private:
   // This function returns the LocalFrameView associated with the parent frame's
   // local root, or nullptr if the parent frame is not a local frame. For
   // portals, this will return the local root associated with the portal's
   // owner.
   LocalFrameView* ParentLocalRootFrameView() const;
 
-  void OnViewportIntersectionChanged(
-      const HeapVector<Member<IntersectionObserverEntry>>& entries);
-  void UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled);
-  bool CanThrottleRendering() const;
-  void UpdateVisibility(bool scroll_visible);
-
   // The properties and handling of the cycle between RemoteFrame
   // and its RemoteFrameView corresponds to that between LocalFrame
   // and LocalFrameView. Please see the LocalFrameView::frame_ comment for
   // details.
   Member<RemoteFrame> remote_frame_;
-  bool is_attached_;
   IntRect last_viewport_intersection_;
   FrameOcclusionState last_occlusion_state_ = FrameOcclusionState::kUnknown;
-  IntRect frame_rect_;
-  bool self_visible_;
-  bool parent_visible_;
-  bool scroll_visible_ = true;
-  blink::mojom::FrameVisibility visibility_ =
-      blink::mojom::FrameVisibility::kRenderedInViewport;
 
-  Member<IntersectionObserver> visibility_observer_;
-  bool subtree_throttled_ = false;
-  bool hidden_for_throttling_ = false;
   IntrinsicSizingInfo intrinsic_sizing_info_;
   bool has_intrinsic_sizing_info_ = false;
   bool needs_occlusion_tracking_ = false;
 };
 
+template <>
+struct DowncastTraits<RemoteFrameView> {
+  static bool AllowFrom(const EmbeddedContentView& embedded_content_view) {
+    return !embedded_content_view.IsLocalFrameView() &&
+           !embedded_content_view.IsPluginView();
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REMOTE_FRAME_VIEW_H_
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 9149d1a..6f88f2b7 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -932,12 +932,6 @@
   GetFrame()->Loader().StopAllLoaders();
 }
 
-WebDocumentLoader* WebLocalFrameImpl::GetProvisionalDocumentLoader() const {
-  DCHECK(GetFrame());
-  return DocumentLoaderForDocLoader(
-      GetFrame()->Loader().GetProvisionalDocumentLoader());
-}
-
 WebDocumentLoader* WebLocalFrameImpl::GetDocumentLoader() const {
   DCHECK(GetFrame());
   return DocumentLoaderForDocLoader(GetFrame()->Loader().GetDocumentLoader());
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 012f659..dc5b0d0 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -154,7 +154,6 @@
   void StartNavigation(const WebURLRequest&) override;
   void CheckCompleted() override;
   void StopLoading() override;
-  WebDocumentLoader* GetProvisionalDocumentLoader() const override;
   WebDocumentLoader* GetDocumentLoader() const override;
   void EnableViewSourceMode(bool enable) override;
   bool IsViewSourceModeEnabled() const override;
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
index 762ac43..9d50db7e 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -90,10 +90,9 @@
 }
 
 /**
- * @const
- * @type {number}
+ * @type {Intl.DateTimeFormat}
  */
-var ImperialEraLimit = 2117;
+let japaneseEraFormatter = null;
 
 /**
  * @param {!number} year
@@ -101,29 +100,30 @@
  * @return {!string}
  */
 function formatJapaneseImperialEra(year, month) {
-  // We don't show an imperial era if it is greater than 99 becase of space
-  // limitation.
-  if (year > ImperialEraLimit)
+  // Eras prior to Meiji are not helpful.
+  if (year <= 1867 || year == 1868 && month <= 9)
     return '';
-  if (year >= 2020)
-    return '(\u4ee4\u548c' + localizeNumber(year - 2018) + '\u5e74)';
-  if (year == 2019 && month >= 4)
-    return '(\u4ee4\u548c\u5143\u5e74)';
-  if (year > 1989)
-    return '(\u5e73\u6210' + localizeNumber(year - 1988) + '\u5e74)';
-  if (year == 1989)
-    return '(\u5e73\u6210\u5143\u5e74)';
-  if (year >= 1927)
-    return '(\u662d\u548c' + localizeNumber(year - 1925) + '\u5e74)';
-  if (year > 1912)
-    return '(\u5927\u6b63' + localizeNumber(year - 1911) + '\u5e74)';
-  if (year == 1912 && month >= 7)
-    return '(\u5927\u6b63\u5143\u5e74)';
-  if (year > 1868)
-    return '(\u660e\u6cbb' + localizeNumber(year - 1867) + '\u5e74)';
-  if (year == 1868)
-    return '(\u660e\u6cbb\u5143\u5e74)';
-  return '';
+  if (!japaneseEraFormatter) {
+    japaneseEraFormatter = new Intl.DateTimeFormat('ja-JP-u-ca-japanese',
+                                                   {era: 'long'});
+  }
+  // Produce the era for day 16 because it's almost the midpoint of a month.
+  let day16string = japaneseEraFormatter.format(new Date(year, month, 16));
+  let nenIndex = day16string.indexOf('\u5e74');
+  if (nenIndex == -1)
+    return '';
+  let yearPart = day16string.substring(0, nenIndex + 1);
+
+  // We don't show an imperial era if it is greater than 99 because of space
+  // limitation.
+  if (yearPart.length > 5)
+    return '';
+
+  // Replace 1-nen with Gan-nen.
+  if (yearPart.length == 4 && yearPart[2] == '1')
+    yearPart = yearPart.substring(0, 2) + '\u5143\u5e74';
+
+  return '(' + yearPart + ')';
 }
 
 function createUTCDate(year, month, date) {
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 17c94ad..726df7a 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -37,7 +37,6 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/lazy_load_frame_observer.h"
 #include "third_party/blink/renderer/core/html_names.h"
-#include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
@@ -513,67 +512,9 @@
   }
 }
 
-void HTMLFrameOwnerElement::OnViewportIntersectionChanged(
-    const HeapVector<Member<IntersectionObserverEntry>>& entries) {
-  // TODO(szager): this logic is only used for LocalFrame's; RemoteFrame's do
-  // the equivalent in RemoteFrameView::UpdateViewportIntersectionsForSubtree.
-  // The reason for the divergent implementations is to avoid redundant
-  // intersection computation, since RemoteFrameView must propagate the viewport
-  // intersection rect to the child process (which can't be done using standard
-  // IntersectionObserver behavior). Ultimately, the logic in RemoteFrameView
-  // should be used for both local and remote frames.
-  if (!content_frame_ || !content_frame_->IsLocalFrame())
-    return;
-  if (embedded_content_view_ && !embedded_content_view_->IsLocalFrameView())
-    return;
-  bool visible = entries.back()->intersectionRatio() > 0;
-  blink::mojom::FrameVisibility frame_visibility =
-      blink::mojom::FrameVisibility::kNotRendered;
-  if (embedded_content_view_) {
-    if (embedded_content_view_->IsAttached()) {
-      frame_visibility =
-          visible ? blink::mojom::FrameVisibility::kRenderedInViewport
-                  : blink::mojom::FrameVisibility::kRenderedOutOfViewport;
-    }
-    To<LocalFrameView>(embedded_content_view_.Get())->UpdateVisibility(visible);
-  }
-
-  if (frame_visibility == frame_visibility_)
-    return;
-
-  if (auto* client = To<LocalFrame>(content_frame_.Get())->Client()) {
-    frame_visibility_ = frame_visibility;
-    client->VisibilityChanged(frame_visibility);
-  }
-}
-
-void HTMLFrameOwnerElement::StartVisibilityObserver() {
-  // When this method is called, it indicates that the caller wants to receive
-  // a notification at the next opportunity, even if nothing has changed.
-  // Un-observing and then re-observing will accomplish that.
-  if (visibility_observer_) {
-    visibility_observer_->unobserve(this);
-  } else {
-    visibility_observer_ = IntersectionObserver::Create(
-        {}, {IntersectionObserver::kMinimumThreshold}, &GetDocument(),
-        WTF::BindRepeating(
-            &HTMLFrameOwnerElement::OnViewportIntersectionChanged,
-            WrapWeakPersistent(this)));
-  }
-  visibility_observer_->observe(this);
-}
-
-void HTMLFrameOwnerElement::StopVisibilityObserver() {
-  if (visibility_observer_) {
-    visibility_observer_->disconnect();
-    visibility_observer_ = nullptr;
-  }
-}
-
 void HTMLFrameOwnerElement::Trace(Visitor* visitor) {
   visitor->Trace(content_frame_);
   visitor->Trace(embedded_content_view_);
-  visitor->Trace(visibility_observer_);
   visitor->Trace(lazy_load_frame_observer_);
   HTMLElement::Trace(visitor);
   FrameOwner::Trace(visitor);
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index 2a526e95..a24e1c1 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -39,8 +39,6 @@
 
 class ExceptionState;
 class Frame;
-class IntersectionObserver;
-class IntersectionObserverEntry;
 class LayoutEmbeddedContent;
 class LazyLoadFrameObserver;
 class WebPluginContainerImpl;
@@ -128,9 +126,6 @@
 
   void CancelPendingLazyLoad();
 
-  void StartVisibilityObserver();
-  void StopVisibilityObserver();
-
   void ParseAttribute(const AttributeModificationParams&) override;
 
   void Trace(Visitor*) override;
@@ -183,17 +178,12 @@
   }
 
   bool IsLoadingFrameDefaultEagerEnforced() const;
-  void OnViewportIntersectionChanged(
-      const HeapVector<Member<IntersectionObserverEntry>>& entries);
 
   Member<Frame> content_frame_;
   Member<EmbeddedContentView> embedded_content_view_;
   FramePolicy frame_policy_;
 
-  Member<IntersectionObserver> visibility_observer_;
   Member<LazyLoadFrameObserver> lazy_load_frame_observer_;
-  blink::mojom::FrameVisibility frame_visibility_ =
-      blink::mojom::FrameVisibility::kRenderedInViewport;
   bool should_lazy_load_children_;
 };
 
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index 9b658d9c..56d6f874 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -457,7 +457,8 @@
 void HTMLMetaElement::ProcessViewportContentAttribute(
     const String& content,
     ViewportDescription::Type origin) {
-  DCHECK(!content.IsNull());
+  if (content.IsNull())
+    return;
 
   ViewportData& viewport_data = GetDocument().GetViewportData();
   if (!viewport_data.ShouldOverrideLegacyDescription(origin))
@@ -475,34 +476,48 @@
   viewport_data.SetViewportDescription(description_from_legacy_tag);
 }
 
-void HTMLMetaElement::ProcessSupportedColorSchemes(
-    const AtomicString& content) {
-  if (!RuntimeEnabledFeatures::MetaSupportedColorSchemesEnabled())
+void HTMLMetaElement::ProcessColorScheme(const AtomicString& content) {
+  if (!RuntimeEnabledFeatures::MetaColorSchemeEnabled())
     return;
 
-  SpaceSplitString supported_schemes_strings(content.LowerASCII());
-  size_t count = supported_schemes_strings.size();
-  ColorSchemeSet supported_schemes;
+  SpaceSplitString color_scheme_strings(content.LowerASCII());
+  size_t count = color_scheme_strings.size();
+  ColorSchemeSet color_scheme_set;
   for (size_t i = 0; i < count; i++) {
-    auto color_scheme = supported_schemes_strings[i];
+    auto color_scheme = color_scheme_strings[i];
     if (color_scheme == "light") {
-      supported_schemes.Set(ColorScheme::kLight);
+      color_scheme_set.Set(ColorScheme::kLight);
     } else if (color_scheme == "dark") {
-      supported_schemes.Set(ColorScheme::kDark);
+      color_scheme_set.Set(ColorScheme::kDark);
     }
   }
-  GetDocument().GetStyleEngine().SetSupportedColorSchemes(supported_schemes);
+  GetDocument().GetStyleEngine().SetMetaColorScheme(color_scheme_set);
+}
+
+void HTMLMetaElement::NameRemoved(const AtomicString& name_value) {
+  const AtomicString& content_value = FastGetAttribute(kContentAttr);
+  if (content_value.IsNull())
+    return;
+  if (EqualIgnoringASCIICase(name_value, "theme-color") &&
+      GetDocument().GetFrame()) {
+    GetDocument().GetFrame()->Client()->DispatchDidChangeThemeColor();
+  }
 }
 
 void HTMLMetaElement::ParseAttribute(
     const AttributeModificationParams& params) {
-  if (params.name == kHttpEquivAttr || params.name == kContentAttr) {
-    Process();
-    return;
-  }
-
-  if (params.name != kNameAttr)
+  if (params.name == kNameAttr) {
+    if (IsInDocumentTree())
+      NameRemoved(params.old_value);
+    ProcessContent();
+  } else if (params.name == kContentAttr) {
+    ProcessContent();
+    ProcessHttpEquiv();
+  } else if (params.name == kHttpEquivAttr) {
+    ProcessHttpEquiv();
+  } else {
     HTMLElement::ParseAttribute(params);
+  }
 }
 
 Node::InsertionNotificationRequest HTMLMetaElement::InsertedInto(
@@ -512,7 +527,14 @@
 }
 
 void HTMLMetaElement::DidNotifySubtreeInsertionsToDocument() {
-  Process();
+  ProcessContent();
+  ProcessHttpEquiv();
+}
+
+void HTMLMetaElement::RemovedFrom(ContainerNode& insertion_point) {
+  const AtomicString& name_value = FastGetAttribute(kNameAttr);
+  if (!name_value.IsEmpty())
+    NameRemoved(name_value);
 }
 
 static bool InDocumentHead(HTMLMetaElement* element) {
@@ -522,51 +544,49 @@
   return Traversal<HTMLHeadElement>::FirstAncestor(*element);
 }
 
-void HTMLMetaElement::Process() {
+void HTMLMetaElement::ProcessHttpEquiv() {
   if (!IsInDocumentTree())
     return;
-
-  // All below situations require a content attribute (which can be the empty
-  // string).
   const AtomicString& content_value = FastGetAttribute(kContentAttr);
   if (content_value.IsNull())
     return;
-
-  const AtomicString& name_value = FastGetAttribute(kNameAttr);
-  if (!name_value.IsEmpty()) {
-    if (EqualIgnoringASCIICase(name_value, "viewport"))
-      ProcessViewportContentAttribute(content_value,
-                                      ViewportDescription::kViewportMeta);
-    else if (EqualIgnoringASCIICase(name_value, "referrer"))
-      GetDocument().ParseAndSetReferrerPolicy(
-          content_value, true /* support legacy keywords */);
-    else if (EqualIgnoringASCIICase(name_value, "handheldfriendly") &&
-             EqualIgnoringASCIICase(content_value, "true"))
-      ProcessViewportContentAttribute(
-          "width=device-width", ViewportDescription::kHandheldFriendlyMeta);
-    else if (EqualIgnoringASCIICase(name_value, "mobileoptimized"))
-      ProcessViewportContentAttribute(
-          "width=device-width, initial-scale=1",
-          ViewportDescription::kMobileOptimizedMeta);
-    else if (EqualIgnoringASCIICase(name_value, "theme-color") &&
-             GetDocument().GetFrame())
-      GetDocument().GetFrame()->Client()->DispatchDidChangeThemeColor();
-    else if (EqualIgnoringASCIICase(name_value, "supported-color-schemes"))
-      ProcessSupportedColorSchemes(content_value);
-  }
-
-  // Get the document to process the tag, but only if we're actually part of DOM
-  // tree (changing a meta tag while it's not in the tree shouldn't have any
-  // effect on the document).
-
   const AtomicString& http_equiv_value = FastGetAttribute(kHttpEquivAttr);
   if (http_equiv_value.IsEmpty())
     return;
-
   HttpEquiv::Process(GetDocument(), http_equiv_value, content_value,
                      InDocumentHead(this), this);
 }
 
+void HTMLMetaElement::ProcessContent() {
+  if (!IsInDocumentTree())
+    return;
+
+  const AtomicString& name_value = FastGetAttribute(kNameAttr);
+  if (name_value.IsEmpty())
+    return;
+
+  const AtomicString& content_value = FastGetAttribute(kContentAttr);
+  if (EqualIgnoringASCIICase(name_value, "viewport")) {
+    ProcessViewportContentAttribute(content_value,
+                                    ViewportDescription::kViewportMeta);
+  } else if (EqualIgnoringASCIICase(name_value, "referrer")) {
+    GetDocument().ParseAndSetReferrerPolicy(content_value,
+                                            true /* support legacy keywords */);
+  } else if (EqualIgnoringASCIICase(name_value, "handheldfriendly") &&
+             EqualIgnoringASCIICase(content_value, "true")) {
+    ProcessViewportContentAttribute("width=device-width",
+                                    ViewportDescription::kHandheldFriendlyMeta);
+  } else if (EqualIgnoringASCIICase(name_value, "mobileoptimized")) {
+    ProcessViewportContentAttribute("width=device-width, initial-scale=1",
+                                    ViewportDescription::kMobileOptimizedMeta);
+  } else if (EqualIgnoringASCIICase(name_value, "theme-color") &&
+             GetDocument().GetFrame()) {
+    GetDocument().GetFrame()->Client()->DispatchDidChangeThemeColor();
+  } else if (EqualIgnoringASCIICase(name_value, "color-scheme")) {
+    ProcessColorScheme(content_value);
+  }
+}
+
 WTF::TextEncoding HTMLMetaElement::ComputeEncoding() const {
   HTMLAttributeList attribute_list;
   for (const Attribute& attr : Attributes())
diff --git a/third_party/blink/renderer/core/html/html_meta_element.h b/third_party/blink/renderer/core/html/html_meta_element.h
index bc25b28..75ac90b2 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.h
+++ b/third_party/blink/renderer/core/html/html_meta_element.h
@@ -73,6 +73,7 @@
 
   void ParseAttribute(const AttributeModificationParams&) override;
   InsertionNotificationRequest InsertedInto(ContainerNode&) override;
+  void RemovedFrom(ContainerNode&) override;
   void DidNotifySubtreeInsertionsToDocument() override;
 
   static float ParsePositiveNumber(Document*,
@@ -111,10 +112,12 @@
                                     const String& replacement1,
                                     const String& replacement2);
 
-  void Process();
+  void ProcessContent();
+  void ProcessHttpEquiv();
+  void NameRemoved(const AtomicString& name_value);
   void ProcessViewportContentAttribute(const String& content,
                                        ViewportDescription::Type origin);
-  void ProcessSupportedColorSchemes(const AtomicString& content);
+  void ProcessColorScheme(const AtomicString& content);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_meta_element_test.cc b/third_party/blink/renderer/core/html/html_meta_element_test.cc
index f7e07a4..38e631e7 100644
--- a/third_party/blink/renderer/core/html/html_meta_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element_test.cc
@@ -24,7 +24,7 @@
     PageTestBase::SetUp();
 
     RuntimeEnabledFeatures::SetDisplayCutoutAPIEnabled(true);
-    RuntimeEnabledFeatures::SetMetaSupportedColorSchemesEnabled(true);
+    RuntimeEnabledFeatures::SetMetaColorSchemeEnabled(true);
     RuntimeEnabledFeatures::SetMediaQueryPrefersColorSchemeEnabled(true);
     GetDocument().GetSettings()->SetViewportMetaEnabled(true);
   }
@@ -38,21 +38,19 @@
   }
 
  protected:
-  HTMLMetaElement* CreateSupportedColorSchemesMeta(
-      const AtomicString& content) {
+  HTMLMetaElement* CreateColorSchemeMeta(const AtomicString& content) {
     auto* meta = MakeGarbageCollected<HTMLMetaElement>(GetDocument());
-    meta->setAttribute(html_names::kNameAttr, "supported-color-schemes");
+    meta->setAttribute(html_names::kNameAttr, "color-scheme");
     meta->setAttribute(html_names::kContentAttr, content);
     return meta;
   }
 
-  void SetSupportedColorSchemes(const AtomicString& content) {
-    GetDocument().head()->AppendChild(CreateSupportedColorSchemesMeta(content));
+  void SetColorScheme(const AtomicString& content) {
+    GetDocument().head()->AppendChild(CreateColorSchemeMeta(content));
   }
 
   bool SupportsColorScheme(ColorScheme scheme) const {
-    return GetDocument().GetStyleEngine().GetSupportedColorSchemes().Contains(
-        scheme);
+    return GetDocument().GetStyleEngine().GetMetaColorScheme().Contains(scheme);
   }
 
  private:
@@ -86,20 +84,20 @@
             LoadTestPageAndReturnViewportFit("invalid"));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesProcessing_LastWins) {
+TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_LastWins) {
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
-    <meta name="supported-color-schemes" content="dark">
-    <meta name="supported-color-schemes" content="light">
+    <meta name="color-scheme" content="dark">
+    <meta name="color-scheme" content="light">
   )HTML");
 
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesProcessing_Remove) {
+TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_Remove) {
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
-    <meta name="supported-color-schemes" content="dark">
-    <meta id="last-meta" name="supported-color-schemes" content="light">
+    <meta name="color-scheme" content="dark">
+    <meta id="last-meta" name="color-scheme" content="light">
   )HTML");
 
   GetDocument().getElementById("last-meta")->remove();
@@ -108,33 +106,32 @@
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesProcessing_InsertBefore) {
+TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_InsertBefore) {
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
-    <meta name="supported-color-schemes" content="dark">
+    <meta name="color-scheme" content="dark">
   )HTML");
 
   Element* head = GetDocument().head();
-  head->insertBefore(CreateSupportedColorSchemesMeta("light"),
-                     head->firstChild());
+  head->insertBefore(CreateColorSchemeMeta("light"), head->firstChild());
 
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesProcessing_AppendChild) {
+TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_AppendChild) {
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
-    <meta name="supported-color-schemes" content="dark">
+    <meta name="color-scheme" content="dark">
   )HTML");
 
-  GetDocument().head()->AppendChild(CreateSupportedColorSchemesMeta("light"));
+  GetDocument().head()->AppendChild(CreateColorSchemeMeta("light"));
 
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesProcessing_SetAttribute) {
+TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_SetAttribute) {
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
-    <meta id="meta" name="supported-color-schemes" content="dark">
+    <meta id="meta" name="color-scheme" content="dark">
   )HTML");
 
   GetDocument().getElementById("meta")->setAttribute(html_names::kContentAttr,
@@ -144,53 +141,57 @@
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesProcessing_RemoveAttribute) {
+TEST_F(HTMLMetaElementTest, ColorSchemeProcessing_RemoveAttribute) {
   GetDocument().head()->SetInnerHTMLFromString(R"HTML(
-    <meta id="meta" name="supported-color-schemes" content="dark">
+    <meta id="meta" name="color-scheme" content="dark">
   )HTML");
 
+  EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
+  EXPECT_TRUE(SupportsColorScheme(ColorScheme::kDark));
+
   GetDocument().getElementById("meta")->removeAttribute(
       html_names::kContentAttr);
 
-  EXPECT_TRUE(SupportsColorScheme(ColorScheme::kDark));
+  EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
+  EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesParsing) {
-  SetSupportedColorSchemes("");
+TEST_F(HTMLMetaElementTest, ColorSchemeParsing) {
+  SetColorScheme("");
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 
-  SetSupportedColorSchemes("light");
+  SetColorScheme("light");
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 
-  SetSupportedColorSchemes("dark");
+  SetColorScheme("dark");
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kDark));
 
-  SetSupportedColorSchemes("light dark");
+  SetColorScheme("light dark");
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kDark));
 
-  SetSupportedColorSchemes("light,dark");
+  SetColorScheme("light,dark");
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kDark));
 
-  SetSupportedColorSchemes("light,");
+  SetColorScheme("light,");
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
 
-  SetSupportedColorSchemes(",light");
+  SetColorScheme(",light");
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
 
-  SetSupportedColorSchemes(", light");
+  SetColorScheme(", light");
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kLight));
 
-  SetSupportedColorSchemes("light, dark");
+  SetColorScheme("light, dark");
   EXPECT_FALSE(SupportsColorScheme(ColorScheme::kLight));
   EXPECT_TRUE(SupportsColorScheme(ColorScheme::kDark));
 }
 
-TEST_F(HTMLMetaElementTest, SupportedColorSchemesForcedDarkeningAndMQ) {
+TEST_F(HTMLMetaElementTest, ColorSchemeForcedDarkeningAndMQ) {
   GetDocument().GetSettings()->SetPreferredColorScheme(
       PreferredColorScheme::kDark);
 
@@ -200,13 +201,13 @@
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
   EXPECT_FALSE(media_query->matches());
 
-  SetSupportedColorSchemes("light");
+  SetColorScheme("light");
   EXPECT_FALSE(media_query->matches());
 
-  SetSupportedColorSchemes("dark");
+  SetColorScheme("dark");
   EXPECT_TRUE(media_query->matches());
 
-  SetSupportedColorSchemes("light dark");
+  SetColorScheme("light dark");
   EXPECT_TRUE(media_query->matches());
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme_test.cc b/third_party/blink/renderer/core/layout/layout_theme_test.cc
index 88cb960..3cfc6f5 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_test.cc
@@ -108,9 +108,9 @@
   // Change color scheme to dark.
   GetDocument().GetSettings()->SetPreferredColorScheme(
       PreferredColorScheme::kDark);
-  ColorSchemeSet supported_schemes;
-  supported_schemes.Set(ColorScheme::kDark);
-  GetDocument().GetStyleEngine().SetSupportedColorSchemes(supported_schemes);
+  ColorSchemeSet color_schemes;
+  color_schemes.Set(ColorScheme::kDark);
+  GetDocument().GetStyleEngine().SetMetaColorScheme(color_schemes);
   UpdateAllLifecyclePhasesForTest();
 
   document_element_style = GetDocument().documentElement()->GetComputedStyle();
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 00816e72..a229055 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -126,9 +126,6 @@
   void DidAccessInitialDocument();
 
   DocumentLoader* GetDocumentLoader() const { return document_loader_.Get(); }
-  DocumentLoader* GetProvisionalDocumentLoader() const {
-    return provisional_document_loader_.Get();
-  }
 
   void SetDefersLoading(bool);
 
@@ -196,7 +193,7 @@
   void RestoreScrollPositionAndViewState();
 
   bool HasProvisionalNavigation() const {
-    return client_navigation_.get() || GetProvisionalDocumentLoader();
+    return client_navigation_.get() || provisional_document_loader_.Get();
   }
 
   bool MaybeRenderFallbackContent();
diff --git a/third_party/blink/renderer/core/style/BUILD.gn b/third_party/blink/renderer/core/style/BUILD.gn
index 9972f36..3c0bba4f 100644
--- a/third_party/blink/renderer/core/style/BUILD.gn
+++ b/third_party/blink/renderer/core/style/BUILD.gn
@@ -81,7 +81,6 @@
     "style_inherited_variables.h",
     "style_initial_data.cc",
     "style_initial_data.h",
-    "style_non_inherited_variables.cc",
     "style_non_inherited_variables.h",
     "style_offset_rotation.h",
     "style_path.cc",
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 0ff8a24..cd5fb5b 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -826,12 +826,12 @@
     return true;
 
   for (const AtomicString& property_name : properties) {
-    if (!DataEquivalent(GetVariable(property_name),
-                        other.GetVariable(property_name))) {
+    if (!DataEquivalent(GetVariableData(property_name),
+                        other.GetVariableData(property_name))) {
       return false;
     }
-    if (!DataEquivalent(GetRegisteredVariable(property_name),
-                        other.GetRegisteredVariable(property_name))) {
+    if (!DataEquivalent(GetVariableValue(property_name),
+                        other.GetVariableValue(property_name))) {
       return false;
     }
   }
@@ -1725,22 +1725,22 @@
   MutableInitialDataInternal() = std::move(data);
 }
 
-void ComputedStyle::SetVariable(const AtomicString& name,
-                                scoped_refptr<CSSVariableData> value,
-                                bool is_inherited_property) {
+void ComputedStyle::SetVariableData(const AtomicString& name,
+                                    scoped_refptr<CSSVariableData> value,
+                                    bool is_inherited_property) {
   if (is_inherited_property)
-    MutableInheritedVariables().SetVariable(name, std::move(value));
+    MutableInheritedVariables().SetData(name, std::move(value));
   else
-    MutableNonInheritedVariables().SetVariable(name, std::move(value));
+    MutableNonInheritedVariables().SetData(name, std::move(value));
 }
 
-void ComputedStyle::SetRegisteredVariable(const AtomicString& name,
-                                          const CSSValue* value,
-                                          bool is_inherited_property) {
+void ComputedStyle::SetVariableValue(const AtomicString& name,
+                                     const CSSValue* value,
+                                     bool is_inherited_property) {
   if (is_inherited_property)
-    MutableInheritedVariables().SetRegisteredVariable(name, value);
+    MutableInheritedVariables().SetValue(name, value);
   else
-    MutableNonInheritedVariables().SetRegisteredVariable(name, value);
+    MutableNonInheritedVariables().SetValue(name, value);
 }
 
 static CSSVariableData* GetInitialVariableData(
@@ -1751,7 +1751,8 @@
   return initial_data->GetVariableData(name);
 }
 
-CSSVariableData* ComputedStyle::GetVariable(const AtomicString& name) const {
+CSSVariableData* ComputedStyle::GetVariableData(
+    const AtomicString& name) const {
   if (InheritedVariables()) {
     if (auto data = InheritedVariables()->GetData(name))
       return *data;
@@ -1763,8 +1764,9 @@
   return GetInitialVariableData(name, InitialDataInternal().get());
 }
 
-CSSVariableData* ComputedStyle::GetVariable(const AtomicString& name,
-                                            bool is_inherited_property) const {
+CSSVariableData* ComputedStyle::GetVariableData(
+    const AtomicString& name,
+    bool is_inherited_property) const {
   if (is_inherited_property) {
     if (InheritedVariables()) {
       if (auto data = InheritedVariables()->GetData(name))
@@ -1779,7 +1781,7 @@
   return GetInitialVariableData(name, InitialDataInternal().get());
 }
 
-static const CSSValue* GetInitialRegisteredVariable(
+static const CSSValue* GetInitialVariableValue(
     const AtomicString& name,
     const StyleInitialData* initial_data) {
   if (!initial_data)
@@ -1787,7 +1789,20 @@
   return initial_data->GetVariableValue(name);
 }
 
-const CSSValue* ComputedStyle::GetRegisteredVariable(
+const CSSValue* ComputedStyle::GetVariableValue(
+    const AtomicString& name) const {
+  if (InheritedVariables()) {
+    if (auto value = InheritedVariables()->GetValue(name))
+      return *value;
+  }
+  if (NonInheritedVariables()) {
+    if (auto value = NonInheritedVariables()->GetValue(name))
+      return *value;
+  }
+  return GetInitialVariableValue(name, InitialDataInternal().get());
+}
+
+const CSSValue* ComputedStyle::GetVariableValue(
     const AtomicString& name,
     bool is_inherited_property) const {
   if (is_inherited_property) {
@@ -1801,20 +1816,7 @@
         return *value;
     }
   }
-  return GetInitialRegisteredVariable(name, InitialDataInternal().get());
-}
-
-const CSSValue* ComputedStyle::GetRegisteredVariable(
-    const AtomicString& name) const {
-  if (InheritedVariables()) {
-    if (auto value = InheritedVariables()->GetValue(name))
-      return *value;
-  }
-  if (NonInheritedVariables()) {
-    if (auto value = NonInheritedVariables()->GetValue(name))
-      return *value;
-  }
-  return GetInitialRegisteredVariable(name, InitialDataInternal().get());
+  return GetInitialVariableValue(name, InitialDataInternal().get());
 }
 
 bool ComputedStyle::SetFontDescription(const FontDescription& v) {
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 6b65571..d0d2bc4c 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1138,24 +1138,21 @@
   CORE_EXPORT StyleInheritedVariables* InheritedVariables() const;
   CORE_EXPORT StyleNonInheritedVariables* NonInheritedVariables() const;
 
-  void SetVariable(const AtomicString&,
-                   scoped_refptr<CSSVariableData>,
-                   bool is_inherited_property);
-
-  void SetRegisteredVariable(const AtomicString&,
-                             const CSSValue*,
-                             bool is_inherited_property);
+  CORE_EXPORT void SetVariableData(const AtomicString&,
+                                   scoped_refptr<CSSVariableData>,
+                                   bool is_inherited_property);
+  CORE_EXPORT void SetVariableValue(const AtomicString&,
+                                    const CSSValue*,
+                                    bool is_inherited_property);
 
   // Handles both inherited and non-inherited variables
-  CORE_EXPORT CSSVariableData* GetVariable(const AtomicString&) const;
+  CORE_EXPORT CSSVariableData* GetVariableData(const AtomicString&) const;
+  CSSVariableData* GetVariableData(const AtomicString&,
+                                   bool is_inherited_property) const;
 
-  CSSVariableData* GetVariable(const AtomicString&,
-                               bool is_inherited_property) const;
-
-  const CSSValue* GetRegisteredVariable(const AtomicString&,
-                                        bool is_inherited_property) const;
-
-  const CSSValue* GetRegisteredVariable(const AtomicString&) const;
+  const CSSValue* GetVariableValue(const AtomicString&) const;
+  const CSSValue* GetVariableValue(const AtomicString&,
+                                   bool is_inherited_property) const;
 
   // Animations.
   CSSAnimationData& AccessAnimations();
@@ -2581,8 +2578,8 @@
 
   bool PropertiesEqual(const Vector<CSSPropertyID>& properties,
                        const ComputedStyle& other) const;
-  bool CustomPropertiesEqual(const Vector<AtomicString>& properties,
-                             const ComputedStyle& other) const;
+  CORE_EXPORT bool CustomPropertiesEqual(const Vector<AtomicString>& properties,
+                                         const ComputedStyle& other) const;
 
   static bool ShadowListHasCurrentColor(const ShadowList*);
 
@@ -2663,6 +2660,8 @@
       UpdatePropertySpecificDifferencesCompositingReasonsContainsPaint);
   FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest,
                            UpdatePropertySpecificDifferencesHasAlpha);
+  FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest, CustomPropertiesEqual_Values);
+  FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest, CustomPropertiesEqual_Data);
 };
 
 inline bool ComputedStyle::SetEffectiveZoom(float f) {
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index 59247d5..3647d71 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -7,6 +7,7 @@
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_gradient_value.h"
+#include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/style/clip_path_operation.h"
 #include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
@@ -16,6 +17,8 @@
 
 namespace blink {
 
+using namespace css_test_helpers;
+
 TEST(ComputedStyleTest, ShapeOutsideBoxEqual) {
   ShapeValue* shape1 = ShapeValue::CreateBoxShapeValue(CSSBoxType::kContent);
   ShapeValue* shape2 = ShapeValue::CreateBoxShapeValue(CSSBoxType::kContent);
@@ -425,4 +428,60 @@
   TEST_ANIMATION_FLAG_NO_DIFF(IsRunningBackdropFilterAnimationOnCompositor);
 }
 
+TEST(ComputedStyleTest, CustomPropertiesEqual_Values) {
+  Document* document = Document::CreateForTest();
+  RegisterProperty(*document, "--x", "<length>", "0px", false);
+
+  scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
+  scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
+
+  using UnitType = CSSPrimitiveValue::UnitType;
+
+  const auto* value1 = CSSPrimitiveValue::Create(1.0, UnitType::kPixels);
+  const auto* value2 = CSSPrimitiveValue::Create(2.0, UnitType::kPixels);
+  const auto* value3 = CSSPrimitiveValue::Create(1.0, UnitType::kPixels);
+
+  Vector<AtomicString> properties;
+  properties.push_back("--x");
+
+  style1->SetVariableValue("--x", value1, false);
+  style2->SetVariableValue("--x", value1, false);
+  EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
+
+  style1->SetVariableValue("--x", value1, false);
+  style2->SetVariableValue("--x", value3, false);
+  EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
+
+  style1->SetVariableValue("--x", value1, false);
+  style2->SetVariableValue("--x", value2, false);
+  EXPECT_FALSE(style1->CustomPropertiesEqual(properties, *style2));
+}
+
+TEST(ComputedStyleTest, CustomPropertiesEqual_Data) {
+  Document* document = Document::CreateForTest();
+  RegisterProperty(*document, "--x", "<length>", "0px", false);
+
+  scoped_refptr<ComputedStyle> style1 = ComputedStyle::Create();
+  scoped_refptr<ComputedStyle> style2 = ComputedStyle::Create();
+
+  auto value1 = CreateVariableData("foo");
+  auto value2 = CreateVariableData("bar");
+  auto value3 = CreateVariableData("foo");
+
+  Vector<AtomicString> properties;
+  properties.push_back("--x");
+
+  style1->SetVariableData("--x", value1, false);
+  style2->SetVariableData("--x", value1, false);
+  EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
+
+  style1->SetVariableData("--x", value1, false);
+  style2->SetVariableData("--x", value3, false);
+  EXPECT_TRUE(style1->CustomPropertiesEqual(properties, *style2));
+
+  style1->SetVariableData("--x", value1, false);
+  style2->SetVariableData("--x", value2, false);
+  EXPECT_FALSE(style1->CustomPropertiesEqual(properties, *style2));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/style_inherited_variables.cc b/third_party/blink/renderer/core/style/style_inherited_variables.cc
index b4066380..43fa47c 100644
--- a/third_party/blink/renderer/core/style/style_inherited_variables.cc
+++ b/third_party/blink/renderer/core/style/style_inherited_variables.cc
@@ -26,11 +26,6 @@
   }
 }
 
-CSSVariableData* StyleInheritedVariables::GetVariable(
-    const AtomicString& name) const {
-  return GetData(name).value_or(nullptr);
-}
-
 StyleVariables::OptionalData StyleInheritedVariables::GetData(
     const AtomicString& name) const {
   if (auto data = variables_.GetData(name))
@@ -40,18 +35,6 @@
   return base::nullopt;
 }
 
-void StyleInheritedVariables::SetRegisteredVariable(
-    const AtomicString& name,
-    const CSSValue* parsed_value) {
-  needs_resolution_ = true;
-  variables_.SetValue(name, parsed_value);
-}
-
-const CSSValue* StyleInheritedVariables::RegisteredVariable(
-    const AtomicString& name) const {
-  return GetValue(name).value_or(nullptr);
-}
-
 StyleVariables::OptionalValue StyleInheritedVariables::GetValue(
     const AtomicString& name) const {
   if (auto data = variables_.GetValue(name))
diff --git a/third_party/blink/renderer/core/style/style_inherited_variables.h b/third_party/blink/renderer/core/style/style_inherited_variables.h
index bad95426..774e957 100644
--- a/third_party/blink/renderer/core/style/style_inherited_variables.h
+++ b/third_party/blink/renderer/core/style/style_inherited_variables.h
@@ -32,17 +32,17 @@
     return !(*this == other);
   }
 
-  void SetVariable(const AtomicString& name,
-                   scoped_refptr<CSSVariableData> value) {
+  void SetData(const AtomicString& name, scoped_refptr<CSSVariableData> value) {
     needs_resolution_ =
         needs_resolution_ || (value && value->NeedsVariableResolution());
     variables_.SetData(name, std::move(value));
   }
-  CSSVariableData* GetVariable(const AtomicString& name) const;
   StyleVariables::OptionalData GetData(const AtomicString&) const;
 
-  void SetRegisteredVariable(const AtomicString&, const CSSValue*);
-  const CSSValue* RegisteredVariable(const AtomicString&) const;
+  void SetValue(const AtomicString& name, const CSSValue* value) {
+    needs_resolution_ = true;
+    variables_.SetValue(name, value);
+  }
   StyleVariables::OptionalValue GetValue(const AtomicString&) const;
 
   // Note that not all custom property names returned here necessarily have
diff --git a/third_party/blink/renderer/core/style/style_non_inherited_variables.cc b/third_party/blink/renderer/core/style/style_non_inherited_variables.cc
deleted file mode 100644
index 4f6cafb..0000000
--- a/third_party/blink/renderer/core/style/style_non_inherited_variables.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 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/core/style/style_non_inherited_variables.h"
-
-#include "third_party/blink/renderer/core/style/data_equivalency.h"
-
-namespace blink {
-
-bool StyleNonInheritedVariables::operator==(
-    const StyleNonInheritedVariables& other) const {
-  return variables_ == other.variables_;
-}
-
-CSSVariableData* StyleNonInheritedVariables::GetVariable(
-    const AtomicString& name) const {
-  return variables_.GetData(name).value_or(nullptr);
-}
-
-StyleVariables::OptionalData StyleNonInheritedVariables::GetData(
-    const AtomicString& name) const {
-  return variables_.GetData(name);
-}
-
-void StyleNonInheritedVariables::SetRegisteredVariable(
-    const AtomicString& name,
-    const CSSValue* parsed_value) {
-  needs_resolution_ = true;
-  variables_.SetValue(name, parsed_value);
-}
-
-StyleVariables::OptionalValue StyleNonInheritedVariables::GetValue(
-    const AtomicString& name) const {
-  return variables_.GetValue(name);
-}
-
-HashSet<AtomicString> StyleNonInheritedVariables::GetCustomPropertyNames()
-    const {
-  return variables_.GetNames();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/style/style_non_inherited_variables.h b/third_party/blink/renderer/core/style/style_non_inherited_variables.h
index fc2eb0ae..e68c998 100644
--- a/third_party/blink/renderer/core/style/style_non_inherited_variables.h
+++ b/third_party/blink/renderer/core/style/style_non_inherited_variables.h
@@ -28,27 +28,34 @@
     return base::WrapUnique(new StyleNonInheritedVariables(*this));
   }
 
-  bool operator==(const StyleNonInheritedVariables& other) const;
+  bool operator==(const StyleNonInheritedVariables& other) const {
+    return variables_ == other.variables_;
+  }
+
   bool operator!=(const StyleNonInheritedVariables& other) const {
     return !(*this == other);
   }
 
-  void SetVariable(const AtomicString& name,
-                   scoped_refptr<CSSVariableData> value) {
+  void SetData(const AtomicString& name, scoped_refptr<CSSVariableData> value) {
     needs_resolution_ =
         needs_resolution_ || (value && value->NeedsVariableResolution());
     variables_.SetData(name, std::move(value));
   }
-  CSSVariableData* GetVariable(const AtomicString& name) const;
-  StyleVariables::OptionalData GetData(const AtomicString&) const;
-
-  void SetRegisteredVariable(const AtomicString&, const CSSValue*);
-  const CSSValue* RegisteredVariable(const AtomicString& name) const {
-    return variables_.GetValue(name).value_or(nullptr);
+  StyleVariables::OptionalData GetData(const AtomicString& name) const {
+    return variables_.GetData(name);
   }
-  StyleVariables::OptionalValue GetValue(const AtomicString&) const;
 
-  HashSet<AtomicString> GetCustomPropertyNames() const;
+  void SetValue(const AtomicString& name, const CSSValue* value) {
+    needs_resolution_ = true;
+    variables_.SetValue(name, value);
+  }
+  StyleVariables::OptionalValue GetValue(const AtomicString& name) const {
+    return variables_.GetValue(name);
+  }
+
+  HashSet<AtomicString> GetCustomPropertyNames() const {
+    return variables_.GetNames();
+  }
 
   const StyleVariables::DataMap& Data() const { return variables_.Data(); }
   const StyleVariables::ValueMap& Values() const { return variables_.Values(); }
diff --git a/third_party/blink/renderer/core/style/style_variables_test.cc b/third_party/blink/renderer/core/style/style_variables_test.cc
index e81e20a6..8fa27e17 100644
--- a/third_party/blink/renderer/core/style/style_variables_test.cc
+++ b/third_party/blink/renderer/core/style/style_variables_test.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/core/style/style_variables.h"
 
-#include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
@@ -15,22 +14,7 @@
 
 using namespace css_test_helpers;
 
-class StyleVariablesTest : public PageTestBase {
- public:
-  scoped_refptr<CSSVariableData> CreateData(String s) {
-    auto tokens = CSSTokenizer(s).TokenizeToEOF();
-    CSSParserTokenRange range(tokens);
-    bool is_animation_tainted = false;
-    bool needs_variable_resolution = false;
-    return CSSVariableData::Create(range, is_animation_tainted,
-                                   needs_variable_resolution, KURL(),
-                                   WTF::TextEncoding());
-  }
-
-  const CSSValue* CreateValue(AtomicString s) {
-    return MakeGarbageCollected<CSSCustomIdentValue>(s);
-  }
-};
+class StyleVariablesTest : public PageTestBase {};
 
 TEST_F(StyleVariablesTest, EmptyEqual) {
   StyleVariables vars1;
@@ -41,8 +25,8 @@
 }
 
 TEST_F(StyleVariablesTest, Copy) {
-  auto foo_data = CreateData("foo");
-  const CSSValue* foo_value = CreateValue("foo");
+  auto foo_data = CreateVariableData("foo");
+  const CSSValue* foo_value = CreateCustomIdent("foo");
 
   StyleVariables vars1;
   vars1.SetData("--x", foo_data);
@@ -56,8 +40,8 @@
 
 TEST_F(StyleVariablesTest, GetNames) {
   StyleVariables vars;
-  vars.SetData("--x", CreateData("foo"));
-  vars.SetData("--y", CreateData("bar"));
+  vars.SetData("--x", CreateVariableData("foo"));
+  vars.SetData("--y", CreateVariableData("bar"));
 
   HashSet<AtomicString> names = vars.GetNames();
   EXPECT_EQ(2u, names.size());
@@ -70,15 +54,15 @@
 TEST_F(StyleVariablesTest, IsEmptyData) {
   StyleVariables vars;
   EXPECT_TRUE(vars.IsEmpty());
-  vars.SetData("--x", CreateData("foo"));
+  vars.SetData("--x", CreateVariableData("foo"));
   EXPECT_FALSE(vars.IsEmpty());
 }
 
 TEST_F(StyleVariablesTest, SetData) {
   StyleVariables vars;
 
-  auto foo = CreateData("foo");
-  auto bar = CreateData("bar");
+  auto foo = CreateVariableData("foo");
+  auto bar = CreateVariableData("bar");
 
   EXPECT_FALSE(vars.GetData("--x").has_value());
 
@@ -99,7 +83,7 @@
 }
 
 TEST_F(StyleVariablesTest, SingleDataSamePointer) {
-  auto data = CreateData("foo");
+  auto data = CreateVariableData("foo");
   StyleVariables vars1;
   StyleVariables vars2;
   vars1.SetData("--x", data);
@@ -110,25 +94,25 @@
 TEST_F(StyleVariablesTest, SingleDataSameContent) {
   StyleVariables vars1;
   StyleVariables vars2;
-  vars1.SetData("--x", CreateData("foo"));
-  vars2.SetData("--x", CreateData("foo"));
+  vars1.SetData("--x", CreateVariableData("foo"));
+  vars2.SetData("--x", CreateVariableData("foo"));
   EXPECT_EQ(vars1, vars2);
 }
 
 TEST_F(StyleVariablesTest, SingleDataContentNotEqual) {
   StyleVariables vars1;
   StyleVariables vars2;
-  vars1.SetData("--x", CreateData("foo"));
-  vars2.SetData("--x", CreateData("bar"));
+  vars1.SetData("--x", CreateVariableData("foo"));
+  vars2.SetData("--x", CreateVariableData("bar"));
   EXPECT_NE(vars1, vars2);
 }
 
 TEST_F(StyleVariablesTest, DifferentDataSize) {
   StyleVariables vars1;
   StyleVariables vars2;
-  vars1.SetData("--x", CreateData("foo"));
-  vars2.SetData("--x", CreateData("bar"));
-  vars2.SetData("--y", CreateData("foz"));
+  vars1.SetData("--x", CreateVariableData("foo"));
+  vars2.SetData("--x", CreateVariableData("bar"));
+  vars2.SetData("--y", CreateVariableData("foz"));
   EXPECT_NE(vars1, vars2);
 }
 
@@ -137,15 +121,15 @@
 TEST_F(StyleVariablesTest, IsEmptyValue) {
   StyleVariables vars;
   EXPECT_TRUE(vars.IsEmpty());
-  vars.SetValue("--x", CreateValue("foo"));
+  vars.SetValue("--x", CreateCustomIdent("foo"));
   EXPECT_FALSE(vars.IsEmpty());
 }
 
 TEST_F(StyleVariablesTest, SetValue) {
   StyleVariables vars;
 
-  const CSSValue* foo = CreateValue("foo");
-  const CSSValue* bar = CreateValue("bar");
+  const CSSValue* foo = CreateCustomIdent("foo");
+  const CSSValue* bar = CreateCustomIdent("bar");
 
   EXPECT_FALSE(vars.GetValue("--x").has_value());
 
@@ -166,7 +150,7 @@
 }
 
 TEST_F(StyleVariablesTest, SingleValueSamePointer) {
-  const CSSValue* foo = CreateValue("foo");
+  const CSSValue* foo = CreateCustomIdent("foo");
   StyleVariables vars1;
   StyleVariables vars2;
   vars1.SetValue("--x", foo);
@@ -177,25 +161,25 @@
 TEST_F(StyleVariablesTest, SingleValueSameContent) {
   StyleVariables vars1;
   StyleVariables vars2;
-  vars1.SetValue("--x", CreateValue("foo"));
-  vars2.SetValue("--x", CreateValue("foo"));
+  vars1.SetValue("--x", CreateCustomIdent("foo"));
+  vars2.SetValue("--x", CreateCustomIdent("foo"));
   EXPECT_EQ(vars1, vars2);
 }
 
 TEST_F(StyleVariablesTest, SingleValueContentNotEqual) {
   StyleVariables vars1;
   StyleVariables vars2;
-  vars1.SetValue("--x", CreateValue("foo"));
-  vars2.SetValue("--x", CreateValue("bar"));
+  vars1.SetValue("--x", CreateCustomIdent("foo"));
+  vars2.SetValue("--x", CreateCustomIdent("bar"));
   EXPECT_NE(vars1, vars2);
 }
 
 TEST_F(StyleVariablesTest, DifferentValueSize) {
   StyleVariables vars1;
   StyleVariables vars2;
-  vars1.SetValue("--x", CreateValue("foo"));
-  vars2.SetValue("--x", CreateValue("bar"));
-  vars2.SetValue("--y", CreateValue("foz"));
+  vars1.SetValue("--x", CreateCustomIdent("foo"));
+  vars2.SetValue("--x", CreateCustomIdent("bar"));
+  vars2.SetValue("--y", CreateCustomIdent("foz"));
   EXPECT_NE(vars1, vars2);
 }
 
diff --git a/third_party/blink/renderer/core/testing/null_execution_context.cc b/third_party/blink/renderer/core/testing/null_execution_context.cc
index b887edb7..fedccd4 100644
--- a/third_party/blink/renderer/core/testing/null_execution_context.cc
+++ b/third_party/blink/renderer/core/testing/null_execution_context.cc
@@ -15,7 +15,7 @@
 namespace blink {
 
 NullExecutionContext::NullExecutionContext()
-    : ExecutionContext(v8::Isolate::GetCurrent()),
+    : ExecutionContext(v8::Isolate::GetCurrent(), nullptr),
       tasks_need_pause_(false),
       is_secure_context_(true),
       scheduler_(scheduler::CreateDummyFrameScheduler()) {}
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 51fce807..c1d33b815 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/events/error_event.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_notifier.h"
 #include "third_party/blink/renderer/core/frame/dom_timer_coordinator.h"
 #include "third_party/blink/renderer/core/frame/user_activation.h"
@@ -69,6 +70,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
 #include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -493,7 +495,7 @@
 }
 
 void WorkerGlobalScope::queueMicrotask(V8VoidFunction* callback) {
-  Microtask::EnqueueMicrotask(WTF::Bind(
+  GetAgent()->event_loop()->EnqueueMicrotask(WTF::Bind(
       &V8PersistentCallbackFunction<V8VoidFunction>::InvokeAndReportException,
       WrapPersistent(ToV8PersistentCallbackFunction(callback)), nullptr));
 }
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index b3ac7fa..5875f6e4 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/platform/web_worker_fetch_context.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/dom/events/event_queue.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/loader/loader_factory_for_worker.h"
@@ -171,7 +172,7 @@
     WorkerClients* worker_clients,
     scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context,
     WorkerReportingProxy& reporting_proxy)
-    : ExecutionContext(isolate),
+    : ExecutionContext(isolate, Agent::CreateForWorkerOrWorklet(isolate)),
       name_(name),
       parent_devtools_token_(parent_devtools_token),
       worker_clients_(worker_clients),
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index f45d1768..98484606 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/inspector/console_message_storage.h"
 #include "third_party/blink/renderer/core/inspector/inspector_task_runner.h"
 #include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
@@ -52,6 +53,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
 #include "third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 #include "third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
@@ -258,7 +260,18 @@
 
 void WorkerThread::DidProcessTask(const base::PendingTask& pending_task) {
   DCHECK(IsCurrentThread());
+
+  // TODO(tzik): Move this to WorkerThreadScheduler::OnTaskCompleted(), so that
+  // metrics for microtasks are counted as a part of the preceding task.
+  GlobalScope()->GetAgent()->event_loop()->PerformMicrotaskCheckpoint();
+
+  // Microtask::PerformCheckpoint() runs microtasks and its completion hooks for
+  // the default microtask queue. The default queue may contain the microtasks
+  // queued by V8 itself, and legacy blink::MicrotaskQueue::EnqueueMicrotask.
+  // The completion hook contains IndexedDB clean-up task, as described at
+  // https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint
   // TODO(tzik): Move rejected promise handling to EventLoop.
+
   GlobalScope()->ScriptController()->GetRejectedPromises()->ProcessQueue();
   if (GlobalScope()->IsClosing()) {
     // This WorkerThread will eventually be requested to terminate.
@@ -387,32 +400,42 @@
       forcible_termination_delay_);
 }
 
-bool WorkerThread::ShouldTerminateScriptExecution() {
+WorkerThread::TerminationState WorkerThread::ShouldTerminateScriptExecution() {
   DCHECK_CALLED_ON_VALID_THREAD(parent_thread_checker_);
   switch (thread_state_) {
     case ThreadState::kNotStarted:
       // Shutdown sequence will surely start during initialization sequence
       // on the worker thread. Don't have to schedule a termination task.
-      return false;
+      return TerminationState::kTerminationUnnecessary;
     case ThreadState::kRunning:
       // Terminating during debugger task may lead to crash due to heavy use
       // of v8 api in debugger. Any debugger task is guaranteed to finish, so
       // we can wait for the completion.
-      return !debugger_task_counter_;
+      return debugger_task_counter_ > 0 ? TerminationState::kPostponeTerminate
+                                        : TerminationState::kTerminate;
     case ThreadState::kReadyToShutdown:
-      // Shutdown sequence will surely start soon. Don't have to schedule a
-      // termination task.
-      return false;
+      // Shutdown sequence might have started in a nested event loop but
+      // JS might continue running after it exits the nested loop.
+      return exit_code_ == ExitCode::kNotTerminated
+                 ? TerminationState::kTerminate
+                 : TerminationState::kTerminationUnnecessary;
   }
   NOTREACHED();
-  return false;
+  return TerminationState::kTerminationUnnecessary;
 }
 
 void WorkerThread::EnsureScriptExecutionTerminates(ExitCode exit_code) {
   DCHECK_CALLED_ON_VALID_THREAD(parent_thread_checker_);
   MutexLocker lock(mutex_);
-  if (!ShouldTerminateScriptExecution())
-    return;
+  switch (ShouldTerminateScriptExecution()) {
+    case TerminationState::kTerminationUnnecessary:
+      return;
+    case TerminationState::kTerminate:
+      break;
+    case TerminationState::kPostponeTerminate:
+      ScheduleToTerminateScriptExecution();
+      return;
+  }
 
   DCHECK(exit_code == ExitCode::kSyncForciblyTerminated ||
          exit_code == ExitCode::kAsyncForciblyTerminated);
@@ -565,8 +588,6 @@
     if (thread_state_ == ThreadState::kReadyToShutdown)
       return;
     SetThreadState(ThreadState::kReadyToShutdown);
-    if (exit_code_ == ExitCode::kNotTerminated)
-      SetExitCode(ExitCode::kGracefullyTerminated);
   }
 
   if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::From(GetIsolate()))
@@ -587,13 +608,13 @@
 
 void WorkerThread::PerformShutdownOnWorkerThread() {
   DCHECK(IsCurrentThread());
-#if DCHECK_IS_ON()
   {
     MutexLocker lock(mutex_);
     DCHECK(requested_to_terminate_);
     DCHECK_EQ(ThreadState::kReadyToShutdown, thread_state_);
+    if (exit_code_ == ExitCode::kNotTerminated)
+      SetExitCode(ExitCode::kGracefullyTerminated);
   }
-#endif
 
   // When child workers are present, wait for them to shutdown before shutting
   // down this thread. ChildThreadTerminatedOnWorkerThread() is responsible
diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h
index 9f7fb0f..c3914fc 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.h
+++ b/third_party/blink/renderer/core/workers/worker_thread.h
@@ -277,9 +277,16 @@
   // normal shutdown sequence does not start within a certain time period.
   void ScheduleToTerminateScriptExecution();
 
+  enum class TerminationState {
+    kTerminate,
+    kPostponeTerminate,
+    kTerminationUnnecessary,
+  };
+
   // Returns true if we should synchronously terminate the script execution so
   // that a shutdown task can be handled by the thread event loop.
-  bool ShouldTerminateScriptExecution() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+  TerminationState ShouldTerminateScriptExecution()
+      EXCLUSIVE_LOCKS_REQUIRED(mutex_);
 
   // Terminates worker script execution if the worker thread is running and not
   // already shutting down. Does not terminate if a debugger task is running,
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test.cc b/third_party/blink/renderer/core/workers/worker_thread_test.cc
index 56ce1311..1d3f091 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread_test.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/inspector/inspector_task_runner.h"
 #include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
+#include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
 #include "third_party/blink/renderer/core/script/script.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
@@ -52,6 +53,13 @@
   waitable_event->Signal();
 }
 
+void PauseExecution(v8::Isolate* isolate, void* data) {
+  WorkerThread* worker_thread = static_cast<WorkerThread*>(data);
+  WorkerThreadDebugger* debugger =
+      WorkerThreadDebugger::From(worker_thread->GetIsolate());
+  debugger->PauseWorkerOnStart(worker_thread);
+}
+
 // This helper managers a child worker thread and a reporting proxy
 // and ensures they stay alive for the duration of the test. The struct
 // is created on the main thread, but its members are created and
@@ -221,16 +229,25 @@
   MutexLocker dummy_lock(worker_thread_->mutex_);
 
   EXPECT_EQ(ThreadState::kNotStarted, worker_thread_->thread_state_);
-  EXPECT_FALSE(worker_thread_->ShouldTerminateScriptExecution());
+  EXPECT_EQ(WorkerThread::TerminationState::kTerminationUnnecessary,
+            worker_thread_->ShouldTerminateScriptExecution());
 
   worker_thread_->SetThreadState(ThreadState::kRunning);
-  EXPECT_TRUE(worker_thread_->ShouldTerminateScriptExecution());
+  EXPECT_EQ(WorkerThread::TerminationState::kTerminate,
+            worker_thread_->ShouldTerminateScriptExecution());
+
+  worker_thread_->debugger_task_counter_ = 1;
+  EXPECT_EQ(WorkerThread::TerminationState::kPostponeTerminate,
+            worker_thread_->ShouldTerminateScriptExecution());
+  worker_thread_->debugger_task_counter_ = 0;
 
   worker_thread_->SetThreadState(ThreadState::kReadyToShutdown);
-  EXPECT_FALSE(worker_thread_->ShouldTerminateScriptExecution());
+  EXPECT_EQ(WorkerThread::TerminationState::kTerminate,
+            worker_thread_->ShouldTerminateScriptExecution());
 
-  // This is necessary to satisfy DCHECK in the dtor of WorkerThread.
   worker_thread_->SetExitCode(ExitCode::kGracefullyTerminated);
+  EXPECT_EQ(WorkerThread::TerminationState::kTerminationUnnecessary,
+            worker_thread_->ShouldTerminateScriptExecution());
 }
 
 TEST_F(WorkerThreadTest, AsyncTerminate_OnIdle) {
@@ -407,15 +424,14 @@
   EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
 
   // Wait until the task runs. It shouldn't terminate the script execution
-  // because of the running debugger task.
+  // because of the running debugger task but it should get reposted.
   test::RunDelayedTasks(kDelay);
-  EXPECT_FALSE(IsForcibleTerminationTaskScheduled());
-  EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
-
-  // Forcible script termination request should also respect the running
-  // debugger task.
-  worker_thread_->EnsureScriptExecutionTerminates(
-      ExitCode::kSyncForciblyTerminated);
+  {
+    MutexLocker lock(worker_thread_->mutex_);
+    EXPECT_EQ(WorkerThread::TerminationState::kPostponeTerminate,
+              worker_thread_->ShouldTerminateScriptExecution());
+  }
+  EXPECT_TRUE(IsForcibleTerminationTaskScheduled());
   EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
 
   // Resume the debugger task. Shutdown starts after that.
@@ -454,15 +470,14 @@
   EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
 
   // Wait until the task runs. It shouldn't terminate the script execution
-  // because of the running debugger task.
+  // because of the running debugger task but it should get reposted.
   test::RunDelayedTasks(kDelay);
-  EXPECT_FALSE(IsForcibleTerminationTaskScheduled());
-  EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
-
-  // Forcible script termination request should also respect the running
-  // debugger task.
-  worker_thread_->EnsureScriptExecutionTerminates(
-      ExitCode::kSyncForciblyTerminated);
+  {
+    MutexLocker lock(worker_thread_->mutex_);
+    EXPECT_EQ(WorkerThread::TerminationState::kPostponeTerminate,
+              worker_thread_->ShouldTerminateScriptExecution());
+  }
+  EXPECT_TRUE(IsForcibleTerminationTaskScheduled());
   EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
 
   // Resume the debugger task. Shutdown starts after that.
@@ -495,4 +510,26 @@
   waitable_event.Wait();
 }
 
+// Tests terminating a worker when debugger is paused.
+TEST_F(WorkerThreadTest, TerminateWhileWorkerPausedByDebugger) {
+  constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10);
+  SetForcibleTerminationDelay(kDelay);
+
+  ExpectReportingCallsForWorkerForciblyTerminated();
+  StartWithSourceCodeNotToFinish();
+  reporting_proxy_->WaitUntilScriptEvaluation();
+
+  worker_thread_->GetIsolate()->RequestInterrupt(&PauseExecution,
+                                                 worker_thread_.get());
+
+  // Terminate() schedules a forcible termination task.
+  worker_thread_->Terminate();
+  EXPECT_TRUE(IsForcibleTerminationTaskScheduled());
+  EXPECT_EQ(ExitCode::kNotTerminated, GetExitCode());
+
+  test::RunDelayedTasks(kDelay);
+  worker_thread_->WaitForShutdownForTesting();
+  EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager.cc b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
index 66ef19f3..9f64d217 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
@@ -7,9 +7,9 @@
 #include <memory>
 
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_client.h"
 #include "third_party/blink/public/platform/modules/push_messaging/web_push_provider.h"
-#include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc b/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc
index 7f0321f..51e7d3b 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager_test.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/modules/push_messaging/push_manager.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
@@ -47,8 +47,8 @@
     sender_key[i] = kApplicationServerKey[i];
   sender_key[kApplicationServerKeyLength] = 0x0;
   EXPECT_EQ(reinterpret_cast<const char*>(sender_key),
-            output.application_server_key.Latin1());
-  EXPECT_FALSE(output.application_server_key.IsEmpty());
+            output.application_server_key);
+  EXPECT_FALSE(output.application_server_key.empty());
 }
 
 TEST(PushManagerTest, InvalidSenderKeyLength) {
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
index c635971..c1a3f00 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription_options.cc
@@ -4,33 +4,31 @@
 
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
 
-#include "third_party/blink/public/platform/modules/push_messaging/web_push_subscription_options.h"
-#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/common/push_messaging/web_push_subscription_options.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options_init.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 namespace {
 
 const int kMaxApplicationServerKeyLength = 255;
 
-String BufferSourceToString(
+std::string BufferSourceToString(
     const ArrayBufferOrArrayBufferView& application_server_key,
     ExceptionState& exception_state) {
-  unsigned char* input;
+  char* input;
   int length;
   // Convert the input array into a string of bytes.
   if (application_server_key.IsArrayBuffer()) {
-    input = static_cast<unsigned char*>(
-        application_server_key.GetAsArrayBuffer()->Data());
+    input =
+        static_cast<char*>(application_server_key.GetAsArrayBuffer()->Data());
     length = application_server_key.GetAsArrayBuffer()->ByteLength();
   } else if (application_server_key.IsArrayBufferView()) {
-    input = static_cast<unsigned char*>(
+    input = static_cast<char*>(
         application_server_key.GetAsArrayBufferView().View()->buffer()->Data());
     length = application_server_key.GetAsArrayBufferView()
                  .View()
@@ -38,7 +36,7 @@
                  ->ByteLength();
   } else {
     NOTREACHED();
-    return String();
+    return std::string();
   }
 
   // Check the validity of the sender info. It must either be a 65-byte
@@ -47,16 +45,16 @@
   const bool is_vapid = length == 65 && *input == 0x04;
   const bool is_sender_id =
       length > 0 && length < kMaxApplicationServerKeyLength &&
-      (std::find_if_not(input, input + length,
-                        &WTF::IsASCIIDigit<unsigned char>) == input + length);
+      (std::find_if_not(input, input + length, &WTF::IsASCIIDigit<char>) ==
+       input + length);
 
   if (is_vapid || is_sender_id)
-    return WebString::FromLatin1(input, length);
+    return std::string(input, length);
 
   exception_state.ThrowDOMException(
       DOMExceptionCode::kInvalidAccessError,
       "The provided applicationServerKey is not valid.");
-  return String();
+  return std::string();
 }
 
 }  // namespace
@@ -78,7 +76,7 @@
     const WebPushSubscriptionOptions& options)
     : user_visible_only_(options.user_visible_only),
       application_server_key_(DOMArrayBuffer::Create(
-          options.application_server_key.Latin1().data(),
+          options.application_server_key.data(),
           SafeCast<unsigned>(options.application_server_key.length()))) {}
 
 void PushSubscriptionOptions::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
index c8ab772..c81e4fd 100644
--- a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
@@ -10,9 +10,11 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
+#include "third_party/blink/renderer/platform/scheduler/public/event_loop.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
@@ -77,6 +79,9 @@
     // "Upon fulfillment or rejection of f, queue a microtask to run these
     // substeps: Decrement the pending promises count by one."
 
+    scoped_refptr<scheduler::EventLoop> event_loop =
+        ExecutionContext::From(GetScriptState())->GetAgent()->event_loop();
+
     // At this time point the microtask A running resolve/reject function of
     // this promise has already been queued, in order to allow microtask A to
     // call waitUntil, we enqueue another microtask B to delay the promise
@@ -85,13 +90,13 @@
     // will run after B so C maybe can't call waitUntil if there has no any
     // extend lifetime promise at that time.
     if (resolve_type_ == kRejected) {
-      Microtask::EnqueueMicrotask(
+      event_loop->EnqueueMicrotask(
           WTF::Bind(&WaitUntilObserver::OnPromiseRejected,
                     WrapPersistent(observer_.Get())));
       value =
           ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue();
     } else {
-      Microtask::EnqueueMicrotask(
+      event_loop->EnqueueMicrotask(
           WTF::Bind(&WaitUntilObserver::OnPromiseFulfilled,
                     WrapPersistent(observer_.Get())));
     }
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data.h b/third_party/blink/renderer/platform/fonts/font_platform_data.h
index 8df5288..6e4bf97a 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.h
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.h
@@ -139,7 +139,6 @@
   enum Flags {
     kAntiAlias = 1 << 0,
     kSubpixelsAntiAlias = 1 << 1,
-    kSubpixelMetrics = 1 << 2,
   };
   int FontFlags() const { return font_flags_; }
 #endif
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
index f3a7b80..e42f3193 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
@@ -199,6 +199,11 @@
   skfont->setSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
   skfont->setSubpixel(should_subpixel_position);
 
+  // CoreText always provides linear metrics if it can, so the linear metrics
+  // flag setting doesn't affect typefaces backed by CoreText. However, it
+  // does affect FreeType backed typefaces, so set the flag for consistency.
+  skfont->setLinearMetrics(should_subpixel_position);
+
   // When rendering using CoreGraphics, disable hinting when
   // webkit-font-smoothing:antialiased or text-rendering:geometricPrecision is
   // used.  See crbug.com/152304
diff --git a/third_party/blink/renderer/platform/fonts/web_font_render_style.cc b/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
index be4c64d..a8b68cd6 100644
--- a/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
+++ b/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
@@ -117,6 +117,8 @@
       (sk_hint_style != SkFontHinting::kFull || device_scale_factor > 1.0f);
 
   font->setSubpixel(force_subpixel_positioning || use_subpixel_positioning);
+
+  font->setLinearMetrics(use_subpixel_positioning == 1);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc b/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
index 5df0159..36a553e 100644
--- a/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
+++ b/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
@@ -53,7 +53,6 @@
   } else {
     font->setEdging(SkFont::Edging::kAlias);
   }
-  font->setSubpixel(SkToBool(font_flags & kSubpixelMetrics));
 
   // Only use sub-pixel positioning if anti aliasing is enabled. Otherwise,
   // without font smoothing, subpixel text positioning leads to uneven spacing
diff --git a/third_party/blink/renderer/platform/heap/gc_info.cc b/third_party/blink/renderer/platform/heap/gc_info.cc
index 6e943a75..3f4f9ea 100644
--- a/third_party/blink/renderer/platform/heap/gc_info.cc
+++ b/third_party/blink/renderer/platform/heap/gc_info.cc
@@ -51,7 +51,7 @@
 
 uint32_t GCInfoTable::EnsureGCInfoIndex(
     const GCInfo* gc_info,
-    std::atomic_uint32_t* gc_info_index_slot) {
+    std::atomic<uint32_t>* gc_info_index_slot) {
   DCHECK(gc_info);
   DCHECK(gc_info_index_slot);
 
diff --git a/third_party/blink/renderer/platform/heap/gc_info.h b/third_party/blink/renderer/platform/heap/gc_info.h
index 2109246a..42a9d91d 100644
--- a/third_party/blink/renderer/platform/heap/gc_info.h
+++ b/third_party/blink/renderer/platform/heap/gc_info.h
@@ -62,7 +62,7 @@
     return info;
   }
 
-  uint32_t EnsureGCInfoIndex(const GCInfo*, std::atomic_uint32_t*);
+  uint32_t EnsureGCInfoIndex(const GCInfo*, std::atomic<std::uint32_t>*);
 
   uint32_t GcInfoIndex() const { return current_index_; }
 
@@ -107,7 +107,7 @@
         NameTrait<T>::GetName, std::is_polymorphic<T>::value};
     // This is more complicated than using threadsafe initialization, but this
     // is instantiated many times (once for every GC type).
-    static std::atomic_uint32_t gc_info_index{0};
+    static std::atomic<std::uint32_t> gc_info_index{0};
     uint32_t index = gc_info_index.load(std::memory_order_acquire);
     if (!index)
       index = GCInfoTable::Get().EnsureGCInfoIndex(&kGcInfo, &gc_info_index);
diff --git a/third_party/blink/renderer/platform/heap/gc_info_test.cc b/third_party/blink/renderer/platform/heap/gc_info_test.cc
index a263ac1..d6786ef3 100644
--- a/third_party/blink/renderer/platform/heap/gc_info_test.cc
+++ b/third_party/blink/renderer/platform/heap/gc_info_test.cc
@@ -16,7 +16,7 @@
 TEST(GCInfoTest, ResizeToMaxIndex) {
   GCInfoTable table;
   GCInfo info = {nullptr, nullptr, nullptr, false};
-  std::atomic_uint32_t slot{0};
+  std::atomic<std::uint32_t> slot{0};
   for (uint32_t i = 0; i < (GCInfoTable::kMaxIndex - 1); i++) {
     slot = 0;
     uint32_t index = table.EnsureGCInfoIndex(&info, &slot);
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 508e2f9..909642dc 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -212,39 +212,6 @@
     return weak_callback_worklist_.get();
   }
 
-  // Is the finalizable GC object still alive, but slated for lazy sweeping?
-  // If a lazy sweep is in progress, returns true if the object was found
-  // to be not reachable during the marking phase, but it has yet to be swept
-  // and finalized. The predicate returns false in all other cases.
-  //
-  // Holding a reference to an already-dead object is not a valid state
-  // to be in; willObjectBeLazilySwept() has undefined behavior if passed
-  // such a reference.
-  template <typename T>
-  NO_SANITIZE_ADDRESS static bool WillObjectBeLazilySwept(
-      const T* object_pointer) {
-    static_assert(IsGarbageCollectedType<T>::value,
-                  "only objects deriving from GarbageCollected can be used.");
-    BasePage* page = PageFromObject(object_pointer);
-    // Page has been swept and it is still alive.
-    if (page->HasBeenSwept())
-      return false;
-    DCHECK(page->Arena()->GetThreadState()->IsSweepingInProgress());
-
-    // If marked and alive, the object hasn't yet been swept..and won't
-    // be once its page is processed.
-    if (ThreadHeap::IsHeapObjectAlive(const_cast<T*>(object_pointer)))
-      return false;
-
-    if (page->IsLargeObjectPage())
-      return true;
-
-    // If the object is unmarked, it may be on the page currently being
-    // lazily swept.
-    return page->Arena()->WillObjectBeLazilySwept(
-        page, const_cast<T*>(object_pointer));
-  }
-
   // Register an ephemeron table for fixed-point iteration.
   void RegisterWeakTable(void* container_object,
                          EphemeronCallback);
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.cc b/third_party/blink/renderer/platform/heap/heap_allocator.cc
index 748fe6ec..a81beba7 100644
--- a/third_party/blink/renderer/platform/heap/heap_allocator.cc
+++ b/third_party/blink/renderer/platform/heap/heap_allocator.cc
@@ -6,30 +6,57 @@
 
 namespace blink {
 
+namespace {
+
+struct BackingModifier {
+  bool can_modify;
+  BasePage* const page;
+  HeapObjectHeader* const header;
+};
+
+BackingModifier CanFreeOrShrinkBacking(ThreadState* const state,
+                                       void* address) {
+  // - |SweepForbidden| protects against modifying objects from destructors.
+  // - |in_atomic_pause| protects against modifying objects from within the GC.
+  // This can
+  //   e.g. happen when hash table buckets that have containers inlined are
+  //   freed during weakness processing.
+  // - |IsMarkingInProgress| protects against incremental marking which may have
+  //   registered callbacks.
+  if (state->SweepForbidden() || state->in_atomic_pause() ||
+      state->IsMarkingInProgress())
+    return {false, nullptr, nullptr};
+
+  // - Don't adjust large objects because their page is never reused.
+  // - Don't free backings allocated on other threads.
+  BasePage* page = PageFromObject(address);
+  if (page->IsLargeObjectPage() || page->Arena()->GetThreadState() != state)
+    return {false, nullptr, nullptr};
+
+  HeapObjectHeader* const header = HeapObjectHeader::FromPayload(address);
+  // - Guards against pages that have not been swept. Technically, it should be
+  // fine to modify those backings. We bail out to maintain the invariant that
+  // no marked backing is modified.
+  if (header->IsMarked())
+    return {false, nullptr, nullptr};
+  return {true, page, header};
+}
+
+}  // namespace
+
 void HeapAllocator::BackingFree(void* address) {
   if (!address)
     return;
 
-  ThreadState* state = ThreadState::Current();
-  if (state->SweepForbidden())
-    return;
-  DCHECK(!state->in_atomic_pause());
-
-  // Don't promptly free large objects because their page is never reused.
-  // Don't free backings allocated on other threads.
-  BasePage* page = PageFromObject(address);
-  if (page->IsLargeObjectPage() || page->Arena()->GetThreadState() != state)
+  ThreadState* const state = ThreadState::Current();
+  BackingModifier result = CanFreeOrShrinkBacking(state, address);
+  if (!result.can_modify)
     return;
 
-  HeapObjectHeader* header = HeapObjectHeader::FromPayload(address);
-  // Don't promptly free marked backing as they may be registered on the marking
-  // callback stack. The effect on non incremental marking GCs is that promptly
-  // free is disabled for surviving backings during lazy sweeping.
-  if (header->IsMarked())
-    return;
-  state->Heap().PromptlyFreed(header->GcInfoIndex());
-  static_cast<NormalPage*>(page)->ArenaForNormalPage()->PromptlyFreeObject(
-      header);
+  state->Heap().PromptlyFreed(result.header->GcInfoIndex());
+  static_cast<NormalPage*>(result.page)
+      ->ArenaForNormalPage()
+      ->PromptlyFreeObject(result.header);
 }
 
 void HeapAllocator::FreeVectorBacking(void* address) {
@@ -41,10 +68,7 @@
 }
 
 void HeapAllocator::FreeHashTableBacking(void* address) {
-  // When incremental marking is enabled weak callbacks may have been
-  // registered.
-  if (!ThreadState::Current()->IsMarkingInProgress())
-    BackingFree(address);
+  BackingFree(address);
 }
 
 bool HeapAllocator::BackingExpand(void* address, size_t new_size) {
@@ -92,39 +116,27 @@
 
   DCHECK_LT(quantized_shrunk_size, quantized_current_size);
 
-  ThreadState* state = ThreadState::Current();
-  if (state->SweepForbidden())
+  ThreadState* const state = ThreadState::Current();
+  BackingModifier result = CanFreeOrShrinkBacking(state, address);
+  if (!result.can_modify)
     return false;
-  DCHECK(!state->in_atomic_pause());
+
   DCHECK(state->IsAllocationAllowed());
   DCHECK_EQ(&state->Heap(), &ThreadState::FromObject(address)->Heap());
 
-  // FIXME: Support shrink for large objects.
-  // Don't shrink backings allocated on other threads.
-  BasePage* page = PageFromObject(address);
-  if (page->IsLargeObjectPage() || page->Arena()->GetThreadState() != state)
-    return false;
-
-  HeapObjectHeader* header = HeapObjectHeader::FromPayload(address);
-
-  // Compaction may register slots for compaction in slots of vector backings.
-  // E.g., when vectors are embedded in each other. To avoid dereferincing a
-  // broken slot, bail out on already marked backings.
-  if (header->IsMarked())
-    return false;
-
-  NormalPageArena* arena = static_cast<NormalPage*>(page)->ArenaForNormalPage();
+  NormalPageArena* arena =
+      static_cast<NormalPage*>(result.page)->ArenaForNormalPage();
   // We shrink the object only if the shrinking will make a non-small
   // prompt-free block.
   // FIXME: Optimize the threshold size.
   if (quantized_current_size <= quantized_shrunk_size +
                                     sizeof(HeapObjectHeader) +
                                     sizeof(void*) * 32 &&
-      !arena->IsObjectAllocatedAtAllocationPoint(header))
+      !arena->IsObjectAllocatedAtAllocationPoint(result.header))
     return true;
 
   bool succeeded_at_allocation_point =
-      arena->ShrinkObject(header, quantized_shrunk_size);
+      arena->ShrinkObject(result.header, quantized_shrunk_size);
   if (succeeded_at_allocation_point)
     state->Heap().AllocationPointAdjusted(arena->ArenaIndex());
   return true;
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index b270cd7..03b8e19e 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -18,8 +18,6 @@
 
 namespace blink {
 
-bool HeapCompact::force_compaction_gc_ = false;
-
 // The real worker behind heap compaction, recording references to movable
 // objects ("slots".) When the objects end up being compacted and moved,
 // relocate() will adjust the slots to point to the new location of the
@@ -326,56 +324,33 @@
   return *fixups_;
 }
 
-bool HeapCompact::ShouldCompact(ThreadHeap* heap,
-                                BlinkGC::StackState stack_state,
+bool HeapCompact::ShouldCompact(BlinkGC::StackState stack_state,
                                 BlinkGC::MarkingType marking_type,
                                 BlinkGC::GCReason reason) {
-#if !ENABLE_HEAP_COMPACTION
-  return false;
-#else
-  if (!RuntimeEnabledFeatures::HeapCompactionEnabled())
+  if (!RuntimeEnabledFeatures::HeapCompactionEnabled()) {
     return false;
-
-  LOG_HEAP_COMPACTION() << "shouldCompact(): gc=" << static_cast<int>(reason)
-                        << " count=" << gc_count_since_last_compaction_
-                        << " free=" << free_list_size_;
-  gc_count_since_last_compaction_++;
-
-  // If the GCing thread requires a stack scan, do not compact.
-  // Why? Should the stack contain an iterator pointing into its
-  // associated backing store, its references wouldn't be
-  // correctly relocated.
-  if (stack_state == BlinkGC::kHeapPointersOnStack)
-    return false;
-
-  if (reason == BlinkGC::GCReason::kForcedGCForTesting) {
-    UpdateHeapResidency();
-    return force_compaction_gc_;
   }
 
-  // Compaction enable rules:
-  //  - It's been a while since the last time.
-  //  - "Considerable" amount of heap memory is bound up in freelist
-  //    allocations. For now, use a fixed limit irrespective of heap
-  //    size.
-  //
-  // As this isn't compacting all arenas, the cost of doing compaction
-  // isn't a worry as it will additionally only be done by idle GCs.
-  // TODO: add some form of compaction overhead estimate to the marking
-  // time estimate.
+  DCHECK_NE(BlinkGC::MarkingType::kTakeSnapshot, marking_type);
+  if (marking_type == BlinkGC::MarkingType::kAtomicMarking &&
+      stack_state == BlinkGC::StackState::kHeapPointersOnStack) {
+    // The following check ensures that tests that want to test compaction are
+    // not interrupted by garbage collections that cannot use compaction.
+    CHECK(!force_for_next_gc_);
+    return false;
+  }
 
   UpdateHeapResidency();
 
-#if STRESS_TEST_HEAP_COMPACTION
-  // Exercise the handling of object movement by compacting as
-  // often as possible.
-  return true;
-#else
-  return force_compaction_gc_ || (gc_count_since_last_compaction_ >
-                                      kGCCountSinceLastCompactionThreshold &&
-                                  free_list_size_ > kFreeListSizeThreshold);
-#endif
-#endif
+  if (force_for_next_gc_) {
+    return true;
+  }
+
+  // TODO(mlippautz): Only enable compaction when doing garbage collections that
+  // should aggressively reduce memory footprint.
+  return gc_count_since_last_compaction_ >
+             kGCCountSinceLastCompactionThreshold &&
+         free_list_size_ > kFreeListSizeThreshold;
 }
 
 void HeapCompact::Initialize(ThreadState* state) {
@@ -384,7 +359,7 @@
   do_compact_ = true;
   fixups_.reset();
   gc_count_since_last_compaction_ = 0;
-  force_compaction_gc_ = false;
+  force_for_next_gc_ = false;
 }
 
 void HeapCompact::RegisterMovingObjectReference(MovableReference* slot) {
@@ -498,10 +473,4 @@
   Fixups().AddCompactingPage(page);
 }
 
-bool HeapCompact::ScheduleCompactionGCForTesting(bool value) {
-  bool current = force_compaction_gc_;
-  force_compaction_gc_ = value;
-  return current;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.h b/third_party/blink/renderer/platform/heap/heap_compact.h
index 20f8bfa..5fe668b0 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.h
+++ b/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -18,9 +18,6 @@
 
 // Compaction-specific debug switches:
 
-// Set to 0 to prevent compaction GCs, disabling the heap compaction feature.
-#define ENABLE_HEAP_COMPACTION 1
-
 // Emit debug info during compaction.
 #define DEBUG_HEAP_COMPACTION 0
 
@@ -28,41 +25,32 @@
 // 0 - disabled, 1 - minimal, 2 - verbose.
 #define DEBUG_HEAP_FREELIST 0
 
-// Log the amount of time spent compacting.
-#define DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME 0
-
-// Compact during all idle + precise GCs; for debugging.
-#define STRESS_TEST_HEAP_COMPACTION 0
-
 namespace blink {
 
-namespace incremental_marking_test {
-class IncrementalMarkingTestDriver;
-}
-
 class NormalPageArena;
 class BasePage;
 class ThreadState;
 class ThreadHeap;
 
 class PLATFORM_EXPORT HeapCompact final {
-  friend class incremental_marking_test::IncrementalMarkingTestDriver;
-
  public:
+  // Returns |true| if the ongoing GC may compact the given arena/sub-heap.
+  static bool IsCompactableArena(int arena_index) {
+    return arena_index >= BlinkGC::kVector1ArenaIndex &&
+           arena_index <= BlinkGC::kHashTableArenaIndex;
+  }
+
   explicit HeapCompact(ThreadHeap*);
   ~HeapCompact();
 
-  // Determine if a GC for the given type and reason should also perform
-  // additional heap compaction.
-  //
-  bool ShouldCompact(ThreadHeap*,
-                     BlinkGC::StackState,
+  // Returns true if compaction can and should be used for the provided
+  // parameters.
+  bool ShouldCompact(BlinkGC::StackState,
                      BlinkGC::MarkingType,
                      BlinkGC::GCReason);
 
   // Compaction should be performed as part of the ongoing GC, initialize
-  // the heap compaction pass. Returns the appropriate visitor type to
-  // use when running the marking phase.
+  // the heap compaction pass.
   void Initialize(ThreadState*);
 
   // Returns true if the ongoing GC will perform compaction.
@@ -74,12 +62,6 @@
     return do_compact_ && (compactable_arenas_ & (0x1u << arena_index));
   }
 
-  // Returns |true| if the ongoing GC may compact the given arena/sub-heap.
-  static bool IsCompactableArena(int arena_index) {
-    return arena_index >= BlinkGC::kVector1ArenaIndex &&
-           arena_index <= BlinkGC::kHashTableArenaIndex;
-  }
-
   // See |Heap::registerMovingObjectReference()| documentation.
   void RegisterMovingObjectReference(MovableReference* slot);
 
@@ -116,14 +98,11 @@
   // (Called by the sweep compaction pass.)
   void Relocate(Address from, Address to);
 
-  // For unit testing only: arrange for a compaction GC to be triggered
-  // next time a non-conservative GC is run. Sets the compact-next flag
-  // to the new value, returning old.
-  static bool ScheduleCompactionGCForTesting(bool);
+  // Enables compaction for the next garbage collection if technically possible.
+  void EnableCompactionForNextGCForTesting() { force_for_next_gc_ = true; }
 
-  // Test-only: verify that one or more of the vector arenas are
-  // in the process of being compacted.
-  bool IsCompactingVectorArenas() {
+  // Returns true if one or more vector arenas are being compacted.
+  bool IsCompactingVectorArenasForTesting() const {
     for (int i = BlinkGC::kVector1ArenaIndex; i <= BlinkGC::kVector4ArenaIndex;
          ++i) {
       if (IsCompactingArena(i))
@@ -132,15 +111,13 @@
     return false;
   }
 
-  size_t last_fixup_count_for_testing() {
+  size_t LastFixupCountForTesting() const {
     return last_fixup_count_for_testing_;
   }
 
  private:
   class MovableObjectFixups;
 
-  static bool force_compaction_gc_;
-
   // Number of GCs that must have passed since last compaction GC.
   static const int kGCCountSinceLastCompactionThreshold = 10;
 
@@ -171,12 +148,13 @@
   // Last reported freelist size, across all compactable arenas.
   size_t free_list_size_ = 0;
 
+  // If compacting, i'th heap arena will be compacted if corresponding bit is
+  // set. Indexes are in the range of BlinkGC::ArenaIndices.
+  unsigned compactable_arenas_ = 0u;
+
   size_t last_fixup_count_for_testing_ = 0;
 
-  // If compacting, i'th heap arena will be compacted
-  // if corresponding bit is set. Indexes are in
-  // the range of BlinkGC::ArenaIndices.
-  unsigned compactable_arenas_ = 0u;
+  bool force_for_next_gc_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_compact_test.cc b/third_party/blink/renderer/platform/heap/heap_compact_test.cc
index 2d2caa3..fd5d1d4 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact_test.cc
@@ -54,7 +54,7 @@
             blink::BlinkGC::kHashTableArenaIndex));
         return;
       case VectorsAreCompacted:
-        CHECK(compaction->IsCompactingVectorArenas());
+        CHECK(compaction->IsCompactingVectorArenasForTesting());
         return;
     }
   }
@@ -83,8 +83,6 @@
 
 }  // namespace
 
-#if ENABLE_HEAP_COMPACTION
-
 using IntVector = blink::HeapVector<blink::Member<IntWrapper>>;
 using IntDeque = blink::HeapDeque<blink::Member<IntWrapper>>;
 using IntMap = blink::HeapHashMap<blink::Member<IntWrapper>, int>;
@@ -95,9 +93,8 @@
 namespace blink {
 
 static void PerformHeapCompaction() {
-  EXPECT_FALSE(HeapCompact::ScheduleCompactionGCForTesting(true));
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   PreciselyCollectGarbage();
-  EXPECT_FALSE(HeapCompact::ScheduleCompactionGCForTesting(false));
 }
 
 TEST(HeapCompactTest, CompactVector) {
@@ -392,4 +389,3 @@
 
 }  // namespace blink
 
-#endif  // ENABLE_HEAP_COMPACTION
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index 9d02a88..04cc73a2 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -351,68 +351,6 @@
   return large_object;
 }
 
-bool BaseArena::WillObjectBeLazilySwept(BasePage* page,
-                                        void* object_pointer) const {
-  // If not on the current page being (potentially) lazily swept,
-  // |objectPointer| is an unmarked, sweepable object.
-  if (page != first_unswept_page_)
-    return true;
-
-  DCHECK(!page->IsLargeObjectPage());
-  // Check if the arena is currently being lazily swept.
-  NormalPage* normal_page = reinterpret_cast<NormalPage*>(page);
-  NormalPageArena* normal_arena = normal_page->ArenaForNormalPage();
-  if (!normal_arena->IsLazySweeping())
-    return true;
-
-  // Rare special case: unmarked object is on the page being lazily swept,
-  // and a finalizer for an object on that page calls
-  // ThreadHeap::willObjectBeLazilySwept().
-  //
-  // Need to determine if |objectPointer| represents a live (unmarked) object or
-  // an unmarked object that will be lazily swept later. As lazy page sweeping
-  // doesn't record a frontier pointer representing how far along it is, the
-  // page is scanned from the start, skipping past freed & unmarked regions.
-  //
-  // If no marked objects are encountered before |objectPointer|, we know that
-  // the finalizing object calling willObjectBeLazilySwept() comes later, and
-  // |objectPointer| has been deemed to be alive already (=> it won't be swept.)
-  //
-  // If a marked object is encountered before |objectPointer|, it will
-  // not have been lazily swept past already. Hence it represents an unmarked,
-  // sweepable object.
-  //
-  // As willObjectBeLazilySwept() is used rarely and it happening to be
-  // used while runnning a finalizer on the page being lazily swept is
-  // even rarer, the page scan is considered acceptable and something
-  // really wanted -- willObjectBeLazilySwept()'s result can be trusted.
-  Address page_end = normal_page->PayloadEnd();
-  for (Address header_address = normal_page->Payload();
-       header_address < page_end;) {
-    HeapObjectHeader* header =
-        reinterpret_cast<HeapObjectHeader*>(header_address);
-    size_t size = header->size();
-    // Scan made it to |objectPointer| without encountering any marked objects.
-    //  => lazy sweep will have processed this unmarked, but live, object.
-    //  => |object_pointer| will not be lazily swept.
-    //
-    // Notice that |object_pointer| might be pointer to a GarbageCollectedMixin,
-    // hence using |FromPayload| to derive the HeapObjectHeader isn't possible
-    // (and use its value to check if |header_address| is equal to it.)
-    if (header_address > object_pointer)
-      return false;
-    if (!header->IsFree() && header->IsMarked()) {
-      // There must be a marked object on this page and the one located must
-      // have room after it for the unmarked |objectPointer| object.
-      DCHECK(header_address + size < page_end);
-      return true;
-    }
-    header_address += size;
-  }
-  NOTREACHED();
-  return true;
-}
-
 NormalPageArena::NormalPageArena(ThreadState* state, int index)
     : BaseArena(state, index),
       current_allocation_point_(nullptr),
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h
index 31f162f..d0fffbf3 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -754,8 +754,6 @@
 
   Address AllocateLargeObject(size_t allocation_size, size_t gc_info_index);
 
-  bool WillObjectBeLazilySwept(BasePage*, void*) const;
-
   virtual void VerifyObjectStartBitmap() {}
   virtual void VerifyMarking() {}
 
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index edacb2f..8149cfab 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -1578,7 +1578,7 @@
 
   size_t GetHeapCompactLastFixupCount() {
     HeapCompact* compaction = ThreadState::Current()->Heap().Compaction();
-    return compaction->last_fixup_count_for_testing();
+    return compaction->LastFixupCountForTesting();
   }
 
  private:
@@ -1674,7 +1674,7 @@
   Persistent<Store> persistent(new Store());
   persistent->insert(MakeGarbageCollected<Object>());
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  HeapCompact::ScheduleCompactionGCForTesting(true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   driver.Start();
   driver.FinishSteps();
   persistent->clear();
@@ -1690,7 +1690,7 @@
   Persistent<Store> persistent2(MakeGarbageCollected<Store>());
 
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  HeapCompact::ScheduleCompactionGCForTesting(true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   persistent->push_back(MakeGarbageCollected<Object>());
   driver.Start();
   driver.FinishGC();
@@ -1711,7 +1711,7 @@
   Persistent<Store> persistent(new Store());
 
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  HeapCompact::ScheduleCompactionGCForTesting(true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   driver.Start();
   driver.FinishSteps();
   persistent->insert(MakeGarbageCollected<Object>());
@@ -1727,7 +1727,7 @@
   persistent->push_back(MakeGarbageCollected<Object>());
 
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  HeapCompact::ScheduleCompactionGCForTesting(true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   driver.Start();
   driver.FinishSteps();
   ThreadState::Current()->CollectGarbage(
@@ -1874,8 +1874,7 @@
     holder->at(i).emplace_back(MakeGarbageCollected<Object>());
   }
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  ThreadState::Current()->Heap().Compaction()->ScheduleCompactionGCForTesting(
-      true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   driver.Start();
   driver.FinishSteps();
   // Reduce size of the outer backing store.
@@ -1894,8 +1893,7 @@
 
   using Nested = HeapVector<HeapVector<Member<Object>>>;
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  ThreadState::Current()->Heap().Compaction()->ScheduleCompactionGCForTesting(
-      true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   // Allocate a vector and reserve a buffer to avoid triggering the write
   // barrier during incremental marking.
   Nested* nested = MakeGarbageCollected<Nested>();
@@ -1940,8 +1938,7 @@
   // that slots filtering happens before any eager sweep phase.
 
   IncrementalMarkingTestDriver driver(ThreadState::Current());
-  ThreadState::Current()->Heap().Compaction()->ScheduleCompactionGCForTesting(
-      true);
+  ThreadState::Current()->EnableCompactionForNextGCForTesting();
   EagerlySweptWithVectorWithInlineStorage* eagerly =
       MakeGarbageCollected<EagerlySweptWithVectorWithInlineStorage>();
   driver.Start();
diff --git a/third_party/blink/renderer/platform/heap/persistent.h b/third_party/blink/renderer/platform/heap/persistent.h
index 79e840e..fe3f226 100644
--- a/third_party/blink/renderer/platform/heap/persistent.h
+++ b/third_party/blink/renderer/platform/heap/persistent.h
@@ -176,6 +176,10 @@
     persistent_node_.ClearWithLockHeld();
   }
 
+ protected:
+  NO_SANITIZE_ADDRESS
+  bool IsNotNull() const { return raw_; }
+
  private:
   NO_SANITIZE_ADDRESS
   void Assign(T* ptr) {
@@ -420,6 +424,9 @@
     Parent::operator=(other);
     return *this;
   }
+
+  NO_SANITIZE_ADDRESS
+  bool IsClearedUnsafe() const { return this->IsNotNull(); }
 };
 
 // Unlike Persistent, we can destruct a CrossThreadPersistent in a thread
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 75f5107e..04ede50 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -1616,23 +1616,25 @@
   Heap().CommitCallbackStacks();
 
   const bool take_snapshot = marking_type == BlinkGC::kTakeSnapshot;
-  const bool should_compact =
-      !take_snapshot && Heap().Compaction()->ShouldCompact(
-                            &Heap(), stack_state, marking_type, reason);
+
+  // Enable compaction if supported and feasible.
+  const bool compaction_enabled =
+      !take_snapshot &&
+      Heap().Compaction()->ShouldCompact(stack_state, marking_type, reason);
+  if (compaction_enabled) {
+    Heap().Compaction()->Initialize(this);
+  }
 
   current_gc_data_.reason = reason;
   current_gc_data_.visitor =
       IsUnifiedGCMarkingInProgress()
           ? std::make_unique<UnifiedHeapMarkingVisitor>(
-                this, GetMarkingMode(should_compact, take_snapshot),
+                this, GetMarkingMode(compaction_enabled, take_snapshot),
                 GetIsolate())
           : std::make_unique<MarkingVisitor>(
-                this, GetMarkingMode(should_compact, take_snapshot));
+                this, GetMarkingMode(compaction_enabled, take_snapshot));
   current_gc_data_.stack_state = stack_state;
   current_gc_data_.marking_type = marking_type;
-
-  if (should_compact)
-    Heap().Compaction()->Initialize(this);
 }
 
 void ThreadState::AtomicPausePrologue(BlinkGC::StackState stack_state,
@@ -1774,6 +1776,10 @@
   }
 }
 
+void ThreadState::EnableCompactionForNextGCForTesting() {
+  Heap().Compaction()->EnableCompactionForNextGCForTesting();
+}
+
 void ThreadState::UpdateIncrementalMarkingStepDuration() {
   if (!IsIncrementalMarking())
     return;
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 3bb7d431..ffdb605 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -237,9 +237,11 @@
   bool IsSweepingInProgress() const { return gc_phase_ == GCPhase::kSweeping; }
   bool IsUnifiedGCMarkingInProgress() const {
     return IsMarkingInProgress() &&
-           current_gc_data_.reason == BlinkGC::GCReason::kUnifiedHeapGC;
+           (current_gc_data_.reason == BlinkGC::GCReason::kUnifiedHeapGC);
   }
 
+  void EnableCompactionForNextGCForTesting();
+
   // Incremental GC.
   void ScheduleIncrementalMarkingStep();
   void ScheduleIncrementalMarkingFinalize();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 5742a1f9..ac1e1a1 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -849,10 +849,11 @@
     {
       name: "MergeBlockingNonBlockingPools",
     },
-    // Support for META tag for setting supported-color-schemes used for opting
-    // into dark UA theming and opting out of forced dark mode.
+    // Support for META tag for setting color-scheme used for opting into dark
+    // UA theming and opting out of forced dark mode.
+    // https://drafts.csswg.org/css-color-adjust/#color-scheme-meta
     {
-      name: "MetaSupportedColorSchemes",
+      name: "MetaColorScheme",
     },
     // This is enabled by default on Windows only. The only part that's
     // "experimental" is the support on other platforms.
diff --git a/third_party/blink/renderer/platform/scheduler/common/event_loop.cc b/third_party/blink/renderer/platform/scheduler/common/event_loop.cc
index b48398a..b51f93a 100644
--- a/third_party/blink/renderer/platform/scheduler/common/event_loop.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/event_loop.cc
@@ -13,21 +13,15 @@
 namespace blink {
 namespace scheduler {
 
-// static
-scoped_refptr<EventLoop> EventLoop::CreateForWorkerOrWorklet(
-    v8::Isolate* isolate) {
-  return base::AdoptRef(new EventLoop(isolate));
-}
-
 EventLoop::EventLoop(v8::Isolate* isolate)
-    : isolate_(isolate), microtask_queue_(v8::MicrotaskQueue::New(isolate)) {
+    : isolate_(isolate),
+      microtask_queue_(
+          v8::MicrotaskQueue::New(isolate, v8::MicrotasksPolicy::kScoped)) {
   DCHECK(isolate_);
 }
 
 EventLoop::~EventLoop() {
   microtask_queue_ = nullptr;
-
-  // TODO(tzik): Remove the instance from associated EventLoopGroup.
 }
 
 void EventLoop::EnqueueMicrotask(base::OnceClosure task) {
diff --git a/third_party/blink/renderer/platform/scheduler/public/event_loop.h b/third_party/blink/renderer/platform/scheduler/public/event_loop.h
index 68b4bd2..b3b98fc 100644
--- a/third_party/blink/renderer/platform/scheduler/public/event_loop.h
+++ b/third_party/blink/renderer/platform/scheduler/public/event_loop.h
@@ -20,13 +20,13 @@
 }  // namespace v8
 
 namespace blink {
+
+class Agent;
+
 namespace scheduler {
 
-// TODO(tzik): Implement EventLoopGroup that represents a group of reachable
-// browsing contexts.
-
 // Represents an event loop. The instance is held by ExecutionContexts.
-// https://html.spec.whatwg.org/multipage/webappapis.html#event-loop
+// https://html.spec.whatwg.org/C#event-loop
 //
 // Browsing contexts must share the same EventLoop if they have a chance to
 // access each other synchronously.
@@ -39,11 +39,6 @@
   USING_FAST_MALLOC(EventLoop);
 
  public:
-  // An static constructor for Workers and Worklets.
-  // For Document, use EventLoopGroup to get or create the instance.
-  static scoped_refptr<EventLoop> CreateForWorkerOrWorklet(
-      v8::Isolate* isolate);
-
   // Queues |cb| to the backing v8::MicrotaskQueue.
   void EnqueueMicrotask(base::OnceClosure cb);
 
@@ -64,14 +59,13 @@
 
  private:
   friend class WTF::RefCounted<EventLoop>;
+  friend blink::Agent;
 
   explicit EventLoop(v8::Isolate* isolate);
   ~EventLoop();
 
   static void RunPendingMicrotask(void* data);
 
-  // TODO(tzik): Add a back pointer to EventLoopGroup.
-
   v8::Isolate* isolate_;
   bool loop_enabled_ = true;
   Deque<base::OnceClosure> pending_microtasks_;
diff --git a/third_party/blink/renderer/platform/timer.h b/third_party/blink/renderer/platform/timer.h
index 5844a72..a58231a 100644
--- a/third_party/blink/renderer/platform/timer.h
+++ b/third_party/blink/renderer/platform/timer.h
@@ -32,6 +32,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/sanitizers.h"
@@ -111,22 +112,12 @@
   DISALLOW_COPY_AND_ASSIGN(TimerBase);
 };
 
-template <typename T, bool = IsGarbageCollectedType<T>::value>
-class TimerIsObjectAliveTrait {
- public:
-  static bool IsHeapObjectAlive(T*) { return true; }
-};
-
-template <typename T>
-class TimerIsObjectAliveTrait<T, true> {
- public:
-  static bool IsHeapObjectAlive(T* object_pointer) {
-    return !ThreadHeap::WillObjectBeLazilySwept(object_pointer);
-  }
-};
+template <typename TimerFiredClass,
+          bool = WTF::IsGarbageCollectedTypeInternal<TimerFiredClass>::value>
+class TaskRunnerTimer;
 
 template <typename TimerFiredClass>
-class TaskRunnerTimer : public TimerBase {
+class TaskRunnerTimer<TimerFiredClass, false> : public TimerBase {
  public:
   using TimerFiredFunction = void (TimerFiredClass::*)(TimerBase*);
 
@@ -141,23 +132,37 @@
   void Fired() override { (object_->*function_)(this); }
 
   NO_SANITIZE_ADDRESS
-  bool CanFire() const override {
-    // Oilpan: if a timer fires while Oilpan heaps are being lazily
-    // swept, it is not safe to proceed if the object is about to
-    // be swept (and this timer will be stopped while doing so.)
-    return TimerIsObjectAliveTrait<TimerFiredClass>::IsHeapObjectAlive(object_);
-  }
+  bool CanFire() const override { return true; }
 
  private:
-  // FIXME: Oilpan: TimerBase should be moved to the heap and m_object should be
-  // traced.  This raw pointer is safe as long as Timer<X> is held by the X
-  // itself (That's the case
-  // in the current code base).
-  GC_PLUGIN_IGNORE("363031")
   TimerFiredClass* object_;
   TimerFiredFunction function_;
 };
 
+template <typename TimerFiredClass>
+class TaskRunnerTimer<TimerFiredClass, true> : public TimerBase {
+ public:
+  using TimerFiredFunction = void (TimerFiredClass::*)(TimerBase*);
+
+  TaskRunnerTimer(scoped_refptr<base::SingleThreadTaskRunner> web_task_runner,
+                  TimerFiredClass* o,
+                  TimerFiredFunction f)
+      : TimerBase(std::move(web_task_runner)), object_(o), function_(f) {}
+
+  ~TaskRunnerTimer() override = default;
+
+ protected:
+  void Fired() override { (object_->*function_)(this); }
+
+  NO_SANITIZE_ADDRESS
+  bool CanFire() const override { return object_.IsClearedUnsafe(); }
+
+ private:
+  GC_PLUGIN_IGNORE("363031")
+  WeakPersistent<TimerFiredClass> object_;
+  TimerFiredFunction function_;
+};
+
 NO_SANITIZE_ADDRESS
 inline bool TimerBase::IsActive() const {
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/platform/wtf/type_traits.h b/third_party/blink/renderer/platform/wtf/type_traits.h
index 0fbd7d2..7378c53 100644
--- a/third_party/blink/renderer/platform/wtf/type_traits.h
+++ b/third_party/blink/renderer/platform/wtf/type_traits.h
@@ -180,12 +180,10 @@
 };
 
 template <typename T>
-class IsGarbageCollectedType {
+class IsGarbageCollectedTypeInternal {
   typedef char YesType;
   typedef struct NoType { char padding[8]; } NoType;
 
-  static_assert(sizeof(T), "T must be fully defined");
-
   using NonConstType = typename std::remove_const<T>::type;
   template <typename U>
   static YesType CheckGarbageCollectedType(
@@ -217,6 +215,11 @@
        sizeof(CheckGarbageCollectedMixinType<NonConstType>(nullptr)));
 };
 
+template <typename T>
+class IsGarbageCollectedType : public IsGarbageCollectedTypeInternal<T> {
+  static_assert(sizeof(T), "T must be fully defined");
+};
+
 template <>
 class IsGarbageCollectedType<void> {
  public:
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
index 033b726..190408c 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
+++ b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
@@ -23,6 +23,15 @@
 Bug(none) http/tests/misc/redirect-to-about-blank.html [ Pass ]
 
 # The below is only supported with network service.
-Bug(none) virtual/omt-worker-fetch [ Skip ]
+Bug(none) http/tests/devtools/console-xhr-logging.js [ Skip ]
+Bug(none) http/tests/eventsource/eventsource-cors-non-http.html [ Skip ]
+Bug(none) http/tests/eventsource/workers/eventsource-cors-non-http.html [ Skip ]
 Bug(none) http/tests/inspector-protocol/fetch [ Skip ]
+Bug(none) http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js [ Skip ]
 Bug(none) http/tests/inspector-protocol/network/request-will-be-sent-for-sync-xhrs.js [ Skip ]
+Bug(none) http/tests/security/script-crossorigin-redirect-credentials.html [ Skip ]
+Bug(none) http/tests/xmlhttprequest/cross-origin-unsupported-url.html [ Skip ]
+Bug(none) http/tests/xmlhttprequest/workers/cross-origin-unsupported-url.html [ Skip ]
+Bug(none) http/tests/security/img-redirect-to-crossorigin-credentials.html [ Skip ]
+Bug(none) mhtml/cid_in_html_resource.html [ Skip ]
+Bug(none) virtual/omt-worker-fetch [ Skip ]
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 5f91c57e..cdafcbd4 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -70,6 +70,7 @@
 crbug.com/591099 editing/pasteboard/drop-text-without-selection.html [ Failure ]
 crbug.com/591099 editing/selection/paint-hyphen.html [ Pass ]
 crbug.com/591099 external/wpt/acid/acid2/reftest.html [ Failure Pass ]
+crbug.com/591099 external/wpt/content-security-policy/unsafe-hashes/javascript_src_allowed-href_blank.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/float-nowrap-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/floats-line-wrap-shifted-001.html [ Pass ]
 crbug.com/408859 external/wpt/css/CSS2/floats/overhanging-float-paint-order.html [ Pass ]
@@ -81,7 +82,7 @@
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-nested-002.xht [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-remove-006.xht [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/text/white-space-mixed-003.xht [ Pass ]
-crbug.com/591099 external/wpt/css/css-animations/Element-getAnimations.tentative.html [ Failure Pass ]
+crbug.com/591099 external/wpt/css/css-animations/Element-getAnimations.tentative.html [ Failure ]
 crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-1.html [ Pass ]
 crbug.com/714962 external/wpt/css/css-fonts/font-features-across-space-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003.html [ Pass ]
@@ -108,11 +109,25 @@
 crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-007.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-009.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-010.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/hyphens/hyphens-out-of-flow-002.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/hyphens/hyphens-shaping-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/hyphens/shy-styling-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-009.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-011.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-015.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-001.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-002.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-003.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-004.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-005.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-006.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-ic-003.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-001.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-002.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-005.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-text/overflow-wrap/overflow-wrap-shaping-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-000.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-002.html [ Pass ]
@@ -132,12 +147,16 @@
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-020.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-021.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-022.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/shaping/shaping-023.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping_lig-000.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/text-encoding/shaping-join-003.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/text-encoding/shaping-tatweel-003.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/white-space/control-chars-00C.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-002.html [ Pass ]
 crbug.com/40634 external/wpt/css/css-text/white-space/trailing-space-before-br-001.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/white-space/white-space-intrinsic-size-004.html [ Pass ]
+crbug.com/591099 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-002.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-ui/text-overflow-010.html [ Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-icb-vlr-003.xht [ Pass ]
@@ -260,8 +279,15 @@
 crbug.com/591099 external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Pass ]
 crbug.com/591099 external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Pass ]
 crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
+crbug.com/591099 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html [ Pass ]
 crbug.com/591099 external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener_base.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/html/user-activation/activation-transfer-without-click.tentative.html [ Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html [ Failure Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html [ Failure Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-none-css_touch.html [ Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-x-css_touch.html [ Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html [ Pass ]
+crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch.html [ Failure Pass ]
 crbug.com/591099 external/wpt/portals/portals-adopt-predecessor.html [ Pass Timeout ]
 crbug.com/845902 external/wpt/quirks/line-height-trailing-collapsable-whitespace.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Pass ]
@@ -269,12 +295,13 @@
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/shared-worker/keep-origin-redirect/generic.http.html [ Pass ]
 crbug.com/591099 external/wpt/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/shared-worker/no-redirect/generic.http.html [ Pass ]
 crbug.com/591099 external/wpt/service-workers/service-worker/client-navigate.https.html [ Pass ]
+crbug.com/591099 external/wpt/service-workers/service-worker/update-registration-with-type.https.html [ Pass ]
 crbug.com/591099 external/wpt/webauthn/createcredential-extensions.https.html [ Pass ]
 crbug.com/591099 external/wpt/webrtc/RTCIceConnectionState-candidate-pair.https.html [ Pass ]
 crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ]
 crbug.com/591099 fast/block/float/4145535Crash.html [ Pass ]
 crbug.com/591099 fast/borders/inline-mask-overlay-image-outset-vertical-rl.html [ Failure ]
-crbug.com/591099 fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
+crbug.com/591099 fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
 crbug.com/591099 fast/css-intrinsic-dimensions/height-positioned.html [ Pass ]
 crbug.com/591099 fast/css-intrinsic-dimensions/height-tables.html [ Failure Pass ]
 crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ]
@@ -289,35 +316,7 @@
 crbug.com/889721 fast/inline/outline-continuations.html [ Failure ]
 crbug.com/591099 fast/multicol/border-radius-clipped-layer.html [ Pass ]
 crbug.com/591099 fast/peerconnection/RTCPeerConnection-many.html [ Pass ]
-crbug.com/591099 fast/replaced/replaced-breaking.html [ Failure Pass ]
-# Some outline rect is added double offset.
-crbug.com/591099 external/wpt/css/css-text/hyphens/hyphens-shaping-001.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/hyphens/shy-styling-001.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-015.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-001.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-002.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-003.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-004.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-005.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-atomic-006.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-001.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-002.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/line-breaking/line-breaking-replaced-003.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/overflow-wrap/overflow-wrap-shaping-001.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/shaping/shaping-023.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/white-space/control-chars-00C.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/white-space/white-space-intrinsic-size-004.html [ Pass ]
-crbug.com/591099 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-002.html [ Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html [ Failure Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html [ Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch.html [ Failure Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html [ Failure Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html [ Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-none-css_touch.html [ Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-x-css_touch.html [ Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html [ Failure Pass ]
-crbug.com/591099 external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch.html [ Pass ]
-crbug.com/591099 external/wpt/service-workers/service-worker/update-registration-with-type.https.html [ Pass ]
+crbug.com/591099 fast/replaced/replaced-breaking.html [ Failure ]
 crbug.com/899902 fast/text/ellipsis-with-self-painting-layer.html [ Pass ]
 crbug.com/591099 fast/text/emoji-vertical-origin-visual.html [ Failure ]
 crbug.com/591099 fast/text/font-format-support-color-cff2-vertical.html [ Failure ]
@@ -329,16 +328,24 @@
 crbug.com/591099 fast/writing-mode/table-percent-width-quirk.html [ Pass ]
 crbug.com/591099 http/tests/appcache/non-html.xhtml [ Crash Pass ]
 crbug.com/591099 http/tests/csspaint/invalidation-border-image.html [ Pass ]
+crbug.com/591099 http/tests/devtools/console-xhr-logging.js [ Pass ]
 crbug.com/591099 http/tests/devtools/sources/debugger-frameworks/frameworks-jquery.js [ Crash Pass ]
 crbug.com/591099 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Pass ]
 crbug.com/591099 http/tests/devtools/tracing-session-id.js [ Pass ]
 crbug.com/591099 http/tests/devtools/tracing/console-timeline.js [ Pass ]
+crbug.com/591099 http/tests/eventsource/eventsource-cors-non-http.html [ Pass ]
+crbug.com/591099 http/tests/eventsource/workers/eventsource-cors-non-http.html [ Pass ]
 crbug.com/591099 http/tests/html/validation-bubble-oopif-clip.html [ Failure Pass ]
-crbug.com/591099 http/tests/media/autoplay/document-user-activation-cross-origin-feature-policy-disabled.html [ Failure Pass ]
+crbug.com/591099 http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js [ Pass ]
 crbug.com/591099 http/tests/media/video-load-metadata-decode-error.html [ Pass ]
+crbug.com/591099 http/tests/security/img-redirect-to-crossorigin-credentials.html [ Pass ]
 crbug.com/591099 http/tests/security/inactive-document-with-empty-security-origin.html [ Pass ]
 crbug.com/591099 http/tests/security/mixedContent/insecure-css-resources.html [ Failure Pass ]
+crbug.com/591099 http/tests/security/script-crossorigin-redirect-credentials.html [ Pass ]
+crbug.com/591099 http/tests/xmlhttprequest/cross-origin-unsupported-url.html [ Pass ]
+crbug.com/591099 http/tests/xmlhttprequest/workers/cross-origin-unsupported-url.html [ Pass ]
 crbug.com/591099 images/feature-policy-oversized-images-resize.html [ Pass ]
+crbug.com/591099 mhtml/cid_in_html_resource.html [ Pass ]
 crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
 crbug.com/591099 paint/invalidation/media-audio-no-spurious-repaints.html [ Failure ]
 crbug.com/835484 paint/invalidation/outline/inline-focus.html [ Failure ]
@@ -383,7 +390,7 @@
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/fractional_scrolling_threaded/fast/scrolling/unscrollable-layer-subpixel-size-with-negative-overflow.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Pass ]
-crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
+crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/OffscreenCanvas-filter.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blend-image.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-color-over-pattern.html [ Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 1f6aef4..ab175678 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2903,6 +2903,11 @@
 crbug.com/939181 virtual/not-site-per-process/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Failure Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html [ Failure ]
+crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphens-out-of-flow-001.html [ Failure ]
 crbug.com/626703 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechSynthesisUtterance-volume-manual.html [ Skip ]
 crbug.com/626703 [ Linux ] external/wpt/speech-api/SpeechSynthesisUtterance-volume-manual.html [ Skip ]
@@ -4222,6 +4227,8 @@
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/dedicated-inheritance.html [ Skip ]
 # This is failing because of the lack of CSP support.
 crbug.com/940316 virtual/omt-worker-fetch/external/wpt/content-security-policy/inside-worker/shared-script.html [ Failure Pass ]
+# This is failing because of the lack of origin trial context support.
+crbug.com/945215 virtual/omt-worker-fetch/http/tests/origin_trials/sample-api-workers.html [ Failure ]
 
 # This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
 # worker script, because the script url has a .html file extension.
@@ -5961,14 +5968,3 @@
 
 # Sheriff 2019-05-07
 crbug.com/960443 [ Mac10.10 Mac10.11 ] external/wpt/animation-worklet/worklet-animation-with-scroll-timeline.https.html [ Failure Pass ]
-
-# Sheriff 2019-05-08
-crbug.com/961009 [ Linux ] http/tests/devtools/console-xhr-logging.js [ Failure Timeout ]
-crbug.com/961009 [ Linux ] http/tests/eventsource/eventsource-cors-non-http.html [ Failure ]
-crbug.com/961009 [ Linux ] http/tests/eventsource/workers/eventsource-cors-non-http.html [ Failure ]
-crbug.com/961009 [ Linux ] http/tests/inspector-protocol/network-fetch-content-with-error-status-code.js [ Failure ]
-crbug.com/961009 [ Linux ] http/tests/security/script-crossorigin-redirect-credentials.html [ Failure ]
-crbug.com/961009 [ Linux ] http/tests/xmlhttprequest/cross-origin-unsupported-url.html [ Failure ]
-crbug.com/961009 [ Linux ] http/tests/xmlhttprequest/workers/cross-origin-unsupported-url.html [ Failure ]
-crbug.com/961009 [ Linux ] mhtml/cid_in_html_resource.html [ Failure ]
-crbug.com/961009 [ Linux ] http/tests/security/img-redirect-to-crossorigin-credentials.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 504004e..c98312a 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -599,22 +599,22 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/mixed-content/classic-data-worker-fetch",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/mixed-content/module-data-worker-import",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/mixed-content/module-worker-top-level",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/mixed-content/worker-request",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
@@ -624,7 +624,7 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/resource-timing",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
@@ -634,7 +634,7 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/upgrade-insecure-requests",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
@@ -644,7 +644,7 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/xhr",
-    "args": ["--enable-features=OffMainThreadServiceWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
   },
   {
     "prefix": "omt-worker-fetch",
@@ -653,6 +653,11 @@
   },
   {
     "prefix": "omt-worker-fetch",
+    "base": "http/tests/origin_trials",
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
+  },
+  {
+    "prefix": "omt-worker-fetch",
     "base": "http/tests/security/cors-rfc1918",
     "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch",
              "--enable-blink-features=CorsRFC1918"]
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 88523d1..68b6c56 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
@@ -95075,6 +95075,18 @@
      {}
     ]
    ],
+   "css/filter-effects/backdrop-filter-clip-rect-zoom.html": [
+    [
+     "css/filter-effects/backdrop-filter-clip-rect-zoom.html",
+     [
+      [
+       "/css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/filter-effects/backdrop-filter-clip-rect.html": [
     [
      "css/filter-effects/backdrop-filter-clip-rect.html",
@@ -113723,6 +113735,30 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html": [
+    [
+     "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html": [
+    [
+     "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/cue_too_long.html": [
     [
      "webvtt/rendering/cues-with-video/processing-model/cue_too_long.html",
@@ -114409,6 +114445,30 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html": [
+    [
+     "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html": [
+    [
+     "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/selectors/cue/white-space_normal_wrapped.html": [
     [
      "webvtt/rendering/cues-with-video/processing-model/selectors/cue/white-space_normal_wrapped.html",
@@ -114949,6 +115009,18 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html": [
+    [
+     "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html",
+     [
+      [
+       "/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_white-space_normal_wrapped.html": [
     [
      "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_white-space_normal_wrapped.html",
@@ -146191,11 +146263,6 @@
      {}
     ]
    ],
-   "css/css-scroll-snap/parsing/scroll-snap-type-valid-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt": [
     [
      {}
@@ -158151,6 +158218,11 @@
      {}
     ]
    ],
+   "css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/filter-effects/backdrop-filter-edge-pixels-ref.html": [
     [
      {}
@@ -203541,6 +203613,16 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl-ref.html": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/cue_too_long-ref.html": [
     [
      {}
@@ -203921,6 +204003,16 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright-ref.html": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/selectors/cue/white-space_normal_wrapped-ref.html": [
     [
      {}
@@ -204146,6 +204238,11 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright-ref.html": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_white-space_normal_wrapped-ref.html": [
     [
      {}
@@ -204726,6 +204823,26 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/support/bidi_text-combine-upright.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_lr.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl-oneline.vtt": [
+    [
+     {}
+    ]
+   ],
+   "webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl.vtt": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/support/bold_long.vtt": [
     [
      {}
@@ -204746,6 +204863,11 @@
      {}
     ]
    ],
+   "webvtt/rendering/cues-with-video/processing-model/support/class_text-combine-upright.vtt": [
+    [
+     {}
+    ]
+   ],
    "webvtt/rendering/cues-with-video/processing-model/support/class_with_2_timestamps.vtt": [
     [
      {}
@@ -228813,6 +228935,18 @@
      {}
     ]
    ],
+   "css/css-color/inheritance.html": [
+    [
+     "css/css-color/inheritance.html",
+     {}
+    ]
+   ],
+   "css/css-color/parsing/color-computed.html": [
+    [
+     "css/css-color/parsing/color-computed.html",
+     {}
+    ]
+   ],
    "css/css-color/parsing/color-invalid.html": [
     [
      "css/css-color/parsing/color-invalid.html",
@@ -228825,6 +228959,12 @@
      {}
     ]
    ],
+   "css/css-color/parsing/opacity-computed.html": [
+    [
+     "css/css-color/parsing/opacity-computed.html",
+     {}
+    ]
+   ],
    "css/css-color/parsing/opacity-invalid.html": [
     [
      "css/css-color/parsing/opacity-invalid.html",
@@ -231787,6 +231927,12 @@
      {}
     ]
    ],
+   "css/css-images/inheritance.html": [
+    [
+     "css/css-images/inheritance.html",
+     {}
+    ]
+   ],
    "css/css-images/parsing/gradient-position-invalid.html": [
     [
      "css/css-images/parsing/gradient-position-invalid.html",
@@ -231865,6 +232011,12 @@
      {}
     ]
    ],
+   "css/css-images/parsing/object-position-computed.html": [
+    [
+     "css/css-images/parsing/object-position-computed.html",
+     {}
+    ]
+   ],
    "css/css-images/parsing/object-position-invalid.html": [
     [
      "css/css-images/parsing/object-position-invalid.html",
@@ -233599,15 +233751,21 @@
      {}
     ]
    ],
+   "css/css-scroll-snap/scroll-snap-type-change.html": [
+    [
+     "css/css-scroll-snap/scroll-snap-type-change.html",
+     {}
+    ]
+   ],
    "css/css-scroll-snap/scroll-snap-type-on-root-element.html": [
     [
      "css/css-scroll-snap/scroll-snap-type-on-root-element.html",
      {}
     ]
    ],
-   "css/css-scroll-snap/scroll-snap-type-proximity.html": [
+   "css/css-scroll-snap/scroll-snap-type.html": [
     [
-     "css/css-scroll-snap/scroll-snap-type-proximity.html",
+     "css/css-scroll-snap/scroll-snap-type.html",
      {}
     ]
    ],
@@ -235297,6 +235455,60 @@
      {}
     ]
    ],
+   "css/css-text-decor/parsing/text-decoration-skip-ink-computed.html": [
+    [
+     "css/css-text-decor/parsing/text-decoration-skip-ink-computed.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-decoration-skip-ink-invalid.html": [
+    [
+     "css/css-text-decor/parsing/text-decoration-skip-ink-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-decoration-skip-ink-valid.html": [
+    [
+     "css/css-text-decor/parsing/text-decoration-skip-ink-valid.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-decoration-style-computed.html": [
+    [
+     "css/css-text-decor/parsing/text-decoration-style-computed.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-decoration-style-invalid.html": [
+    [
+     "css/css-text-decor/parsing/text-decoration-style-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-decoration-style-valid.html": [
+    [
+     "css/css-text-decor/parsing/text-decoration-style-valid.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-underline-position-computed.html": [
+    [
+     "css/css-text-decor/parsing/text-underline-position-computed.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-underline-position-invalid.html": [
+    [
+     "css/css-text-decor/parsing/text-underline-position-invalid.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/parsing/text-underline-position-valid.html": [
+    [
+     "css/css-text-decor/parsing/text-underline-position-valid.html",
+     {}
+    ]
+   ],
    "css/css-text-decor/text-decoration-serialization.tentative.html": [
     [
      "css/css-text-decor/text-decoration-serialization.tentative.html",
@@ -311747,6 +311959,12 @@
      {}
     ]
    ],
+   "svg/pservers/inheritance.svg": [
+    [
+     "svg/pservers/inheritance.svg",
+     {}
+    ]
+   ],
    "svg/pservers/parsing/stop-color-computed.svg": [
     [
      "svg/pservers/parsing/stop-color-computed.svg",
@@ -342416,7 +342634,7 @@
    "support"
   ],
   "README.md": [
-   "6f9436bc3e4261e2f6813253e1709cbb60763ca2",
+   "d4d6032d8343ded32da0604fe0efe901dbed2474",
    "support"
   ],
   "WebCryptoAPI/META.yml": [
@@ -347540,7 +347758,7 @@
    "support"
   ],
   "content-security-policy/reporting-api/reporting-api-report-only-sends-reports-on-violation.https.sub.html": [
-   "61658863a7fb49b4e362330382bf224692b932ca",
+   "302025669d4417db670b6ebba18a6a49aed4e2eb",
    "testharness"
   ],
   "content-security-policy/reporting-api/reporting-api-report-only-sends-reports-on-violation.https.sub.html.sub.headers": [
@@ -347564,7 +347782,7 @@
    "support"
   ],
   "content-security-policy/reporting-api/reporting-api-sends-reports-on-violation.https.sub.html": [
-   "6ec11ef0327e5af7d45e336a212a0e4caab4f397",
+   "23337ae8d077e33fcec38fec6baa59778a39e0f8",
    "testharness"
   ],
   "content-security-policy/reporting-api/reporting-api-sends-reports-on-violation.https.sub.html.sub.headers": [
@@ -347572,7 +347790,7 @@
    "support"
   ],
   "content-security-policy/reporting-api/reporting-api-works-on-frame-src.https.sub.html": [
-   "d633e3b30a1fce04f4eb73fcd94e4a814bac3d6b",
+   "b83a05ce4b5bd4cbf05bbb0ef7fb5bb3f85f96e4",
    "testharness"
   ],
   "content-security-policy/reporting-api/reporting-api-works-on-frame-src.https.sub.html.sub.headers": [
@@ -370863,6 +371081,10 @@
    "26cbb024e8aedc9b8f613872b76dd1d9f4dedc31",
    "support"
   ],
+  "css/css-color/inheritance.html": [
+   "2928bfa3bbf7686f17d28f4ef31ecb3674d0439c",
+   "testharness"
+  ],
   "css/css-color/lab-001.html": [
    "70dbd9123c5405c3ec020b01ed01e15c44af10c6",
    "reftest"
@@ -370939,6 +371161,10 @@
    "61a98dcb7bb02fab3db18ed337eee5ddb07724fc",
    "reftest"
   ],
+  "css/css-color/parsing/color-computed.html": [
+   "e339a0ca5471165098368b6cbb5ef7b263a44474",
+   "testharness"
+  ],
   "css/css-color/parsing/color-invalid.html": [
    "676ffb315a98c1ba686bf237e2fdc82e1b0ba691",
    "testharness"
@@ -370947,6 +371173,10 @@
    "70e2450c77f26391ddea1c42dbed4de5d0f817b5",
    "testharness"
   ],
+  "css/css-color/parsing/opacity-computed.html": [
+   "32250b38544aac0362a4b10bf1e3740f6d15f4d1",
+   "testharness"
+  ],
   "css/css-color/parsing/opacity-invalid.html": [
    "1bcf4c5b64b07dd0f2097329631cff92c92dd292",
    "testharness"
@@ -387043,6 +387273,10 @@
    "8b1d10766a3293e2503ff5cb838618f542e186f9",
    "visual"
   ],
+  "css/css-images/inheritance.html": [
+   "5ca1ed6b23ff70637d790baf81ae4e3cbf5afe8c",
+   "testharness"
+  ],
   "css/css-images/linear-gradient-1.html": [
    "dcf590f36734222359fb693a286af31731742895",
    "reftest"
@@ -387163,12 +387397,16 @@
    "f8905624287312ace1dcee3f9c2056177dd3f226",
    "testharness"
   ],
+  "css/css-images/parsing/object-position-computed.html": [
+   "f95dcc05fed41ae9eb942f15c0d5bb08fc628ca3",
+   "testharness"
+  ],
   "css/css-images/parsing/object-position-invalid.html": [
    "63e47cf17a6590d4d26fa111897547a8e9883fb9",
    "testharness"
   ],
   "css/css-images/parsing/object-position-valid.html": [
-   "b9dab78b518138272a1e29a6a2690d13f3e208e1",
+   "90178c660229041245c136c6c2aa14060a23bef9",
    "testharness"
   ],
   "css/css-images/reference/100x100-blue-green.html": [
@@ -393448,15 +393686,11 @@
    "testharness"
   ],
   "css/css-scroll-snap/parsing/scroll-snap-type-invalid.html": [
-   "72306dc090828a191aab7c93567f2418c1b0b3f2",
+   "6177ff3baf49bf246cc35349aaad56fbd494b24e",
    "testharness"
   ],
-  "css/css-scroll-snap/parsing/scroll-snap-type-valid-expected.txt": [
-   "3c91974ba9b54222c406cc87516b3f7537827187",
-   "support"
-  ],
   "css/css-scroll-snap/parsing/scroll-snap-type-valid.html": [
-   "59a0cb9ab203b5ec44e46286594c7f77700c66f8",
+   "ca995770f454f9c3658b0fbcad799d7c4e59501f",
    "testharness"
   ],
   "css/css-scroll-snap/scroll-padding.html": [
@@ -393467,6 +393701,10 @@
    "7d2a228688fc2011662b659803cc615dac14f350",
    "testharness"
   ],
+  "css/css-scroll-snap/scroll-snap-type-change.html": [
+   "89b4edaf135be41ebb2e548076498a2c5f9afdf8",
+   "testharness"
+  ],
   "css/css-scroll-snap/scroll-snap-type-on-root-element-expected.txt": [
    "2694ce2a7ba671991d17777d6c5ff7007c129911",
    "support"
@@ -393475,8 +393713,8 @@
    "c2c413d04bde1a419c346fbc1d47c19825de9365",
    "testharness"
   ],
-  "css/css-scroll-snap/scroll-snap-type-proximity.html": [
-   "cfe990c4fcab85898899039f71fa353484558789",
+  "css/css-scroll-snap/scroll-snap-type.html": [
+   "1577aa7afc66571dd010da4103c0ccd5eaee3cc5",
    "testharness"
   ],
   "css/css-scroll-snap/scroll-target-001-ref.html": [
@@ -396488,7 +396726,7 @@
    "support"
   ],
   "css/css-text-decor/inheritance.html": [
-   "17486f1f193d04fadc1ed000121f62cae0b3d530",
+   "b106343742e03aa305a3017610272c8692b2a428",
    "testharness"
   ],
   "css/css-text-decor/line-through-vertical.html": [
@@ -396507,6 +396745,42 @@
    "3dd2d0c834ec8c7340b093a4c7a1272fb35a8a26",
    "testharness"
   ],
+  "css/css-text-decor/parsing/text-decoration-skip-ink-computed.html": [
+   "3d6435eea870ccf7e7a9f28cb4482dd0bdc25b8d",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-decoration-skip-ink-invalid.html": [
+   "4c983182dd4ea70e2f773aa2104991407b8243c1",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-decoration-skip-ink-valid.html": [
+   "bc694490d4ab086b44ba1910086c35dddca523c4",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-decoration-style-computed.html": [
+   "da4977ad2189c75d7a18690907a8fbb0132f0462",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-decoration-style-invalid.html": [
+   "ad92c71f90ec6e5a87a59902730db6765df7ff86",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-decoration-style-valid.html": [
+   "d47303f80546526c1c96b2c1e58d5c5e45ecedd9",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-underline-position-computed.html": [
+   "eadbe04a032bcf602360ed1d487848e64442437c",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-underline-position-invalid.html": [
+   "5feea8ed531de3de31b0692603ae48c065056819",
+   "testharness"
+  ],
+  "css/css-text-decor/parsing/text-underline-position-valid.html": [
+   "fa05448f09e40f34de18ac6896d81269c1103dfa",
+   "testharness"
+  ],
   "css/css-text-decor/reference/line-through-vertical-ref.html": [
    "979512787a18ec9cbed7e9baf4b2cbd57ab99d33",
    "support"
@@ -410524,7 +410798,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/scroll-snap-type-expected.txt": [
-   "0295837e1d3876672a8dea6d01f325e47ec95e28",
+   "e53364b64ac795f09691507c86eb183e4794b3e3",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/scroll-snap-type.html": [
@@ -421468,7 +421742,7 @@
    "testharness"
   ],
   "css/cssom/CSSStyleSheet-constructable.html": [
-   "c7ae5f27b8bfb4220d7e9b2c956d2360f335c12a",
+   "c30e764152deddcfcb3311636aa8cadcb5295dee",
    "testharness"
   ],
   "css/cssom/CSSStyleSheet.html": [
@@ -422183,6 +422457,14 @@
    "9082fa32de3bd022245d5f2c3a75c354674ff38e",
    "support"
   ],
+  "css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html": [
+   "5e43c7df922c27b104c3b2e6a975932e4ab70da2",
+   "support"
+  ],
+  "css/filter-effects/backdrop-filter-clip-rect-zoom.html": [
+   "36f0b1b38bc6d8eb832a8d088ffa5ea3fb3e9877",
+   "reftest"
+  ],
   "css/filter-effects/backdrop-filter-clip-rect.html": [
    "4fc4b739ddb5a316ca6ca4706940c04df96e57d3",
    "reftest"
@@ -432720,7 +433002,7 @@
    "support"
   ],
   "domparsing/XMLSerializer-serializeToString.html": [
-   "cb624a962035392a2b55af16d77e8859c9fd0d9a",
+   "17280af7b83cc039eaf53b8445b306eb0eb73587",
    "testharness"
   ],
   "domparsing/createContextualFragment.html": [
@@ -460812,7 +461094,7 @@
    "support"
   ],
   "interfaces/README.md": [
-   "741cf1cd1d56fbb5f97e6568abd6fa28ded60d59",
+   "f70ffd2e11e8ead7ccefed830331a1a5017a43e4",
    "support"
   ],
   "interfaces/SRI.idl": [
@@ -461728,11 +462010,11 @@
    "testharness"
   ],
   "layout-stability/buffer-layout-jank.html": [
-   "f5818cfbc64e0dc70d503d79a278f20777151280",
+   "57009590e221b398573ca9e68d261596a58dd091",
    "testharness"
   ],
   "layout-stability/observe-layoutjank.html": [
-   "1a0533ac703e92abbd3d2116b9d3eedaff6278ce",
+   "9d0f71ced46d111aa88a10b6a2866b10a91c8c3e",
    "testharness"
   ],
   "layout-stability/resources/slow-image.py": [
@@ -492647,6 +492929,10 @@
    "297f8ede687a28a12ced98a4b89051dd9ddf5090",
    "testharness"
   ],
+  "svg/pservers/inheritance.svg": [
+   "e08b93da83c7d0fd3e546eaa9b708cdda83eba31",
+   "testharness"
+  ],
   "svg/pservers/parsing/stop-color-computed.svg": [
    "b05a69f10b9e3614f7a588998ea06879df437da7",
    "testharness"
@@ -498480,7 +498766,7 @@
    "support"
   ],
   "tools/wptserve/wptserve/request.py": [
-   "1b1061ba7b7a94e83ed5177cdbe15e61591491a2",
+   "a80bc0c8300acfcf766512b89ecfee338bac2295",
    "support"
   ],
   "tools/wptserve/wptserve/response.py": [
@@ -504144,7 +504430,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-iceConnectionState.https.html": [
-   "4a7f137fd6a3a4f96b74526b14ff3f40f2d3852d",
+   "6d4ab50b0e266c30d6ce329e1f587886f33ae5fb",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-iceGatheringState-expected.txt": [
@@ -507123,6 +507409,22 @@
    "a3c69c8adb9d121730025b850088ea5e820808de",
    "reftest"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr-ref.html": [
+   "42b6a5dac03e55645a3bf572d88bacb1870052fc",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html": [
+   "c5e7d2b15dc6640d83e78c534f6ab4b47f3505ee",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl-ref.html": [
+   "737c6955532e3d01022db26b442fa88d3ef63671",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html": [
+   "9afdb2dd10b76f046f3268636aeba0f0aeb8288a",
+   "reftest"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/cue_too_long-ref.html": [
    "05f12413bcafc5472f15242eacdf7f8716514bbf",
    "support"
@@ -507655,6 +507957,22 @@
    "fbd4022a3881627a8f3d69f32aeadd1d84b11b37",
    "reftest"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position-ref.html": [
+   "53405cf1c0a771604fb7b73c6db66225abdd4766",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html": [
+   "371a296aaeb7aacf10b192a5ffc9d1fe39470e04",
+   "reftest"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright-ref.html": [
+   "270bb790f952237f6f6fcbe7c358a317254bce66",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html": [
+   "5aebd035e99cac4b69d2a4df35fedbfaf77cdc35",
+   "reftest"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/selectors/cue/white-space_normal_wrapped-ref.html": [
    "6c28a4f792e496beb7479bd807448ff1134ba541",
    "support"
@@ -508015,6 +508333,14 @@
    "adb5328b835d955e76b9aaf94a09cd6da0f1c5df",
    "reftest"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright-ref.html": [
+   "09491271679eeb3e39e769204a753d4cebb6993c",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html": [
+   "bf99691c2e504653f4c2c8c4d0e7984b79a48753",
+   "reftest"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_white-space_normal_wrapped-ref.html": [
    "b710c322ee070ef9e49e28e7a3283ae24834d26f",
    "support"
@@ -508887,6 +509213,22 @@
    "19e67450160d2e074b6399d8a2481e5f7c18f617",
    "support"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/support/bidi_text-combine-upright.vtt": [
+   "e3c9914387adb457ac358e13512f023428887ad2",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_lr.vtt": [
+   "e991973ab4c9b33381bd88307d3bd6cd363624a0",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl-oneline.vtt": [
+   "30297e1b4fb3ee191153c79f15da016f89d7b7fb",
+   "support"
+  ],
+  "webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl.vtt": [
+   "56fcbd412ad88852c8e5e0db1cddf4d271dd6550",
+   "support"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/support/bold_long.vtt": [
    "c452682e4c5b0d88999a863d19a9ec7669a661b5",
    "support"
@@ -508903,6 +509245,10 @@
    "a551d677435ff8584c1f0bc832a291f8dc3d0d0c",
    "support"
   ],
+  "webvtt/rendering/cues-with-video/processing-model/support/class_text-combine-upright.vtt": [
+   "064f75ad9ec952f79ed473f273b04fbbb1ab88b3",
+   "support"
+  ],
   "webvtt/rendering/cues-with-video/processing-model/support/class_with_2_timestamps.vtt": [
    "d95f7602573304bf7a7bfba11fdf22610c3e070e",
    "support"
diff --git a/third_party/blink/web_tests/external/wpt/README.md b/third_party/blink/web_tests/external/wpt/README.md
index 6f9436b..d4d6032d 100644
--- a/third_party/blink/web_tests/external/wpt/README.md
+++ b/third_party/blink/web_tests/external/wpt/README.md
@@ -156,55 +156,6 @@
 * `wpt install` - For installing the latest release of a browser or
   webdriver server on the local machine.
 
-<span id="submodules">Submodules</span>
-=======================================
-
-Some optional components of web-platform-tests (test components from
-third party software and pieces of the CSS build system) are included
-as submodules. To obtain these components run the following in the
-root of your checkout:
-
-```
-git submodule update --init --recursive
-```
-
-Prior to commit `39d07eb01fab607ab1ffd092051cded1bdd64d78` submodules
-were required for basic functionality. If you are working with an
-older checkout, the above command is required in all cases.
-
-When moving between a commit prior to `39d07eb` and one after it git
-may complain
-
-```
-$ git checkout master
-error: The following untracked working tree files would be overwritten by checkout:
-[…]
-```
-
-...followed by a long list of files. To avoid this error, remove
-the `resources` and `tools` directories before switching branches:
-
-```
-$ rm -r resources/ tools/
-$ git checkout master
-Switched to branch 'master'
-Your branch is up-to-date with 'origin/master'
-```
-
-When moving in the opposite direction, i.e. to a commit that does have
-submodules, you will need to `git submodule update`, as above. If git
-throws an error like:
-
-```
-fatal: No url found for submodule path 'resources/webidl2/test/widlproc' in .gitmodules
-Failed to recurse into submodule path 'resources/webidl2'
-fatal: No url found for submodule path 'tools/html5lib' in .gitmodules
-Failed to recurse into submodule path 'resources'
-Failed to recurse into submodule path 'tools'
-```
-
-...then remove the `tools` and `resources` directories, as above.
-
 <span id="windows-notes">Windows Notes</span>
 =============================================
 
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom/CSSStyleSheet-constructable.html b/third_party/blink/web_tests/external/wpt/css/cssom/CSSStyleSheet-constructable.html
index c7ae5f2..c30e7641 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom/CSSStyleSheet-constructable.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom/CSSStyleSheet-constructable.html
@@ -575,4 +575,14 @@
   document.body.offsetTop;
 }, 'Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.');
 
+test(() => {
+  const host = document.createElement("div");
+  thirdSection.appendChild(host);
+  const root = host.attachShadow({mode: "open"});
+  const sheet = new CSSStyleSheet();
+  root.adoptedStyleSheets = [sheet];
+  host.remove();
+  sheet.replaceSync('');
+}, 'Modifying an adopted stylesheet on a disconnected shadow root should not crash.');
+
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
index cb624a9..17280af 100644
--- a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
+++ b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
@@ -50,6 +50,7 @@
   assert_equals(serialize(root), '<root xmlns="urn:bar"><outer xmlns=""><inner>value1</inner></outer></root>');
 }, 'Check if there is no redundant empty namespace declaration.');
 
+// https://github.com/w3c/DOM-Parsing/issues/47
 test(function() {
   assert_equals(serialize(parse('<root><child xmlns=""/></root>')),
                 '<root><child/></root>');
@@ -97,12 +98,12 @@
                 'Should choose the nearest prefix');
 }, 'Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix');
 
+// https://github.com/w3c/DOM-Parsing/issues/45
 test(function() {
   let root = parse('<el1 xmlns:p="u1" xmlns:q="u1"><el2 xmlns:q="u2"/></el1>');
   root.firstChild.setAttributeNS('u1', 'name', 'v');
   assert_equals(serialize(root),
                 '<el1 xmlns:p="u1" xmlns:q="u1"><el2 xmlns:q="u2" q:name="v"/></el1>');
-  // Maybe this is a specification error.
 }, 'Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix even if the prefix is assigned to another namespace.');
 
 test(function() {
@@ -117,6 +118,7 @@
                 '<r xmlns:xx="uri"><b xx:name="value"/></r>');
 }, 'Check if the prefix of an attribute is replaced with another existing prefix mapped to the same namespace URI.');
 
+// https://github.com/w3c/DOM-Parsing/issues/29
 test(function() {
   let root = parse('<r xmlns:xx="uri"></r>');
   root.setAttributeNS('uri2', 'p:name', 'value');
@@ -183,6 +185,7 @@
   assert_equals(serialize(root), '<root xmlns:p="uri2"><p:child xmlns:p="uri1"/></root>');
 }, 'Check if start tag serialization applied the original prefix even if it is declared in an ancestor element.');
 
+// https://github.com/w3c/DOM-Parsing/issues/52
 test(function() {
   assert_equals(serialize(parse('<root xmlns:x="uri1"><table xmlns="uri1"></table></root>')),
       '<root xmlns:x="uri1"><x:table xmlns="uri1"/></root>');
@@ -196,11 +199,12 @@
   assert_equals(serialize(root), '<root><child1 xmlns:ns1="uri1" ns1:attr1="value1" xmlns:ns2="uri2" ns2:attr2="value2"/><child2 xmlns:ns3="uri3" ns3:attr3="value3"/></root>');
 }, 'Check if generated prefixes match to "ns${index}".');
 
+// https://github.com/w3c/DOM-Parsing/issues/44
+// According to 'DOM Parsing and Serialization' draft as of 2018-12-11,
+// 'generate a prefix' result can conflict with an existing xmlns:ns* declaration.
 test(function() {
   const root = parse('<root xmlns:ns2="uri2"><child xmlns:ns1="uri1"/></root>');
   root.firstChild.setAttributeNS('uri3', 'attr1', 'value1');
-  // According to 'DOM Parsing and Serialization' draft as of 2018-12-11,
-  // 'generate a prefix' result can conflict with an existing xmlns:ns* declaration.
   assert_equals(serialize(root), '<root xmlns:ns2="uri2"><child xmlns:ns1="uri1" xmlns:ns1="uri3" ns1:attr1="value1"/></root>');
 }, 'Check if "ns1" is generated even if the element already has xmlns:ns1.');
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time-datalist-crash.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time-datalist-crash.html
new file mode 100644
index 0000000..2964032e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/time-datalist-crash.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-input-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="newParent"></div>
+<datalist id="suggestions">
+  <option>12:00</option>
+  <input type="time" list="suggestions">
+</datalist>
+<script>
+  test(() => {
+    document.body.offsetTop;
+    newParent.appendChild(suggestions);
+  }, "Moving a datalist enclosing an input type=time using that list should not crash.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/README.md b/third_party/blink/web_tests/external/wpt/interfaces/README.md
index 741cf1c..f70ffd2e 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/README.md
+++ b/third_party/blink/web_tests/external/wpt/interfaces/README.md
@@ -2,4 +2,4 @@
 
 The `.idl` files are extracted from specs by [Reffy](https://github.com/tidoust/reffy) into [reffy-reports](https://github.com/tidoust/reffy-reports), and a [sync script](https://github.com/tidoust/reffy-reports/blob/master/wpt-sync/sync.js) sends [automatic PRs](https://github.com/web-platform-tests/wpt/pulls/autofoolip). The PRs require manual review but can be approved/merged by anyone with write access.
 
-If some IDL in this directory is not up to date with the spec, [file an issue on reffy-reports](https://github.com/tidoust/reffy-reports/issues) and notify @foolip and @lukebjerring.
+If some IDL in this directory is not up to date with the spec, first look for an [open PR](https://github.com/web-platform-tests/wpt/pulls/autofoolip) and if there is none [file an issue on reffy-reports](https://github.com/tidoust/reffy-reports/issues) and notify @foolip and @lukebjerring.
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/request.py b/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/request.py
index 1b1061b..a80bc0c 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/request.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptserve/wptserve/request.py
@@ -556,7 +556,7 @@
             return dict.__getitem__(self, key)[0]
         elif default is not missing:
             return default
-        raise KeyError
+        raise KeyError(key)
 
     def last(self, key, default=missing):
         """Get the last value with a given key
@@ -570,7 +570,7 @@
             return dict.__getitem__(self, key)[-1]
         elif default is not missing:
             return default
-        raise KeyError
+        raise KeyError(key)
 
     def get_list(self, key):
         """Get all values with a given key as a list
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
index 4a7f137..6d4ab50 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
@@ -190,7 +190,8 @@
     t.add_cleanup(() => pc2.close());
 
     const stream = await getNoiseStream({audio: true});
-    pc1.addStream(stream);
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    stream.getTracks().forEach(track => pc1.addTrack(track, stream));
 
     coupleIceCandidates(pc1, pc2);
     doSignalingHandshake(pc1, pc2);
@@ -205,7 +206,8 @@
     t.add_cleanup(() => pc2.close());
 
     const stream = await getNoiseStream({audio: true, video:true});
-    pc1.addStream(stream);
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    stream.getTracks().forEach(track => pc1.addTrack(track, stream));
 
     coupleIceCandidates(pc1, pc2);
     doSignalingHandshake(pc1, pc2);
@@ -221,6 +223,7 @@
 
     caller.addTransceiver('audio', {direction:'recvonly'});
     const stream = await navigator.mediaDevices.getUserMedia({audio:true});
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     callee.addTrack(track, stream);
     coupleIceCandidates(caller, callee);
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html
index e74495fec..8c0552d 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https.html
@@ -86,5 +86,67 @@
       assert_equals(caller_transceiver1.receiver.transport,
                     caller_transceiver2.receiver.transport);
     }, 'RTCRtpSender/receiver.transport at the right time, with bundle policy ' + bundle_policy);
+
+    // Do the same test again, with DataChannel in the mix.
+    promise_test(async t => {
+        const caller = new RTCPeerConnection({bundlePolicy: bundle_policy});
+      t.add_cleanup(() => caller.close());
+      const stream = await navigator.mediaDevices.getUserMedia(
+          {audio: true, video:true});
+      t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+      const [track1, track2] = stream.getTracks();
+      const sender1 = caller.addTrack(track1);
+      const sender2 = caller.addTrack(track2);
+      caller.createDataChannel('datachannel');
+      const callee = new RTCPeerConnection();
+      t.add_cleanup(() => callee.close());
+      exchangeIceCandidates(caller, callee);
+      const offer = await caller.createOffer();
+      assert_equals(sender1.transport, null);
+      assert_equals(sender2.transport, null);
+      if (caller.sctp) {
+        assert_equals(caller.sctp.transport, null);
+      }
+      await caller.setLocalDescription(offer);
+      assert_not_equals(sender1.transport, null);
+      assert_not_equals(sender2.transport, null);
+      assert_not_equals(caller.sctp.transport, null);
+      const [caller_transceiver1, caller_transceiver2] = caller.getTransceivers();
+      assert_equals(sender1.transport, caller_transceiver1.sender.transport);
+      if (bundle_policy == 'max-bundle') {
+        assert_equals(caller_transceiver1.sender.transport,
+                      caller_transceiver2.sender.transport);
+        assert_equals(caller_transceiver1.sender.transport,
+                      caller.sctp.transport);
+      } else {
+        assert_not_equals(caller_transceiver1.sender.transport,
+                          caller_transceiver2.sender.transport);
+        assert_not_equals(caller_transceiver1.sender.transport,
+                      caller.sctp.transport);
+      }
+      await callee.setRemoteDescription(offer);
+      const [callee_transceiver1, callee_transceiver2] = callee.getTransceivers();
+      // According to spec, setRemoteDescription only updates the transports
+      // if the remote description is an answer.
+      assert_equals(callee_transceiver1.receiver.transport, null);
+      assert_equals(callee_transceiver2.receiver.transport, null);
+      const answer = await callee.createAnswer();
+      await callee.setLocalDescription(answer);
+      assert_not_equals(callee_transceiver1.receiver.transport, null);
+      assert_not_equals(callee_transceiver2.receiver.transport, null);
+      assert_not_equals(callee.sctp.transport, null);
+      // At this point, bundle should have kicked in.
+      assert_equals(callee_transceiver1.receiver.transport,
+                    callee_transceiver2.receiver.transport);
+      assert_equals(callee_transceiver1.receiver.transport,
+                    callee.sctp.transport,
+                    'Callee SCTP transport does not match:');
+      await caller.setRemoteDescription(answer);
+      assert_equals(caller_transceiver1.receiver.transport,
+                    caller_transceiver2.receiver.transport);
+      assert_equals(caller_transceiver1.receiver.transport,
+                    caller.sctp.transport,
+                    'Caller SCTP transport does not match:');
+    }, 'RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy ' + bundle_policy);
   }
  </script>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr-ref.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr-ref.html
new file mode 100644
index 0000000..42b6a5da
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, bidi ruby</title>
+ <meta charset="UTF-8">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    text-align: center;
+    unicode-bidi: -webkit-plaintext;
+    unicode-bidi: plaintext;
+    direction: ltr;
+    writing-mode: vertical-lr;
+    -webkit-writing-mode: vertical-lr;
+}
+.cue > span {
+    font-family: sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: white;
+}
+</style>
+<div class="video"><span class="cue"><span><ruby>右<rt>みぎ</rt></ruby>に<ruby> 見<rt>み</rt></ruby>えるのは...<br>二行目</span></span></div>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html
new file mode 100644
index 0000000..c5e7d2b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_lr.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, vertical growing left to right</title>
+<link rel="match" href="vertical_lr-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="../support/bidi_vertical_lr.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl-ref.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl-ref.html
new file mode 100644
index 0000000..737c695
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, bidi ruby</title>
+ <meta charset="UTF-8">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    text-align: center;
+    unicode-bidi: -webkit-plaintext;
+    unicode-bidi: plaintext;
+    direction: ltr;
+    writing-mode: vertical-rl;
+    -webkit-writing-mode: vertical-rl;
+}
+.cue > span {
+    font-family: sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: white;
+}
+</style>
+<div class="video"><span class="cue"><span><ruby></ruby>に<ruby> 見<rt>み</rt></ruby>えるのは...<br>二行目</span></span></div>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html
new file mode 100644
index 0000000..9afdb2d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/vertical_rl.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, vertical growing right to left</title>
+<link rel="match" href="vertical_rl-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="../support/bidi_vertical_rl.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position-ref.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position-ref.html
new file mode 100644
index 0000000..53405cf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, bidi ruby</title>
+ <meta charset="UTF-8">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    text-align: center;
+    unicode-bidi: -webkit-plaintext;
+    unicode-bidi: plaintext;
+    direction: ltr;
+    writing-mode: vertical-rl;
+    -webkit-writing-mode: vertical-rl;
+}
+.cue > span {
+    -webkit-ruby-position: after;
+    ruby-position: under;
+    ruby-position: left;
+    font-family: Ahem, sans-serif;
+    background: #0f0 url('../../media/background.gif') repeat-x top left;
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span><ruby></ruby>に<ruby> 見<rt>み</rt></ruby>えるのは...</span></span></div>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html
new file mode 100644
index 0000000..371a296
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_ruby-position.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, vertical cue with ruby position left</title>
+<link rel="match" href="vertical_ruby-position-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    ruby-position: left;
+    font-family: Ahem, sans-serif;
+    background: #0f0 url('../../media/background.gif') repeat-x top left;
+    color: green;
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="../../support/bidi_vertical_rl-oneline.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright-ref.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright-ref.html
new file mode 100644
index 0000000..270bb790
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, bidi ruby</title>
+ <meta charset="UTF-8">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    text-align: center;
+    unicode-bidi: -webkit-plaintext;
+    unicode-bidi: plaintext;
+    direction: ltr;
+    writing-mode: vertical-lr;
+    -webkit-writing-mode: vertical-lr;
+}
+.cue > span {
+    font-family: Ahem, sans-serif;
+    background: #0f0 url('../../media/background.gif') repeat-x top left;
+    color: green;
+}
+.combine {
+    -webkit-text-combine: horizontal;
+    text-combine-upright: all;
+}
+</style>
+<div class="video"><span class="cue"><span>平成<span class="combine">20</span>年4月<span class="combine">16</span>日に</span></span></div>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html
new file mode 100644
index 0000000..5aebd03
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue/vertical_text-combine-upright.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, vertical cue with text-combine-upright digits</title>
+<link rel="match" href="vertical_text-combine-upright-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue {
+    text-combine-upright: digits;
+    font-family: Ahem, sans-serif;
+    background: #0f0 url('../../media/background.gif') repeat-x top left;
+    color: green;
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="../../support/bidi_text-combine-upright.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright-ref.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright-ref.html
new file mode 100644
index 0000000..09491271
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Reference for WebVTT rendering, bidi ruby</title>
+ <meta charset="UTF-8">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+.video {
+    display: inline-block;
+    width: 320px;
+    height: 180px;
+    position: relative;
+    font-size: 9px;
+}
+.cue {
+    position: absolute;
+    bottom: 0;
+    text-align: center;
+    unicode-bidi: -webkit-plaintext;
+    unicode-bidi: plaintext;
+    direction: ltr;
+    writing-mode: vertical-lr;
+    -webkit-writing-mode: vertical-lr;
+}
+.cue > span {
+    font-family: sans-serif;
+    background: rgba(0,0,0,0.8);
+    color: white;
+}
+.combine {
+    -webkit-text-combine: horizontal;
+    text-combine-upright: all;
+    font-family: Ahem, sans-serif;
+    background: #0f0 url('../../media/background.gif') repeat-x top left;
+    color: green;
+}
+</style>
+<div class="video"><span class="cue"><span>平成<span class="combine">20</span>年4月<span class="combine">16</span>日に</span></span></div>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html
new file mode 100644
index 0000000..bf99691c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_vertical_text-combine-upright.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>WebVTT rendering, vertical cue with class to apply text-combine-upright all</title>
+<link rel="match" href="class_vertical_text-combine-upright-ref.html">
+<style>
+html { overflow:hidden }
+body { margin:0 }
+::cue(.combine) {
+    text-combine-upright: all;
+    font-family: Ahem, sans-serif;
+    background: #0f0 url('../../media/background.gif') repeat-x top left;
+    color: green;
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<video width="320" height="180" autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+    <source src="/media/white.webm" type="video/webm">
+    <source src="/media/white.mp4" type="video/mp4">
+    <track src="../../../support/class_text-combine-upright.vtt">
+    <script>
+    document.getElementsByTagName('track')[0].track.mode = 'showing';
+    </script>
+</video>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_text-combine-upright.vtt b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_text-combine-upright.vtt
new file mode 100644
index 0000000..e3c99143
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_text-combine-upright.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 vertical:lr
+平成20年4月16日に
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_lr.vtt b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_lr.vtt
new file mode 100644
index 0000000..e991973ab
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_lr.vtt
@@ -0,0 +1,5 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 vertical:lr
+<ruby>左<rt>ひだり</rt></ruby>に<ruby> 見<rt>み</rt></ruby>えるのは...
+二行目
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl-oneline.vtt b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl-oneline.vtt
new file mode 100644
index 0000000..30297e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl-oneline.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 vertical:rl
+<ruby>右<rt>みぎ</rt></ruby>に<ruby> 見<rt>み</rt></ruby>えるのは...
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl.vtt b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl.vtt
new file mode 100644
index 0000000..56fcbd41
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/bidi_vertical_rl.vtt
@@ -0,0 +1,5 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 vertical:rl
+<ruby>右<rt>みぎ</rt></ruby>に<ruby> 見<rt>み</rt></ruby>えるのは...
+二行目
diff --git a/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/class_text-combine-upright.vtt b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/class_text-combine-upright.vtt
new file mode 100644
index 0000000..064f75ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webvtt/rendering/cues-with-video/processing-model/support/class_text-combine-upright.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:05.000 vertical:lr
+平成<c.combine>20</c>年4月<c.combine>16</c>日に
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/japanese-era-names.html b/third_party/blink/web_tests/fast/forms/calendar-picker/japanese-era-names.html
index e6d63a1..83b0a440 100644
--- a/third_party/blink/web_tests/fast/forms/calendar-picker/japanese-era-names.html
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/japanese-era-names.html
@@ -22,7 +22,7 @@
   const SYOUWA = '\u662d\u548c';
   const TAISYOU = '\u5927\u6b63';
   const MEIJI = '\u660e\u6cbb';
-  const JAN = 0, APR = 3, MAY = 4, JUL = 6, AUG = 7, DEC = 11;
+  const JAN = 0, APR = 3, MAY = 4, JUL = 6, AUG = 7, OCT = 9, NOV = 10, DEC = 11;
   assert_equals(pw.formatJapaneseImperialEra(2118, JAN), '');
   assert_equals(pw.formatJapaneseImperialEra(2117, JAN), `(${REIWA}99${NEN})`);
   assert_equals(pw.formatJapaneseImperialEra(2020, JAN), `(${REIWA}2${NEN})`);
@@ -37,7 +37,8 @@
   assert_equals(pw.formatJapaneseImperialEra(1912, AUG), `(${TAISYOU}${GANNEN})`);
   assert_equals(pw.formatJapaneseImperialEra(1912, JUL), `(${MEIJI}45${NEN})`);
   assert_equals(pw.formatJapaneseImperialEra(1869, JAN), `(${MEIJI}2${NEN})`);
-  assert_equals(pw.formatJapaneseImperialEra(1868, JAN), `(${MEIJI}${GANNEN})`);
+  assert_equals(pw.formatJapaneseImperialEra(1868, NOV), `(${MEIJI}${GANNEN})`);
+  assert_equals(pw.formatJapaneseImperialEra(1868, OCT), '');
   assert_equals(pw.formatJapaneseImperialEra(1867, DEC), '');
 }
 
diff --git a/third_party/blink/web_tests/platform/linux/svg/custom/acid3-test-77-expected.txt b/third_party/blink/web_tests/platform/linux/svg/custom/acid3-test-77-expected.txt
new file mode 100644
index 0000000..1c4000f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/svg/custom/acid3-test-77-expected.txt
@@ -0,0 +1,29 @@
+Acid3 test 77, checking text metric functionality
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS text.getNumberOfChars() is 3
+FAIL text.getComputedTextLength() should be 4776. Was 5550.
+FAIL text.getSubStringLength(0,1) should be 42. Was 1775.
+FAIL text.getSubStringLength(0,2) should be 65. Was 3775.
+FAIL text.getSubStringLength(1,1) should be 23. Was 2000.
+PASS text.getSubStringLength(1,0) is 0
+FAIL text.getSubStringLength(1, 3) should be 4734. Was 3775.
+FAIL text.getSubStringLength(0, 4) should be 4776. Was 5550.
+PASS text.getSubStringLength(3, 0) threw exception IndexSizeError: Failed to execute 'getSubStringLength' on 'SVGTextContentElement': The charnum provided (3) is greater than or equal to the maximum bound (3)..
+PASS text.getSubStringLength(-17, 20) threw exception IndexSizeError: Failed to execute 'getSubStringLength' on 'SVGTextContentElement': The charnum provided (4294967279) is greater than the maximum bound (3)..
+PASS text.getStartPositionOfChar(0).x is 0
+FAIL text.getStartPositionOfChar(1).x should be 42. Was 1775.
+FAIL text.getStartPositionOfChar(2).x should be 65. Was 3775.
+PASS text.getStartPositionOfChar(0).y is 4000
+PASS text.getEndPositionOfChar(-1) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4294967295) is greater than the maximum bound (3)..
+PASS text.getEndPositionOfChar(4) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4) is greater than the maximum bound (3)..
+FAIL text.getEndPositionOfChar(0).x should be 42. Was 1775.
+FAIL text.getEndPositionOfChar(1).x should be 65. Was 3775.
+FAIL text.getEndPositionOfChar(2).x should be 4776. Was 5550.
+PASS text.getEndPositionOfChar(-17) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4294967279) is greater than the maximum bound (3)..
+PASS text.getEndPositionOfChar(4) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4) is greater than the maximum bound (3)..
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.png
index 1840bdb..b24ad58 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.txt b/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.txt
index 0ed60fae..c61deaf 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.txt
+++ b/third_party/blink/web_tests/platform/win/fast/forms/caret-rtl-expected.txt
@@ -11,5 +11,5 @@
       LayoutBlockFlow {P} at (0,36) size 784x20
         LayoutText {#text} at (0,0) size 378x19
           text run at (0,0) width 378: "Click in the div, the caret should be on the right edge of the div."
-      LayoutBlockFlow {DIV} at (0,72) size 200x368
+      LayoutBlockFlow {DIV} at (0,72) size 200x380
 caret: position 0 of child 5 {DIV} of body
diff --git a/third_party/blink/web_tests/platform/win/fast/text/font-format-support-color-cff2-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/text/font-format-support-color-cff2-vertical-expected.png
index 81e7782..ec80077 100644
--- a/third_party/blink/web_tests/platform/win/fast/text/font-format-support-color-cff2-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/text/font-format-support-color-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png b/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png
index c58b271..529b371 100644
--- a/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-03-t-expected.png b/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-03-t-expected.png
index 8304830..f46c85c 100644
--- a/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-03-t-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/W3C-SVG-1.1/render-groups-03-t-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/custom/acid3-test-77-expected.txt b/third_party/blink/web_tests/platform/win/svg/custom/acid3-test-77-expected.txt
index 1c4000f..13721d76 100644
--- a/third_party/blink/web_tests/platform/win/svg/custom/acid3-test-77-expected.txt
+++ b/third_party/blink/web_tests/platform/win/svg/custom/acid3-test-77-expected.txt
@@ -3,24 +3,24 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 PASS text.getNumberOfChars() is 3
-FAIL text.getComputedTextLength() should be 4776. Was 5550.
-FAIL text.getSubStringLength(0,1) should be 42. Was 1775.
-FAIL text.getSubStringLength(0,2) should be 65. Was 3775.
+FAIL text.getComputedTextLength() should be 4776. Was 5500.
+FAIL text.getSubStringLength(0,1) should be 42. Was 1750.
+FAIL text.getSubStringLength(0,2) should be 65. Was 3750.
 FAIL text.getSubStringLength(1,1) should be 23. Was 2000.
 PASS text.getSubStringLength(1,0) is 0
-FAIL text.getSubStringLength(1, 3) should be 4734. Was 3775.
-FAIL text.getSubStringLength(0, 4) should be 4776. Was 5550.
+FAIL text.getSubStringLength(1, 3) should be 4734. Was 3750.
+FAIL text.getSubStringLength(0, 4) should be 4776. Was 5500.
 PASS text.getSubStringLength(3, 0) threw exception IndexSizeError: Failed to execute 'getSubStringLength' on 'SVGTextContentElement': The charnum provided (3) is greater than or equal to the maximum bound (3)..
 PASS text.getSubStringLength(-17, 20) threw exception IndexSizeError: Failed to execute 'getSubStringLength' on 'SVGTextContentElement': The charnum provided (4294967279) is greater than the maximum bound (3)..
 PASS text.getStartPositionOfChar(0).x is 0
-FAIL text.getStartPositionOfChar(1).x should be 42. Was 1775.
-FAIL text.getStartPositionOfChar(2).x should be 65. Was 3775.
+FAIL text.getStartPositionOfChar(1).x should be 42. Was 1750.
+FAIL text.getStartPositionOfChar(2).x should be 65. Was 3750.
 PASS text.getStartPositionOfChar(0).y is 4000
 PASS text.getEndPositionOfChar(-1) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4294967295) is greater than the maximum bound (3)..
 PASS text.getEndPositionOfChar(4) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4) is greater than the maximum bound (3)..
-FAIL text.getEndPositionOfChar(0).x should be 42. Was 1775.
-FAIL text.getEndPositionOfChar(1).x should be 65. Was 3775.
-FAIL text.getEndPositionOfChar(2).x should be 4776. Was 5550.
+FAIL text.getEndPositionOfChar(0).x should be 42. Was 1750.
+FAIL text.getEndPositionOfChar(1).x should be 65. Was 3750.
+FAIL text.getEndPositionOfChar(2).x should be 4776. Was 5500.
 PASS text.getEndPositionOfChar(-17) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4294967279) is greater than the maximum bound (3)..
 PASS text.getEndPositionOfChar(4) threw exception IndexSizeError: Failed to execute 'getEndPositionOfChar' on 'SVGTextContentElement': The charnum provided (4) is greater than the maximum bound (3)..
 PASS successfullyParsed is true
diff --git a/third_party/blink/web_tests/platform/win7/fast/text/font-format-support-color-cff2-vertical-expected.png b/third_party/blink/web_tests/platform/win7/fast/text/font-format-support-color-cff2-vertical-expected.png
index 6ddb66c..6d35a9b 100644
--- a/third_party/blink/web_tests/platform/win7/fast/text/font-format-support-color-cff2-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/text/font-format-support-color-cff2-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/text/unicode-fallback-font-expected.png b/third_party/blink/web_tests/platform/win7/fast/text/unicode-fallback-font-expected.png
index 9e729ac5..bb6dccb 100644
--- a/third_party/blink/web_tests/platform/win7/fast/text/unicode-fallback-font-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/text/unicode-fallback-font-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/module-worker-redirect-upgrade.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/module-worker-redirect-upgrade.https-expected.txt
new file mode 100644
index 0000000..ebbfd0bb
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/module-worker-redirect-upgrade.https-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS secure/same-origin => secure/same-origin worker
+FAIL insecure/same-origin => secure/same-origin worker promise_test: Unhandled rejection with value: object "SecurityError: Failed to construct 'Worker': Script at 'http://web-platform.test:8444/upgrade-insecure-requests/support/redirect-cors.py?location=https%3A%2F%2Fweb-platform.test%3A8444%2Fcommon%2Fsecurity-features%2Fsubresource%2Fworker.py' cannot be accessed from origin 'https://web-platform.test:8444'."
+FAIL secure/same-origin => insecure/same-origin worker promise_test: Unhandled rejection with value: object "[object Event]"
+FAIL insecure/same-origin => insecure/same-origin worker promise_test: Unhandled rejection with value: object "SecurityError: Failed to construct 'Worker': Script at 'http://web-platform.test:8444/upgrade-insecure-requests/support/redirect-cors.py?location=http%3A%2F%2Fweb-platform.test%3A8444%2Fcommon%2Fsecurity-features%2Fsubresource%2Fworker.py' cannot be accessed from origin 'https://web-platform.test:8444'."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/worker-redirect-upgrade.https-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/worker-redirect-upgrade.https-expected.txt
new file mode 100644
index 0000000..ebbfd0bb
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/upgrade-insecure-requests/worker-redirect-upgrade.https-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+PASS secure/same-origin => secure/same-origin worker
+FAIL insecure/same-origin => secure/same-origin worker promise_test: Unhandled rejection with value: object "SecurityError: Failed to construct 'Worker': Script at 'http://web-platform.test:8444/upgrade-insecure-requests/support/redirect-cors.py?location=https%3A%2F%2Fweb-platform.test%3A8444%2Fcommon%2Fsecurity-features%2Fsubresource%2Fworker.py' cannot be accessed from origin 'https://web-platform.test:8444'."
+FAIL secure/same-origin => insecure/same-origin worker promise_test: Unhandled rejection with value: object "[object Event]"
+FAIL insecure/same-origin => insecure/same-origin worker promise_test: Unhandled rejection with value: object "SecurityError: Failed to construct 'Worker': Script at 'http://web-platform.test:8444/upgrade-insecure-requests/support/redirect-cors.py?location=http%3A%2F%2Fweb-platform.test%3A8444%2Fcommon%2Fsecurity-features%2Fsubresource%2Fworker.py' cannot be accessed from origin 'https://web-platform.test:8444'."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
index 7fab2c6..66dbc45 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
@@ -2,7 +2,10 @@
 PASS RTCRtpSender.transport is null when unconnected
 FAIL RTCRtpSender/receiver.transport has a value when connected assert_not_equals: got disallowed value null
 FAIL RTCRtpSender/receiver.transport at the right time, with bundle policy balanced assert_not_equals: got disallowed value null
+FAIL RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy balanced assert_not_equals: got disallowed value null
 FAIL RTCRtpSender/receiver.transport at the right time, with bundle policy max-bundle assert_not_equals: got disallowed value null
+FAIL RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy max-bundle assert_not_equals: got disallowed value null
 FAIL RTCRtpSender/receiver.transport at the right time, with bundle policy max-compat assert_not_equals: got disallowed value null
+FAIL RTCRtpSender/receiver/SCTP transport at the right time, with bundle policy max-compat assert_not_equals: got disallowed value null
 Harness: the test ran to completion.
 
diff --git a/tools/binary_size/libsupersize/console.py b/tools/binary_size/libsupersize/console.py
index 2d4bc76..85c30f2c6 100644
--- a/tools/binary_size/libsupersize/console.py
+++ b/tools/binary_size/libsupersize/console.py
@@ -85,7 +85,6 @@
     self._output_directory_finder = output_directory_finder
     self._tool_prefix_finder = tool_prefix_finder
     self._size_infos = size_infos
-    self._disassemble_prefix_len = None
 
     if len(size_infos) == 1:
       self._variables['size_info'] = size_infos[0]
@@ -261,26 +260,6 @@
         'ensure {} is located beside {}, or pass its path explicitly using '
         'elf_path=').format(os.path.basename(filename), size_info.size_path)
 
-  def _DetectDisassemblePrefixLen(self, args):
-    # Look for a line that looks like:
-    # /usr/{snip}/src/out/Release/../../net/quic/core/quic_time.h:100
-    output = subprocess.check_output(args)
-    for line in output.splitlines():
-      if line and line[0] == os.path.sep and line[-1].isdigit():
-        release_idx = line.find('Release')
-        if release_idx != -1:
-          return line.count(os.path.sep, 0, release_idx)
-        dot_dot_idx = line.find('..')
-        if dot_dot_idx != -1:
-          return line.count(os.path.sep, 0, dot_dot_idx) - 1
-        out_idx = line.find(os.path.sep + 'out')
-        if out_idx != -1:
-          return line.count(os.path.sep, 0, out_idx) + 2
-        logging.warning('Could not guess source path from found path.')
-        return None
-    logging.warning('Found no source paths in objdump output.')
-    return None
-
   def _SizeInfoForSymbol(self, symbol):
     for size_info in self._size_infos:
       if symbol in size_info.raw_symbols:
@@ -302,32 +281,27 @@
                                   'passing .before_symbol or .after_symbol.')
     size_info = self._SizeInfoForSymbol(symbol)
     tool_prefix = self._ToolPrefixForSymbol(size_info)
-    elf_path = self._ElfPathForSymbol(
-        size_info, tool_prefix, elf_path)
+    elf_path = self._ElfPathForSymbol(size_info, tool_prefix, elf_path)
+    # Always use Android NDK's objdump because llvm-objdump does not seem to
+    # correctly disassemble.
+    output_directory_finder = self._output_directory_finder
+    if not output_directory_finder.Tentative():
+      output_directory_finder = path_util.OutputDirectoryFinder(
+          any_path_within_output_directory=elf_path)
+    tool_prefix = path_util.ToolPrefixFinder(
+        output_directory_finder=output_directory_finder,
+        linker_name='ld').Finalized()
 
-    args = [path_util.GetObjDumpPath(tool_prefix), '--disassemble', '--source',
-            '--line-numbers', '--start-address=0x%x' % symbol.address,
-            '--stop-address=0x%x' % symbol.end_address, elf_path]
-    # llvm-objdump does not support '--demangle' switch.
-    if not self._tool_prefix_finder.IsLld():
-      args.append('--demangle')
-    if self._disassemble_prefix_len is None:
-      prefix_len = self._DetectDisassemblePrefixLen(args)
-      if prefix_len is not None:
-        self._disassemble_prefix_len = prefix_len
-
-    if self._disassemble_prefix_len is not None:
-      output_directory = self._output_directory_finder.Tentative()
-      # Only matters for non-generated paths, so be lenient here.
-      if output_directory is None:
-        output_directory = os.path.join(path_util.SRC_ROOT, 'out', 'Release')
-        if not os.path.exists(output_directory):
-          os.makedirs(output_directory)
-
-      args += [
-          '--prefix-strip', str(self._disassemble_prefix_len),
-          '--prefix', os.path.normpath(os.path.relpath(output_directory))
-      ]
+    args = [
+        path_util.GetObjDumpPath(tool_prefix),
+        '--disassemble',
+        '--source',
+        '--line-numbers',
+        '--demangle',
+        '--start-address=0x%x' % symbol.address,
+        '--stop-address=0x%x' % symbol.end_address,
+        elf_path,
+    ]
 
     proc = subprocess.Popen(args, stdout=subprocess.PIPE)
     lines = itertools.chain(('Showing disassembly for %r' % symbol,
diff --git a/tools/clang/blink_gc_plugin/tests/crash_on_invalid.txt b/tools/clang/blink_gc_plugin/tests/crash_on_invalid.txt
index cf19ff5..0906b51 100644
--- a/tools/clang/blink_gc_plugin/tests/crash_on_invalid.txt
+++ b/tools/clang/blink_gc_plugin/tests/crash_on_invalid.txt
@@ -1,5 +1,5 @@
 In file included from crash_on_invalid.cpp:5:
-./crash_on_invalid.h:16:30: error: unknown template name 'GarbageCollectedFinalized'
+./crash_on_invalid.h:16:30: error: no template named 'GarbageCollectedFinalized'
 class Gamepad final : public GarbageCollectedFinalized<Gamepad>,
                              ^
 ./crash_on_invalid.h:20:19: error: unknown type name 'WrapperTypeInfo'
diff --git a/tools/clang/blink_gc_plugin/tests/test.py b/tools/clang/blink_gc_plugin/tests/test.py
index d49e2be..4f0fd34 100755
--- a/tools/clang/blink_gc_plugin/tests/test.py
+++ b/tools/clang/blink_gc_plugin/tests/test.py
@@ -22,6 +22,11 @@
     clang_cmd.append('-Wno-inaccessible-base')
 
   def ProcessOneResult(self, test_name, actual):
+    # TODO(crbug.com/961204): Enable after the next clang roll.
+    if test_name == 'crash_on_invalid':
+      sys.stdout.write('(skipping due to crbug.com/961204) ')
+      return
+
     # Some Blink GC plugins dump a JSON representation of the object graph, and
     # use the processed results as the actual results of the test.
     if os.path.exists('%s.graph.json' % test_name):
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 705a620..6ca8802 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -20293,6 +20293,15 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="SyncPreferences_ManageGoogleAccountClicked">
+  <owner>triploblastic@google.com</owner>
+  <owner>msarda@chromium.org</owner>
+  <description>
+    Used clicked 'Manage your Google Account' in sync and google services menu
+    in android.
+  </description>
+</action>
+
 <action name="SystemBack">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 10dfa0a..9d18ed2 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4601,6 +4601,7 @@
   <int value="213" label="INVALID_INITIATOR_ORIGIN"/>
   <int value="214" label="RFHI_BEGIN_NAVIGATION_MISSING_INITIATOR_ORIGIN"/>
   <int value="215" label="RFHI_BEGIN_NAVIGATION_NON_WEBBY_TRANSITION"/>
+  <int value="216" label="RFH_NO_MATCHING_NAVIGATION_REQUEST_ON_COMMIT"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">
@@ -23147,6 +23148,7 @@
   <int value="2887"
       label="V8RTCRtpReceiver_JitterBufferDelayHint_AttributeSetter"/>
   <int value="2888" label="MediaCapabilitiesDecodingInfoWithKeySystemConfig"/>
+  <int value="2889" label="RevertInCustomIdent"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 0ad0c3e..872acf0 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -138,6 +138,7 @@
       }
     ],
     'platform': 'android-chrome',
+      'browser': 'bin/monochrome_64_32_bundle',
     'dimension': {
       'pool': 'chrome.tests.perf-fyi',
       'os': 'Android',
@@ -1037,6 +1038,8 @@
   # For trybot testing we always use the reference build
   if tester_config.get('testing', False):
     browser_name = 'reference'
+  elif 'browser' in tester_config:
+    browser_name = 'exact'
   elif tester_config['platform'] == 'android':
     browser_name = 'android-chromium'
   elif tester_config['platform'].startswith('android-'):
@@ -1053,7 +1056,13 @@
     '--upload-results'
   ]
 
-  if browser_name.startswith('android-webview'):
+  if 'browser' in tester_config:
+    test_args.append('--browser-executable=../../out/Release/%s' %
+                     tester_config['browser'])
+    if tester_config['platform'].startswith('android'):
+      test_args.append('--device=android')
+
+  if tester_config['platform'].startswith('android-webview'):
     test_args.append(
         '--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk')
 
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 06231c24..758dd41 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -97,6 +97,61 @@
       }
     self.assertEquals(returned_test, expected_generated_test)
 
+  def testGeneratePerformanceTestSuiteExact(self):
+    swarming_dimensions = [
+        {'os': 'SkyNet', 'pool': 'T-RIP'}
+    ]
+    test_config = {
+        'platform': 'android-webview',
+        'browser': 'bin/monochrome_64_32_bundle',
+        'dimension': swarming_dimensions,
+    }
+    test = {
+        'isolate': 'performance_test_suite',
+        'extra_args': [
+            '--run-ref-build',
+            '--test-shard-map-filename=shard_map.json',
+          ],
+        'num_shards': 26
+    }
+    returned_test = perf_data_generator.generate_performance_test(
+        test_config, test)
+
+    expected_generated_test = {
+        'override_compile_targets': ['performance_test_suite'],
+        'isolate_name': 'performance_test_suite',
+        'args': ['-v', '--browser=exact', '--upload-results',
+                 '--browser-executable=../../out/Release'
+                 '/bin/monochrome_64_32_bundle',
+                 '--device=android',
+                 '--webview-embedder-apk=../../out/Release'
+                 '/apks/SystemWebViewShell.apk',
+                 '--run-ref-build',
+                 '--test-shard-map-filename=shard_map.json'],
+        'trigger_script': {
+          'args': [
+            '--multiple-dimension-script-verbose',
+            'True'
+          ],
+          'requires_simultaneous_shard_dispatch': True,
+          'script': '//testing/trigger_scripts/perf_device_trigger.py'
+        },
+        'merge': {
+          'script': '//tools/perf/process_perf_results.py'
+        },
+        'swarming': {
+          'ignore_task_failure': False,
+          'can_use_on_swarming_builders': True,
+          'expiration': 7200,
+          'io_timeout': 1800,
+          'hard_timeout': 36000,
+          'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
+          'shards': 26
+        },
+        'name': 'performance_test_suite'
+      }
+    self.assertEquals(returned_test, expected_generated_test)
+
   def testGeneratePerformanceTestSuiteWebview(self):
     swarming_dimensions = [
         {'os': 'SkyNet', 'pool': 'T-RIP'}
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py
index 28c26c2d..722db78 100644
--- a/tools/perf/core/perf_json_config_validator.py
+++ b/tools/perf/core/perf_json_config_validator.py
@@ -86,17 +86,18 @@
   browser_options = _ParseBrowserFlags(test_config['args'])
   if 'WebView' in builder_name or 'webview' in builder_name:
     if browser_options.browser not in (
-        'android-webview', 'android-webview-google'):
+        'android-webview', 'android-webview-google', 'exact'):
       raise ValueError(
-          "%s must use 'android-webview' or 'android-webview-google' browser" %
-          builder_name)
+          "%s must use 'android-webview', 'android-webview-google' or 'exact' "
+          "browser" % builder_name)
     if not browser_options.webview_embedder_apk:
       raise ValueError('%s must set --webview-embedder-apk flag' % builder_name)
   elif 'Android' in builder_name or 'android' in builder_name:
-    if browser_options.browser not in ('android-chromium', 'android-chrome'):
+    if browser_options.browser not in (
+        'android-chromium', 'android-chrome', 'exact'):
       raise ValueError(
-          "%s must use 'android-chromium' or 'android-chrome' browser" %
-          builder_name)
+          "%s must use 'android-chromium', 'android-chrome' or 'exact' "
+          "browser" % builder_name)
   elif builder_name in ('win-10-perf', 'Win 7 Nvidia GPU Perf'):
     if browser_options.browser != 'release_x64':
       raise ValueError("%s must use 'release_x64' browser type" %
diff --git a/ui/file_manager/file_manager/background/js/file_operation_util.js b/ui/file_manager/file_manager/background/js/file_operation_util.js
index cdbebe7..09a2fd6 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_util.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_util.js
@@ -133,6 +133,14 @@
         maybeInvokeCallback();
       };
 
+      // If this is recursing through a very large directory structure (1000's
+      // of files), this will flood the browser process with IPC because each
+      // getEntryMetadata call causes an IPC to the browser. This can cause the
+      // browser to become unresponsive, and may even be killed by a watchdog
+      // timer. To prevent this, limit the number of concurrent getEntryMetadata
+      // calls to a reasonable level.
+      const metadataFetchQueue = new AsyncUtil.ConcurrentQueue(64);
+
       const process = entry => {
         numRunningTasks++;
         result.push(entry);
@@ -162,11 +170,17 @@
           }, onError);
         } else {
           // For a file, annotate the file size.
-          metadataProxy.getEntryMetadata(entry).then(metadata => {
-            entry.size = metadata.size;
-            --numRunningTasks;
-            maybeInvokeCallback();
-          }, onError);
+          metadataFetchQueue.run(done => {
+            metadataProxy.getEntryMetadata(entry)
+                .then(
+                    metadata => {
+                      entry.size = metadata.size;
+                      --numRunningTasks;
+                      maybeInvokeCallback();
+                    },
+                    onError)
+                .finally(done);
+          });
         }
       };
 
diff --git a/ui/file_manager/file_manager/common/js/async_util.js b/ui/file_manager/file_manager/common/js/async_util.js
index b48a34eb..58205ed4 100644
--- a/ui/file_manager/file_manager/common/js/async_util.js
+++ b/ui/file_manager/file_manager/common/js/async_util.js
@@ -115,24 +115,13 @@
  * @private
  */
 AsyncUtil.ConcurrentQueue.prototype.continue_ = function() {
-  if (this.addedTasks_.length === 0) {
-    return;
+  while (this.addedTasks_.length > 0 &&
+         this.pendingTasks_.length < this.limit_) {
+    // Run the next closure.
+    const closure = this.addedTasks_.shift();
+    this.pendingTasks_.push(closure);
+    closure(this.onTaskFinished_.bind(this, closure));
   }
-
-  console.assert(
-      this.pendingTasks_.length <= this.limit_,
-      'Too many jobs are running (' + this.pendingTasks_.length + ')');
-
-  if (this.pendingTasks_.length >= this.limit_) {
-    return;
-  }
-
-  // Run the next closure.
-  const closure = this.addedTasks_.shift();
-  this.pendingTasks_.push(closure);
-  closure(this.onTaskFinished_.bind(this, closure));
-
-  this.continue_();
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index 4c2062b..9f82bd5 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -100,6 +100,8 @@
   display: flex;
   flex: 1 1 auto;
   margin-top: 8px;
+  overflow-x: auto;
+  overflow-y: auto;
   position: relative;
 }
 
@@ -125,11 +127,9 @@
   bottom: 0;
   flex: none;
   left: 0;
-  overflow-x: hidden;
-  overflow-y: auto;
-  position: absolute;
-  right: 0;
-  top: 0;
+  min-height: max-content;
+  min-width: min-content;
+  overflow: hidden;
 }
 
 #directory-tree .tree-row {
@@ -141,6 +141,12 @@
   padding: 0 3px;
 }
 
+div[section-start='removable'].tree-item {
+  left: 0;
+  max-width: 100%;
+  position: absolute;
+}
+
 #directory-tree .tree-row > .button {
   display: flex;
   min-width: 0;
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 468da85..127135d 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
@@ -1994,8 +1994,14 @@
    */
   onFocus_(e) {
     if (this.selectedItem && this.selectedItem.labelElement) {
+      // Grab the current location to check if it needs to move
       const element = this.selectedItem.labelElement;
+      const originalPos = element.getBoundingClientRect();
       element.scrollIntoViewIfNeeded();
+      const newPos = element.getBoundingClientRect();
+      if (newPos.x !== originalPos.x || newPos.y !== originalPos.y) {
+        element.scrollIntoView({inline: 'start'});
+      }
     }
   }